[엉박사] 2.5.2 CI/CD와 무중단 배포

impala·2023년 1월 12일
0
post-thumbnail

2.5.2 CI/CD와 무중단 배포

앞에서 언급한 것처럼 프로젝트를 배포한 이후에도 개발이 멈추지 않았는데, 변경된 코드를 서버에 적용하기 위해서는 개발환경에서 테스트를 마친 이후 깃허브에 푸쉬하고 서버에 접속핸 뒤 깃허브에서 코드를 당겨온 후 서버를 재부팅하는 과정을 반복했다.

그런데 서비스에 사용된 자연어처리 모델을 로드하는데 꽤 오랜 시간이 걸렸기 때문에 한번 수정사항을 서버에 반영하기 위해서는 최소 1 ~ 3분의 시간이 낭비되었다. 그 동안은 서버가 내려가있는 상태이기 때문에 실제 서비스에서는 이러한 일을 방지해야 할 것 같았다.

그래서 다음 프로젝트에서는 배포중 반복되는 일련의 과정을 CI/CD를 통해 해결하고 배포가 진행되는 중에도 서버가 실행되도록 하기 위해 무중단 배포를 하는 방법을 간략하게 정리하였다.

CI/CD

CI/CD지속적 통합, 지속적 배포(Continuous Integration/Continuous Deployment) 의 약자로, 어플리케이션의 개발 단계부터 배포까지의 모든 단계를 자동화하여 좀 더 효율적이고 빠르게 사용자에게 빈번히 배포할 수 있는 작업방식을 말한다.

CI/CD는 CI단계와 CD단계로 나뉘는데 각 단계의 특징과 역할은 아래와 같다.

  1. CI : 어플리케이션의 버그 수정이나 코드 변경이 주기적으로 빌드 및 테스트되어 공유 레포지토리에 merge되는 것으로, 여러명의 개발자가 같이 개발할 때 병합, 빌드, 테스트등 일련의 과정을 자동화하는 것이다.
  2. CD : CI를 마친 코드를 검증한 후 프로덕션 환경에 배포하는 과정을 자동화하는 것이다.

정리하면 CI/CD 파이프라인은 빌드, 테스트, 릴리즈, 배포 총 4단계를 자동화 한 것이라고 볼 수 있다.

CI/CD를 지원하는 툴은 Jenkins, CircleCI, TravisCI등 여러가지가 있다.

그 중 무료 소프트웨어인 Jenkins를 사용하여 CI/CD를 구현하였다고 가정하면 CI/CD이후 개발과정은 아래와 같다.

  1. 개발자가 깃허브에 코드를 푸쉬한다
  2. 깃허브가 webhook을 통해 Jenkins로 요청을 보낸다
  3. Jenkins의 파이프라인에 따라 코드가 빌드, 테스트, 검증된다
  4. Jenkins에서 검증을 마치면 자동으로 서버에 코드를 배포한다.

Jenkins를 깃허브에 연동하고 깃허브 webhook을 설정하는 것만으로 손쉽게 CI/CD 개발환경을 구축할 수 있다.

무중단 배포

무중단 배포란 서비스가 운영중인 상태에서 새로운 버전을 배포하기 위해서는 기존 서비스를 종료하고 새로운 서비스를 시작해야 하는데, 두 작업 사이에 필연적으로 발생하는 다운타임을 없애는 것이다.

무중단 배포를 구현하기 위한 방식으로는

  • AWS의 Blue-Green 무중단 배포
  • 도커를 사용한 방법
  • L4, L7스위치를 이용한 방법
  • Ngnix 무중단 배포

등 여러방식이 있다.

무중단 배포를 구현하기 위한 전략은 대표적으로 세가지가 있는데 각각의 원리와 특징은 다음과 같다.

  • Rolling : 트래픽을 점진적으로 새로운 버전으로 옮기는 방식
    • 장점 : 배포로 인한 위험성이 비교적 적고, 많은 서버 자원을 확보하지 않아도 무중단 배포가 가능하다.
    • 단점 : 배포 도중 서비스중인 인스턴스의 수가 줄어 각각의 서버에 트래픽이 증가한다.
  • Blue/Green : 트래픽을 한번에 구버전(Blue)에서 신버전(Green)으로 옮기는 방식. Blue와 Green서버를 모두 구현해 둔 상태에서 배포 시점에 로드밸런서가 트래픽을 Blue에서 Green으로 전환시킨다
    • 장점 : 모든 트래픽을 한번에 옮기기 때문에 호환성 문제가 발생하지 않는다.
    • 단점 : 실제 운영에 필요한 서버 리소스의 2배의 리소스를 확보해야 한다.
  • Canary : 소수의 트래픽을 먼저 신버전으로 옮겨 테스트한 뒤 이상이 없을 경우 점진적으로 모든 트래픽을 옮긴다.
    • 장점 : 새로운 버전으로 인한 위험을 최소화 할 수 있다.
    • 단점 : 신/구 버전의 어플리케이션이 동시에 존재하므로 호환성 문제가 발생할 수 있다.

