[TIL] 무중단 배포(Nginx+Docker+Github Actions)

김민재·2024년 9월 30일
0

TIL

목록 보기
168/172

무중단 배포란?

https://velog.io/@minjae98/TIL-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC-ap1dw3zk

문제: 개인 프로젝트를 하는데 Docker와 CI/CD를 이용해 자동화 배포를 했다. 그런데 서버를 최신화 하는 코드를 PUSH하면 DOWNTIME이라는 현상이 발생했다.

DOWNTIME이란?

  • 서버나 서비스를 교체할 때 발생하는 일시적인 중단 현상이다.
    이러한 현상이 발생하면 사용자는 서비스에 일시적으로 접근할 수 없다. 이러한 악영향은 사용자에게 신뢰를 잃게 한다.

Blue-Green 기법

  • 두 개의 환경을 사용하여 무중단 배포를 가능하게 한다. 하나의 환경이 운영 중이고, 다른 환경은 새 버전을 배포한다.

Blue-Green 사용법

  1. docker-compose.blue.yml 파일과 docker-compose.green.yml 파일을 생성한다.
  • docker-compose.blue.yml (구 버전)
services:
  app_blue:
    container_name: app_blue
    build:
      context: ./
      dockerfile: Dockerfile
    image: minjae981002/blog-api-app:latest
    ports:
      - '3000:3000'
    env_file:
      - .env
    depends_on:
      - db
    networks:
      - travelplan_blog-net

  db:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
    ports:
      - '3306:3306'
    networks:
      - travelplan_blog-net

  nginx:
    image: nginx:latest
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - /etc/letsencrypt/live/xn--9r2b17b.shop:/etc/letsencrypt/live/xn--9r2b17b.shop
      - /etc/letsencrypt/archive/xn--9r2b17b.shop:/etc/letsencrypt/archive/xn--9r2b17b.shop
      - ./certs:/etc/letsencrypt
    ports:
      - '8080:80'
      - '8443:443'
    depends_on:
      - app_blue # 초기에는 blue 컨테이너를 사용
    networks:
      - travelplan_blog-net

networks:
  travelplan_blog-net:
    driver: bridge
  • docker-compose.green.yml (신 버전)
services:
  app_green:
    container_name: app_green
    build:
      context: ./
      dockerfile: Dockerfile
    image: minjae981002/blog-api-app:latest
    ports:
      - '3001:3000'
    env_file:
      - .env
    depends_on:
      - db
    networks:
      - travelplan_blog-net

  db:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
    ports:
      - '3306:3306'
    networks:
      - travelplan_blog-net

  nginx:
    image: nginx:latest
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - /etc/letsencrypt/live/xn--9r2b17b.shop:/etc/letsencrypt/live/xn--9r2b17b.shop
      - /etc/letsencrypt/archive/xn--9r2b17b.shop:/etc/letsencrypt/archive/xn--9r2b17b.shop
      - ./certs:/etc/letsencrypt
    ports:
      - '8080:80'
      - '8443:443'
    depends_on:
      - app_green # 초기에는 green 컨테이너를 사용
    networks:
      - travelplan_blog-net

networks:
  travelplan_blog-net:
    driver: bridge

위 두 파일의 차이점은 port와 container 명이 다를 뿐이다. 또한 네트워크 안에서 컨테이너를 읽을 수 있으므로 네트워크 설정이 필수이다.

1-1. 도커 컨테이너를 실행해준다.
sudo docker-compose -f docker-compose.blue.yml up -d 위 명령어를 통해 실행할 수 있다.

  1. nginx 설정을 해줘야한다.
    sudo nano /etc/nginx/sites-enabled/default 명령어를 입력하여 수정한다.
upstream app {
    server app_blue:3000;  # Docker container name
    server app_green:3001;
}

server {
    listen 80;
    server_name xn--9r2b17b.shop www.xn--9r2b17b.shop;

    location / {
        return 301 https://$host$request_uri;  # HTTP 요청을 HTTPS로 리다이렉트
    }
}

