본문 바로가기
TIL

[240128] docker + nginx 무중단배포 구축하기

by 진진리 2024. 1. 28.
728x90
기존에는 코드가 수정되면 CI/CD를 통해 도커 이미지가 올라가게 되고, EC2 서버에서 해당 이미지를 컨테이너로 실행하여 배포를 진행해야 했다.
이 과정에서는 코드가 수정될 때마다 수정 내용을 반영하기 위해 중간에 서버를 멈추고 다시 시작해줘야 한다는 문제점이 발생한다.
사용자 테스트를 앞두고 서버가 중간에 멈추는 일을 방지하기 위해서 무중단 배포를 구축하고자 했다.

 

 

Nginx

- Nginx는 웹 서버로서의 역할을 하며, 백엔드 서버에 대한 요청을 받아 이를 처리하는 역할을 합니다.

무중단 배포를 위해 새로운 버전의 애플리케이션을 배포할 때 이전 버전과 새로운 버전을 함께 관리할 수 있으며, 요청이 들어오면 새로운 버전이 완전히 배포될 때까지 이전 버전으로 요청을 전달할 수 있습니다.

 

- Reverse Proxy: 클라이언트와 서버 간의 통신을 중계하는 중간 서버

  • 리버스 프록시를 사용하면 클라이언트와 서버 간의 직접적인 통신을 방지하여 보안 강화
  • 여러 서버 간의 트래픽을 분산시키는 역할을 수행
  • 서버로부터 받은 응답을 캐싱하여 동일한 요청에 대해 중복 작업을 줄일 수 있음
  • 클라이언트와의 통신에서 SSL 암호화를 처리 가능

 

무중단배포 원리

출처: https://cookiee.tistory.com/690

 

기존에는 ec2 서버에서 도커 이미지를 통해 스프링 부트(port: 8080)을 실행하여 사용자로부터의 요청을 처리하였다.

 

nginx를 통해 무중단배포를 구축하게 되면 nginx가 80번 혹은 443번 포트로 받은 사용자의 요청을

ec2 서버의 8080포트와 8081포트 중에 연결되어있는 포트에서 실행 중인 스프링 부트 서버로 보내게 된다.

 

여기서 8080번 포트를 사용하는 컨테이너를 green, 8081번 포트를 사용하는 컨테이너를 blue라고 하자.

CI/CD 과정에서 현재 nginx와 연결되어 있는 컨테이너를 확인한다.

green이 실행 중인 경우에는 blue를 새로 실행하고 blue와의 연결에 성공했을 때 연결 후 green을 종료시킨다.

 

이를 통해 코드의 수정이 있는 경우에도 끊김없이 서비스를 제공할 수 있다.

 

 

무중단배포 구현

ec2 인스턴스 생성 후

  • 보안그룹에서 인바운드 규칙을 설정한다: 8080, 8081, 80, 443, 22 포트
  • 탄력적 IP 주소 할당
  • Route 53에서 도메인에 ip주소를 추가

 

ec2에 nginx를 설치

sudo apt install nginx

 

nginx 실행 후 상태 확인

sudo service nginx start

sudo service status nginx

 

 

docker-compose 설치

  • docker-compose란?
  • 여러 개의 도커 컨테이너를 정의하고 실행하는 도구로, 복수의 컨테이너 애플리케이션을 쉽게 관리할 수 있도록 도와주는 도구
  • 스프링 컨테이너를 실행할 때 Redis 컨테이너를 함께 실행하여 항상 같은 환경에서 실행되도록 설정함
sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

 

docker-compose 설치 경로에 권한 부여

sudo chmod +x /usr/local/bin/docker-compose

 

 

docker-compose.yml 작성

- green과 blue의 실행 환경(환경 변수 포함)을 작성한다.

version: '3'
services:
  redis:
    image: "redis:alpine"
    container_name: redis
    ports:
      - "6379:6379"
    command: redis-server --requirepass sappun
    volumes:
      - ./data:/data
    restart: always
    
  green:
    container_name: green
    image: #도커 이미지명
    ports:
      - "8080:8080"  # green은 8080 포트를 열어줍니다.
    depends_on:
      - redis
    environment:
      - DB_PASSWORD=
      - DB_URL=
      - DB_USERNAME=
      - KAKAO_CLIENT_ID=
      - KAKAO_CLIENT_SECRET=
      - KAKAO_REDIRECT_URI=http://{ec2 서버의 탄력적 IP주소}:8080/api/users/kakao/callback

  blue:
    container_name: blue
    image: #도커 이미지명
    ports:
      - "8081:8080"  #blue는 8081 포트를 열어줍니다.
    depends_on:
      - redis
    environment:
      - DB_PASSWORD=
      - DB_URL=
      - DB_USERNAME=
      - KAKAO_CLIENT_ID=
      - KAKAO_CLIENT_SECRET=
      - KAKAO_REDIRECT_URI=http://{ec2 서버의 탄력적 IP주소}:8080/api/users/kakao/callback

 

 

nginx.green.conf 및 nginx.blue.conf 작성

저장 위치: ec2 서버의 ./etc/nginx/nginx.green.conf

# nginx.green.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

    events {
        worker_connections  1024;
        }

        http {

        include       mime.types;

        # 443 포트로 접근시 ssl을 적용한 뒤 3000포트로 요청을 전달해 주도록 하는 설 정.
        server {

        server_name # 도메인 이름;

        location / {

        # GREEN - 8080 포트로 연결합니다.
        # BLUE 설정파일은 이부분의 포트만 8081로 변경해주면 됩니다.
        proxy_pass http://localhost: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;

        }

        listen 443 ssl; # managed by Certbot

        ssl_certificate /etc/letsencrypt/live/{도메인_이름}/fullchain.pem; # managed by Cert>
        ssl_certificate_key /etc/letsencrypt/live/{도메인_이름}/privkey.pem; # managed by Ce>
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
        }

        # 80 포트로 접근시 443 포트로 리다이렉트 시켜주는 설정
        server {

        return 301 https://$host$request_uri;

        listen 80;
        server_name # 도메인 이름;
        return 404; # managed by Certbot
        }

}

 

 

