[CI / CD] 프로젝트에 Nginx, Docker, DockerFile, DockerCompose 적용하기

최혜원·2024년 9월 27일
post-thumbnail

Docker를 왜 배우는 걸까?

이식성 : 특정 프로그램을 다른 곳으로 쉽게 옮겨서 설치 및 실행할 수 있는 특성

Docker를 사용하면 아래와 같은 장점이 있다.

  • 매번 귀찮은 설치 과정을 일일이 거치지 않아도 된다.
  • 항상 일관되게 프로그램을 설치할 수 있다. (버전, 환경 설정, 옵션, 운영 체제 등)
  • 각 프로그램이 독립적인 환경에서 실행되기 때문에 프로그램 간에 서로 충돌이 일어나지 않는다.

Docker란 ?

컨테이너를 사용하여 각각의 프로그램을 분리된 환경에서 실행 및 관리할 수 있는 툴이다.

컨테이너(Container)란?

하나의 컴퓨터 환경 내에서 독립적인 컴퓨터 환경을 구성해서, 각 환경에 프로그램을 별도로 설치할 수 있게 만든 개념이다. 하나의 컴퓨터 환경 내에서 여러개의 미니 컴퓨터 환경을 구성할 수 있는 형태이다. 여기서 얘기하는 미니 컴퓨터를 보고 Docker에서는 컨테이너(Container)라고 부른다.

컨테이너(Container)의 독립성

  • 디스크 (저장 공간) : 각 컨테이너마다 서로 각자의 저장 공간을 가지고 있다. 일반적으로 A 컨테이너 내부에서 B 컨테이너 내부에 있는 파일에 접근할 수 없다.
  • 네트워크 (IP, Port) : 각 컨테이너마다 고유의 네트워크를 가지고 있다. 컨테이너는 각자의 IP 주소를 가지고 있다.

이미지(Image)란?

이미지(Image)프로그램을 실행하는 데 필요한 설치 과정, 설정, 버전 정보 등을 포함하고 있다. 즉, 프로그램을 실행하는 데 필요한 모든 것을 포함하고 있다.

Docker 설치(for MacOS)

✅ 체크 사항

2023년 7월부터 Docker Compose V1의 업데이트를 중단했다. 따라서 Docker Compose는 V2를 설치할 것을 권장한다. 혹시나 기존에 설치되어 있는 Docker Compose의 버전이 V1이라면 V2로 교체할 것을 권장한다.

Nginx란?

  • 웹 서버 (HTML 웹 페이지를 렌더링 시키는 역할)
  • 로드 밸런싱
  • 리버스 프록시

1. Nginx 이미지 다운로드

docker pull nginx

2. 다운로드된 이미지 확인

docker image ls

3. 이미지를 컨테이너에 올려 Nginx 서버 실행

docker run --name webserver -d -p 80:80 nginx

3. Nginx 서버가 잘 실행되는지 확인

Nginx + React

Nginx와 React를 묶어서 하나의 이미지로 만들 것이다.

  • 리액트를 빌드해서 HTML 로 만들고, 웹서버를 설치해서 Dockerfile을 이용해 HTML 파일을 넣어서 실행
  • ⭐️ 그리고 중요한 점은 Dockerfile과 nginx.conf 파일을 react 최상위 폴더에 두어야 한다.

conf/conf.d/default.conf

server {
	listen 80;
	server_name [server ip];
    location / {
        root   /usr/share/nginx/html; # 리액트 파일 경로
        index  index.html index.htm;
        try_files $uri $uri/ /index.html; # 리액트의 SPA 라우팅을 위해 index.html로 요청 전달
        # location 안에 넣어줘야함
        proxy_buffer_size          128k;
        proxy_buffers              4 256k;
        proxy_busy_buffers_size    256k;
    }
    location /static/ {
          alias /usr/share/nginx/html/static/;
      }
		location /api/ {
				proxy_pass http://spring-app:8080;
				proxy_set_header Host $http_host;
				proxy_set_header X-Real-IP $remote_addr;
				proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
				proxy_set_header X-Forwarded-Proto $scheme;
				# CORS 헤더 설정
                add_header 'Access-Control-Allow-Origin' '[server ip]';
                add_header 'Access-Control-Allow-Credentials' 'true';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Cache-Control, X-Requested-With';
        
        # Preflight 요청(OPTIONS)을 위한 응답
         if ($request_method = OPTIONS) {
             add_header 'Access-Control-Allow-Origin' '[server ip]';
             add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
             add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Cache-Control, X-Requested-With';
             add_header 'Access-Control-Allow-Credentials' 'true';
             add_header 'Access-Control-Max-Age' 1728000;  # Preflight 요청을 캐시하는 시간 (20일)
             return 204;  # No Content 응답
         }
			}
	
}