Blue/Green방식으로 Nginx와 Docker를 활용하여 이번 프로젝트에서 무중단 배포를 구현하는 상황을 가정하면 배포는 다음과 같은 순서로 이루어진다.

  1. Nginx가 8000번 포트의 Django 컨테이너와 연결되어있다.(Green)
  2. 새로운 버전의 컨테이너를 8001번 포트로 띄운다(Blue)
  3. nginx.conf의 설정을 수정하여 Nginx가 8001번 포트의 컨테이너와 연결되게 하고 Nginx를 reload한다
  4. 8000번 포트의 컨테이너를 제거한다.

위의 방법으로 무중단 배포를 구현하는 방법은 아래와 같다.

  1. blue, green docker-compose.yml작성 : Blue, Green컨테이너를 띄우는 과정

    # dokcer-compose.green.yml
    version: '3.1'
    
    services:
    
    api:
        image: ${DOCKER_REGISTRY}/${DOCKER_APP_NAME}:${IMAGE_TAG}
    
        container_name: ${DOCKER_APP_NAME}-green
    
        ports:
        - '8080:8000'
    # dokcer-compose.blue.yml
    version: '3.1'
    
    services:
    
    api:
        image: ${DOCKER_REGISTRY}/${DOCKER_APP_NAME}:${IMAGE_TAG}
    
        container_name: ${DOCKER_APP_NAME}-blue
    
        ports:
        - '8081:8000'
  2. blue, green nginx.conf 작성 : Ngnix에서 바라보는 컨테이너를 바꾸는 과정

    # nginx.green.conf
    http {
    upstream backend {
        server localhost:8080;  # Green Container
    }
    
    access_log /var/log/nginx/access.log;
    
    server {
        listen 80;
    
        location / {
            include /etc/nginx/uwsgi_params;
            proxy_pass http://backend;
        }
    
    }
    # nginx.blue.conf
    http {
    upstream backend {
        server localhost:8081;  # Blue Container
    }
    
    access_log /var/log/nginx/access.log;
    
    server {
        listen 80;
    
        location / {
            include /etc/nginx/uwsgi_params;
            proxy_pass http://backend;
        }
    
    }
  3. deploy.sh작성 : nginx.conf와 docker-compose.yml을 교체하는 과정

     # deploy.sh
    
     #!/bin/bash
     # Blue 를 기준으로 현재 떠있는 컨테이너를 체크한다.
     EXIST_BLUE=$(docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yaml ps | grep Up)
     
     # 컨테이너 스위칭
     if [ -z "$EXIST_BLUE" ]; then
         echo "blue up"
         docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yaml up -d
         BEFORE_COMPOSE_COLOR="green"
         AFTER_COMPOSE_COLOR="blue"
     else
         echo "green up"
         docker-compose -p ${DOCKER_APP_NAME}-green -f docker-compose.green.yaml up -d
         BEFORE_COMPOSE_COLOR="blue"
         AFTER_COMPOSE_COLOR="green"
     fi
     
     sleep 10
     
     # 새로운 컨테이너가 제대로 떴는지 확인
     EXIST_AFTER=$(docker-compose -p ${DOCKER_APP_NAME}-${AFTER_COMPOSE_COLOR} -f docker-compose.${AFTER_COMPOSE_COLOR}.yaml ps | grep Up)
     if [ -n "$EXIST_AFTER" ]; then
     # nginx.config를 컨테이너에 맞게 변경해주고 reload 한다
     cp /etc/nginx/nginx.${AFTER_COMPOSE_COLOR}.conf /etc/nginx/nginx.conf
     nginx -s reload
     
     # 이전 컨테이너 종료
     docker-compose -p ${DOCKER_APP_NAME}-${BEFORE_COMPOSE_COLOR} -f docker-compose.${BEFORE_COMPOSE_COLOR}.yaml down
     echo "$BEFORE_COMPOSE_COLOR down"
     fi

이렇게 nginx의 설정과 도커 컨테이너를 스위칭하는 방식으로 Blue/Green 무중단 배포를 구현하여 서버를 내리지 않고도 새로운 버전의 서비스를 배포할 수 있다.

0개의 댓글