deploy.sh 작성

./home/ubuntu에 작성

실행마다 nginx.conf 파일을 nginx.green.conf 혹은 nginx.blue.conf 파일로 덮어씌운다.

실행 중이지 않은 이미지를 실행하고 연결한 후에 실행 중이던 컨테이너를 종료한다.

IS_GREEN=$(docker ps | grep green) # 현재 실행중인 App이 blue인지 확 인합니다.
DEFAULT_CONF=" /etc/nginx/nginx.conf"

if [ -z $IS_GREEN  ];then # blue라면

  echo "### BLUE => GREEN ###"

  echo "1. get green image"
  docker-compose pull green # green으로 이미지를 내려받습니다.

  echo "2. green container up"
  docker-compose up -d green # green 컨테이너 실행

  while [ 1 = 1 ]; do
  echo "3. green health check..."
  sleep 3

  REQUEST=$(curl http://localhost:8080) # green으로 request
    if [ -n "$REQUEST" ]; then # 서비스 가능하면 health check 중지
            echo "health check success"
            break ;
    fi
  done;

  echo "4. reload nginx"
  sudo cp /etc/nginx/nginx.green.conf /etc/nginx/nginx.conf
  sudo nginx -s reload

  echo "5. blue container down"
  docker-compose stop blue
else
  echo "### GREEN => BLUE ###"

  echo "1. get blue image"
  docker-compose pull blue

  echo "2. blue container up"
  docker-compose up -d blue

  while [ 1 = 1 ]; do
    echo "3. blue health check..."
    sleep 3
    REQUEST=$(curl http://localhost:8081) # blue 로 request

    if [ -n "$REQUEST" ]; then # 서비스 가능하면 health check 중지
      echo "health check success"
      break ;
    fi
  done;

  echo "4. reload nginx"
  sudo cp /etc/nginx/nginx.blue.conf /etc/nginx/nginx.conf
  sudo nginx -s reload

  echo "5. green container down"
  docker-compose stop green
fi

 

deploy.sh 실행 권한 부여 후 실행 해보기

서버가 연결되기까지 조금 오래 기다려봐야 한다!!

chmod 777 ./deploy.sh

./deploy.sh

 

 

CI/CD yml에 적용

ec2 인스턴스에서 deploy.sh를 실행하는 과정을 자동화한다.

- name: Deploy to prod
        if: contains(github.ref, 'develop') || contains(github.ref, 'main')
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HOST_NAME }}
          username: ${{ secrets.USER_NAME }}
          key: ${{ secrets.AWS_PRIVATE_KEY }}
          port: ${{ secrets.AWS_PORT }}
          script: |
            docker pull ${{ secrets.DOCKER_USER }}/sappun:latest
            chmod 777 ./deploy.sh
            ./deploy.sh
            docker image prune -f

 


https 적용하기

ssl 인증서 발급

  • certbot: Let's Encrypt라는 무료 SSL/TLS 인증 기관에서 제공하는 도구 중 하나로, 웹 서버에 SSL/TLS 암호화를 쉽게 적용할 수 있도록 도와주는 오픈 소스 소프트웨어
  • 인증서 갱신을 자동으로 처리해 주어 보안 인증서의 유효 기간을 관리하는 데에 편리
  • 명령줄 인터페이스를 제공하며, 사용자가 몇 가지 간단한 명령어로 SSL/TLS 인증서를 적용할 수 있도록 도와줌
sudo snap install certbot --classic
sudo certbot --nginx

 

  • nginx가 실행되지 않는 경우에는 /etc/nginx/nginx.conf 파일을 변경해준다.

nginx.conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
        worker_connections 768;
        # multi_accept on;
}
http {
        ##
        # Basic Settings
        ##
        sendfile on;
        tcp_nopush on;
        types_hash_max_size 2048;
        # server_tokens off;
        # server_names_hash_bucket_size 64;
        # server_name_in_redirect off;
        include /etc/nginx/mime.types;
        default_type application/octet-stream;
        ##
        # SSL Settings
        ##
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;
        ##
        # Logging Settings
        ##
        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;
        ##
        # Gzip Settings
        ##
        gzip on;
        # gzip_vary on;
        # gzip_proxied any;
        # gzip_comp_level 6;
        # gzip_buffers 16 8k;
        # gzip_http_version 1.1;
        # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
        ##
        # Virtual Host Configs
        ##
        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}
#mail {
#       # See sample authentication script at:
#       # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
#       # auth_http localhost/auth.php;
#       # pop3_capabilities "TOP" "USER";
#       # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
#       server {
#               listen     localhost:110;
#               protocol   pop3;
#               proxy      on;
#       }
#
#       server {
#               listen     localhost:143;
#               protocol   imap;
#               proxy      on;
#       }
#}

 

nginx를 재실행한다.

sudo service nginx restart

 

 

이후 https://도메인명으로 접속 가능하다!

'TIL' 카테고리의 다른 글

알람 기능 구현 방법  (0) 2024.05.05
[240219] CI/CD 공부 (2) CD  (0) 2024.02.19
[240118] userDetails null 오류  (0) 2024.01.18
[240117] ec2 서버 실행  (0) 2024.01.17
[240114] AWS, docker로 수동 배포하기  (0) 2024.01.15