-
[Infra] Dockerfile & Docker-Compose 작성프로젝트/뉴스타 2024. 6. 11. 08:56
Dockerfile & Docker-Compose 작성 왜 궁금했을까❓
뉴스타 서비스의 경우 Docker를 이용하여 어플리케이션을 띄우기로 했다. 그래서 Dockerfile과 Docker-compose 작성법에 대해서 알아보고 뉴스타 서비스에 적용하는 과정까지 살펴보도록 하겠다.
1. Dockerfile 문법
Dockerfile reference
Find all the available commands you can use in a Dockerfile and learn how to use them, including COPY, ARG, ENTRYPOINT, and more.
docs.docker.com
명령 설명 FROM 베이스 이미지 설정 LABEL 이미지의 Metadata 설정 CMD Docker Container를 생성할 때, 실행하는 쉘 명령 (docker run) RUN 이미지를 생성할 때, 실행하는 쉘 명령 ENTRYPOINT Docker Container가 시작할 때, 실행하는 쉘 명령 (docker start) EXPOSE Docker Container 외부에 오픈할 Port 설정 ENV Docker Container 내부에서 사용할 환경 변수 설정 WORKDIR Docker Container 내부에서 작업 디렉토리 설정 COPY 파일, 디렉토리를 Docker Container에 복사 ADD 파일, 디렉토리, URL를 Docker Container에 복사 및 압축 해제 SHELL Docker Container 기본 Shell 설정 ARG Dockerfile내에서 변수 설정 USER Docker Container 내부에서 작업을 하는 사용자 ID 설정 ONBUILD 생성한 이미지를 기반으로 새로운 이미지를 생성할 때 명령어를 설정 VOLUME 이미지를 위한 볼륨 생성 MAINTAINER 이미지를 생성한 개발자 정보 설정 STOPSIGNAL Docker Container를 STOP 할 때 Signal을 설정 HEALTHCHECK Docker Container의 프로세스 상태를 체크 2. Docker Compose 주요 문법
명령 설명 version Docker Compose 파일의 버전 설정 services Docker Container의 그룹을 설정 image 사용할 Docker Image의 이름 설정 build 이미지를 빌드할 Dockerfile의 경로를 설정 ports 호스트와 Container 간의 네트워크 포트 매핑을 설정 env_file Docker Container에서 사용할 환경 변수를 포함하는 파일의 경로를 설정 environment Docker Container에서 사용할 환경 변수를 설정 depends_on 해당 서비스가 시작하기 전에 먼저 시작해야 하는 서비스를 설정 command Docker Container가 시작할 때, 실행할 명령을 설정 links 해당 서비스와 연결할 다른 서비스를 설정 networks 서비스 간의 네트워킹을 설정 volumes Docker Container의 데이터를 유지하기 위해 설정 3. Dockerfile / Docker compose 실습
3.1. Spring Boot
# 베이스 이미지 설정 FROM openjdk:17 AS builder # 워킹 디렉토리 설정 WORKDIR /usr/src/app # 빌드 파일 복사 COPY build.gradle gradlew settings.gradle gradle src . # 실행 권한 부여 RUN chmod +x gradlew # 프로젝트 빌드 RUN ./gradlew clean bootJar # 빌드 파일 변수 설정 ARG JAR_FILE=build/libs/*.jar # 빌드 파일 복사 COPY ${JAR_FILE} app.jar # 타임존 설정 RUN apk add tzdata && ln -snf /usr/share/zoneinfo/Asia/Seoul /etc/localtime RUN echo Asia/Seoul > /etc/timezone # jar 파일 실행 ENTRYPOINT ["java","-jar", "-Dspring.profiles.active=prod", "app.jar"]
- gradle을 따로 설치하지 않고 빌드 파일들을 복사했다. 또한, Docker Container의 시간을 맞추기 위해 타임존을 설정했다.
docker build -t newstar_back .
- newstar_back 이름을 가진 이미지로 빌드 수행
3.2. FastAPI
# 베이스 이미지 설정 FROM python:3.9.13 # 워킹 디렉토리 설정 WORKDIR /app/ # 코드 및 의존성 등 프로젝트 파일 복사 COPY . . # 패키지 업데이트 RUN pip install --upgrade pip # 패키지 설치 RUN pip install -r requirements.txt # 타임존 설정 RUN ln -snf /usr/share/zoneinfo/Asia/Seoul /etc/localtime RUN echo Asia/Seoul > /etc/timezone # 환경 변수 production 모드 설정 ENV APP_ENV=prod # unicorn을 통해 프로젝트 실행 CMD uvicorn --host=0.0.0.0 --port 8000 app.main:app
- ENV 명령어를 이용하여 APP_ENV에 prod 값을 부여하고 있다. 뉴스타 프로젝트는 dev, prod, create 등으로 환경을 분리해서 관리하고 있기 때문이다.
docker build -t fastapi_back .
- fastapi_back 이름을 가진 이미지로 빌드 수행
3.3. React
# 베이스 이미지 설정 FROM node:alpine AS builder # 워킹 디렉토리 설정 WORKDIR /usr/src/app # 의존성 목록 복사 COPY package.json . # 의존성 설치 RUN npm install --force # 프로젝트 파일 복사 COPY . . # 프로젝트 빌드 RUN npm run build # 베이스 이미지 다시 설정 FROM nginx:latest # nginx 설정 파일 복사 COPY ./default.conf /etc/nginx/conf.d/default.conf # React Build 파일 nginx 정적 파일 반환 경로에 복사 COPY --from=builder /usr/src/app/dist /usr/share/nginx/html # Nginx 실행 CMD [ "nginx", "-g", "daemon off;"]
# default.conf server { listen 3000; location / { root /usr/share/nginx/html; index index.html index.htm; try_files $uri $uri/ /index.html; } }
- React의 경우 Node 이미지에서 프로젝트 파일을 빌드하고 있다.
- Nginx 베이스 이미지를 가져와서 커스텀 설정을 적용하고 React에서 빌드해서 나온 결과물을 가져와서 / 경로의 반환 파일 경로에 복사했다.
- 뉴스타 서비스는 Nginx를 Reverse Proxy로도 사용하고 WebServer로도 활용을 했다. 그러면 가장 외부와 가까운 Nginx에서 정적 파일들을 반환하면 될 것인데 왜 이렇게 역할을 나눈 것인지 궁금할 수도 있다.
- 가장 앞단에 존재하는 Nginx가 forwarding 업무를 수행하고 정적 파일도 반환하고 추후에는 로드 밸러싱까지 담당할 것이라 부하가 많아질 것이라 판단해서 2개로 나눠서 운영을 하기로 결정했다.
docker build -t newstar_front .
- newstar_front 이름을 가진 이미지로 빌드 수행
3.4. Jenkins
FROM jenkins/jenkins:lts USER root RUN apt-get update && \ apt-get -y install apt-transport-https \ ca-certificates \ curl \ gnupg2 \ software-properties-common && \ curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && \ add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \ $(lsb_release -cs) \ stable" && \ apt-get update && \ apt-get -y install docker-ce RUN groupadd -f docker RUN usermod -aG docker jenkins
- Jenkins의 경우 Docker out of Docker를 사용하기 때문에 외부 도커 서버와의 연결을 위해 docker-ce를 설치한다.
docker build -t jenkins/custom .
- jenkins/custom 이름을 가진 이미지로 빌드 수행
3.5. Docker compose
docker network create app-net
- 추후, Docker compose 간의 통신이 요구되는 무중단 서비스를 계획 중에 있어서 네트워크를 만들었다.
version: '3' services: spring: container_name: spring image: newstar_back restart: always expose: - 8080 env_file: - /env/.env environment: TZ: Asia/Seoul react: container_name: react expose: - 3000 image: newstar_front fastapi: container_name: fastapi image: fastapi_back restart: always expose: - 8000 env_file: - /env/.env environment: TZ: Asia/Seoul nginx: container_name: nginx image: nginx:latest restart: always volumes: - ./nginx/default.conf:/etc/nginx/conf.d/default.conf - ./nginx/service-url.inc:/etc/nginx/conf.d/service-url.inc - ./data/certbot/conf:/etc/letsencrypt - ./data/certbot/www:/var/www/certbot ports: - 80:80 - 443:443 command: '/bin/sh -c ''while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g "daemon off;"''' mysql: container_name: mysql image: mysql:8.0 restart: always expose: - 3306 environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} TZ: Asia/Seoul LC_ALL: C.UTF-8 command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci volumes: - /mysql/:/var/lib/mysql - ./config/my.cnf:/etc/mysql/conf.d/my.cnf redis: container_name: redis image: redis:latest restart: always expose: - 6397 environment: TZ: Asia/Seoul labels: - "name=redis" - "mode=standalone" jenkins: container_name: jenkins image: jenkins/custom volumes: - /var/run/docker.sock:/var/run/docker.sock - /jenkins:/var/jenkins_home - /env/.env:/env/.env ports: - 9999:8080 elastic: container_name: elastic image: docker.elastic.co/elasticsearch/elasticsearch:7.10.0 restart: always ports: - 9200:9200 environment: - discovery.type=single-node kibana: container_name: kibana image: docker.elastic.co/kibana/kibana:7.10.1 restart: always ports: - 5601:5601 environment: ELASTICSEARCH_URL: http://elastic:9200 ELASTICSEARCH_HOSTS: http://elastic:9200 privileged: true certbot: container_name: certbot image: certbot/certbot restart: unless-stopped volumes: - ./data/certbot/conf:/etc/letsencrypt - ./data/certbot/www:/var/www/certbot entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" networks: default: name: app-net external: true
- 각 이미지에 맞는 설정을 입력하고 네트워크는 커스텀 네트워크로 설정을 해주었다.
docker compose -f docker-compose.yaml up -d docker ps -a
- 위 명령어를 통해 Docker compose 파일이 실행되고 ps -a 옵션을 통해 컨테이너가 정상적으로 운영되고 있는 지 확인할 수 있을 것이다.
'프로젝트 > 뉴스타' 카테고리의 다른 글
[Spring Boot] Redis를 활용한 성능 향상 (0) 2024.06.16 [Infra] 무중단 서비스를 위한 Health Check (0) 2024.06.14 [Infra] Docker 이미지 최적화 (0) 2024.06.13 [Infra] Nginx Reverse Proxy & SSL 적용 (0) 2024.06.10 [Infra] Nginx 란? (0) 2024.06.08