server {
    listen 443 ssl;
    server_name xn--9r2b17b.shop www.xn--9r2b17b.shop;

    ssl_certificate /etc/letsencrypt/live/xn--9r2b17b.shop/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/xn--9r2b17b.shop/privkey.pem;

    location / {
        proxy_pass http://app;  # Nest.js가 Docker에서 실행 중인 서비스
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

위 설정을 통해 Docker 네트워크 안에 있는 컨테이너와 nginx는 상호작용을 할 수 있다.

2-1. nginx 애플리케이션 간의 통신 설정이 필요하다.
sudo nano /etc/hosts 명령어를 통해 설정해준다.

127.0.0.1 app_blue;
127.0.0.1 app_green;
Docker Container name을 정해준다.
  1. nginx 설정을 완료 했으면 테스트를 하고 재실행을 한다.
    sudo nginx -t 실행 후 successful 나와야함
    sudo systemctl restart nginx 재실행
    sudo systemctl status nginx 재실행 후 명령어를 통해 nginx 확인이 가능하다.

  2. CI/CD 파일을 수정해준다.

      - name: Deploy to EC2
        run: |
          ssh -i keypair.pem -o StrictHostKeyChecking=no ubuntu@${{ secrets.EC2_IP }} "
            # Docker Hub 로그인
            sudo docker login -u '${{ secrets.DOCKER_HUB_USERNAME }}' -p '${{ secrets.DOCKER_HUB_PASSWORD }}' &&
            echo 'Pulling the latest Docker image...' &&
            sudo docker pull ${{ secrets.DOCKER_HUB_USERNAME }}/blog-api-app:latest &&

            # 사용하지 않는 Docker 객체 삭제
            echo 'Cleaning up unused Docker images and volumes...' &&
            sudo docker system prune -a --volumes -f &&

          # 현재 app_blue가 실행 중일 때 app_green 시작
          if [ \$(sudo docker ps -q --filter 'name=app_blue') ]; then
            echo 'Starting new container app_green...' &&
            sudo docker-compose -f /home/ubuntu/TravelPlan/docker-compose.green.yml up -d &&
            echo 'Container app_green started.' &&
            echo 'Modifying Nginx to point to app_green...' &&
            sudo sed -i 's/proxy_pass http:\/\/app_blue:3000;/proxy_pass http:\/\/app_green:3001;/' /etc/nginx/nginx.conf &&
            sudo sed -i 's/proxy_pass http:\/\/app_blue:3000;/proxy_pass http:\/\/app_green:3001;/' /etc/nginx/sites-enabled/default &&
            sudo nginx -t &&
            sudo systemctl reload nginx &&
            echo 'Old container app_blue stopped and removed.' &&
            sudo docker stop app_blue &&
            sudo docker rm app_blue
          else
            echo 'Starting new container app_blue...' &&
            sudo docker-compose -f /home/ubuntu/TravelPlan/docker-compose.blue.yml up -d &&
            echo 'Container app_blue started.' &&
            echo 'Modifying Nginx to point to app_blue...' &&
            sudo sed -i 's/proxy_pass http:\/\/app_green:3001;/proxy_pass http:\/\/app_blue:3000;/' /etc/nginx/nginx.conf &&
            sudo sed -i 's/proxy_pass http:\/\/app_green:3001;/proxy_pass http:\/\/app_blue:3000;/' /etc/nginx/sites-enabled/default &&
            sudo nginx -t &&
            sudo systemctl reload nginx &&
            echo 'Old container app_green stopped and removed.' &&
            sudo docker stop app_green &&
            sudo docker rm app_green
          fi
          "

배포 단계에서 컨테이너가 app_blue로 실행 중이라면 app_green을 실행하고 app_blue를 종료 하고 app_green을 실행시키는 프로세스이다. 이렇게 되면 새로운 버전이 app_green으로 실행되고 app_blue는 삭제하게 된다.

며칠 밤을 새며 무중단 배포를 구현하고 싶었는데 이렇게 성공해서 뿌듯하고 기분이 좋았다.

자주 사용하는 명령어를 정리해두겠다..

네트워크에 container들이 잘 있는지 확인할 때
docker network ls
docker network inspect 

docker-compose.yml 파일을 실행하고 종료할 때
docker-compose.yml 
docker-compose up -d
docker-compose down
docker stop docker

모든 도커 컨테이너를 삭제할 때
docker stop $(docker ps -q)
docker rm $(docker ps -a -q)
docker rmi $(docker images -q)
docker network rm $(docker network ls -q)

모든 자원을 삭제할 때
docker system prune -a --volumes

docker-compose.blue.yml 버전을 실행할 때
sudo docker-compose -f docker-compose.blue.yml up -d

nginx 컨테이너 상호작용을 위해
sudo nano /etc/hosts

nginx 파일 수정할 때
sudo nano /etc/nginx/sites-enabled/default
sudo nano nginx.conf

container 로그 확인할 때
docker logs app_blue

nginx 파일 재실행하거나 테스트 할때 
sudo nginx -t
sudo systemctl status nginx
sudo systemctl restart nginx

중복된 포트 삭제할 때
sudo kill pid 

sudo lsof -i :8080
sudo lsof -i :8443
profile
개발 경험치 쌓는 곳

0개의 댓글

관련 채용 정보