DockerFile?

Docker 이미지를 만들기 위한 설정 파일이다.

#이미지 빌드 
docker build .
#또는 
docker build -t <image_name> .

를 실행하면 Docker는 지정된 디렉터리에 있는 Dockerfile을 읽고 그 안의 지침을 순차적으로 실행한다. 각 명령어는 최종 이미지에 레이어를 생성한다.

Spring DockerFile

FROM openjdk:17-jdk-slim
WORKDIR /app
COPY build/libs/tmarket-0.0.1.jar app.jar
EXPOSE 8080

CMD ["java", "-jar", "app.jar", "--spring.profiles.active=prod"]

React DockerFile

# Step 1: Build the React app
FROM node:16-alpine AS build
WORKDIR /app
# 소스 코드 복사
COPY . .
RUN npm install --legacy-peer-deps # 종속성 충돌 무시하고 설치
# 소스 코드 복사
RUN npm run build

# Step 2: Nginx로 서비스
FROM nginx:alpine

# 기본 nginx 설정 파일을 삭제한다. (custom 설정과 충돌 방지)
RUN rm /etc/nginx/conf.d/default.conf
# custom 설정파일을 컨테이너 내부로 복사한다.
COPY conf/conf.d/default.conf /etc/nginx/conf.d/default.conf

# nginx 가 동작하기 위해 필요한 파일들을 시스템으로 복사
COPY --from=build /app/build /usr/share/nginx/html

# 포트 노출
EXPOSE 80
EXPOSE 443

# Nginx 실행
CMD ["nginx", "-g", "daemon off;"]
  • FROM : 베이스 이미지 생성

    • FROM은 베이스 이미지를 생성하는 역할을 한다. Docker 컨테이너를 특정 초기 이미지를 기반으로 추가적인 셋팅을 할 수 있다. 여기서 얘기한 ‘특정 초기 이미지’가 곧 베이스 이미지이다.
      # 문법
      FROM [이미지명]
      FROM [이미지명]:[태그명]
      • 태그명을 적지 않으면 해당 이미지의 최신(latest) 버전을 사용한다.
  • COPY : 파일 복사(이동)

    • COPY호스트 컴퓨터에 있는 파일을 복사해서 컨테이너로 전달한다.
      # 문법
      COPY [호스트 컴퓨터에 있는 복사할 파일의 경로] [컨테이너에서 파일이 위치할 경로]
      # 예시
      COPY app.txt /app.txt
  • RUN : 이미지를 생성하는 과정에서 사용할 명령문 실행

    • RUN은 이미지 생성 과정에서 명령어를 실행시켜야 할 때 사용한다.

       # 문법
       RUN [명령문]
      
       # 예시
       RUN npm install

DockerCompose란?

도커 컴포즈 는 단일 서버에서 여러개의 컨테이너를 하나의 서비스로 정의컨테이너의 묶음으로 관리할 수 있는 작업 환경을 제공하는 관리 도구

Docker Compose를 사용하는 이유

  1. 여러 개의 컨테이너를 관리하는 데 용이
    여러 개의 컨테이너로 이루어진 복잡한 애플리케이션을 한 번에 관리할 수 있게 해준다.
    여러 컨테이너를 하나의 환경에서 실행하고 관리하는 데 도움이 된다.

  2. 복잡한 명령어로 실행시키던 걸 간소화 시킬 수 있음

    이전에 MySQL 이미지를 컨테이너로 실행시킬 때 아래와 같은 명령어를 실행시켰다.

    $ docker run -e MYSQL_ROOT_PASSWORD=password123 -p 3306:3306 -v /Users/jaeseong/Documents/Develop/docker-mysql/mysql_data:/var/lib/mysql -d mysql

    Docker Compose를 사용하면 위와 같이 컨테이너를 실행시킬 때마다 복잡한 명령어를 입력하지 않아도 된다. 단순히 docker compose up 명령어만 실행시키면 된다.

⭐️ docker compose up -d : docker run 을 간단하게 실행해줌

compose.yml

services:
  spring-app:
    image: won1110218/tmarket-back:latest
    ports:
      - 8080:8080
    container_name: spring-app
    depends_on:
      redis:
        condition: service_healthy
      mysql:
        condition: service_healthy
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/tmarket
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: ********
    networks:
      - tmarket-network

  nginx:
    image: won1110218/tmarket-front:latest 
    ports:
      - 80:80
      - 443:443
    restart: always
    environment:
      TZ: Asia/Seoul
    container_name: nginx
    depends_on:
      - spring-app
    volumes:
      - /home/ubuntu:/home/ubuntu  # 인증서 파일을 위한 Volume 추가
    networks:
      - tmarket-network    

  mysql:
    image: mysql:latest
    environment:
      MYSQL_USER: admin
      MySQL_PASSWORD: dmstnr0218
      MYSQL_ROOT_PASSWORD: dmstnr0218
      MYSQL_DATABASE: tmarket # MySQL 최초 실행 시 tmarket 라는 데이터베이스를 생성해준다.
    container_name: mysql
    volumes:
      - ./mysql_data:/var/lib/mysql
    ports:
      - 3307:3306 # 호스트의 3307 포트를 컨테이너의 3306 포트에 매핑
    healthcheck:
      test: [ "CMD", "mysqladmin", "ping" ] # MySQL 이 healthy 한 지 판단할 수 있는 명령어
      interval: 5s # 5초 간격으로 체크
      retries: 10 # 10번까지 재시도
    networks:
      - tmarket-network  

  redis:
    image: redis:latest
    container_name: redis
    ports:
      - 6379:6379
    healthcheck:
      test: [ "CMD", "redis-cli", "ping" ]
      interval: 5s
      retries: 10
    networks:
      - tmarket-network  

networks:
  tmarket-network:
    driver: bridge      
  • services: my-web-server : Docekr Compose에서 하나의 컨테이너를 서비스(service)라고 부른다. 이 옵션은 서비스에 이름을 붙이는 기능이다.
  • container_name: web-server : 컨테이너를 띄울 때 붙이는 별칭이다. CLI에서 --name web-server 역할과 동일하다.
  • image: nginx : 컨테이너를 실행시킬 때 어떤 이미지를 사용할 지 정의하는 명령어이다. $ docker run [이미지명]와 동일한 역할이다.
  • ports : 포트 매핑은 어떻게 할 지를 설정하는 옵션이다. CLI에서-p 80:80 역할과 동일하다.

compose.yml 파일 배포 ec2에 올리기

doker compose up -d --build -> compose.yml 파일이 실행됨

doker ps (실행 중인 컨테이너 목록 확인)


Docker Compose CLI 명령어

compose.yml에서 정의한 컨테이너 실행

 docker compose up    # 포그라운드에서 실행
 docker compose up -d # 백그라운드에서 실행

Docker Compose로 실행시킨 컨테이너 확인하기

# compose.yml에 정의된 컨테이너 중 실행 중인 컨테이너만 보여준다. 
 docker compose ps 

# compose.yml에 정의된 모든 컨테이너를 보여준다.
 docker compose ps -a

Docker Compose 로그 확인하기

# compose.yml에 정의된 모든 컨테이너의 로그를 모아서 출력한다.
 docker compose logs

컨테이너를 실행하기 전에 이미지 재빌드하기

docker compose up --build # 포그라운드에서 실행
docker compose up --build -d # 백그라운드에서 실행
  • compose.yml에서 정의한 이미지 파일에서 코드가 변경 됐을 경우, 이미지를 다시 빌드해서 컨테이너를 실행시켜야 코드 변경된 부분이 적용된다. 그러므로 이럴 때에는 --build 옵션을 추가해서 사용해야 한다.

참고 : docker compose up vs docker compose up --build

  • docker compose up : 이미지가 없을 때만 빌드해서 컨테이너를 실행시킨다. 이미지가 이미 존재하는 경우 이미지를 빌드하지 않고 컨테이너를 실행시킨다.
  • docker compose up --build : 이미지가 있건 없건 무조건 빌드를 다시해서 컨테이너를 실행시킨다.

이미지 다운받기 / 업데이트하기

docker compose pull
  • compose.yml에서 정의된 이미지를 다운 받거나 업데이트 한다.
    • 로컬 환경에 이미지가 없다면 이미지를 다운 받는다.
    • 로컬 환경에 이미 이미지가 있는데, Dockerhub의 이미지와 다른 이미지일 경우 이미지를 업데이트 한다.

Docker Compose에서 이용한 컨테이너 종료하기

docker compose down

SSE Nginx 설정

    location /api/notifications/subscribe {
          proxy_pass http://spring-app:8080;  # HTTP로 Spring 애플리케이션에 연결
          proxy_http_version 1.1; # Use HTTP/1.1 for SSE
          proxy_set_header Upgrade $http_upgrade; # For upgrading to websocket if needed
          proxy_set_header Connection "keep-alive"; # Necessary for SSE
          proxy_set_header Host $host; # Forward the original Host header
          proxy_set_header X-Real-IP $remote_addr; # Forward the client IP address
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Proxy forwarding headers
          proxy_set_header X-Forwarded-Proto $scheme; # Protocol used by the client (http or https)
          proxy_buffering off; # Disable buffering for SSE
    }
profile
어제보다 나은 오늘

0개의 댓글