Profile picture

[Docker] 도커로 무중단 배포하기 (blue, green)

JaehyoJJAng2023년 12월 20일

▶︎ 개요

도커를 사용해봤다면 서비스 개발 시

로컬에서 테스트를 하기 위해 docker-compose up, docker-compose down등의 명령어를 사용하여

새롭게 컨테이너를 띄우거나 삭제하는 과정들을 진행해봤을거다.

만약 실제 서비스가 배포 중인 production 환경에서 위와 같이 컨테이너를 내렸다 올렸다하면 어떻게 될까?

당연히 컨테이너가 재시작 되는 시점 동안 사용자들이 서비스에 접속을 못하게 될 것이다

그렇기 때문에 실제 production 환경에서는 무중단 배포를 해야한다.


▶︎ 사전 준비

  • 원활한 실습을 위해 include 속성이 추가된 도커 컴포즈 v2.20.0 버전 이상 필요

▶︎ 무중단 배포 종류

무중단 배포는 크게 두 가지로 나뉜다.

배포 종류 설명
rolling update 배포 새로 배포되어야 하는 버전을 하나씩 순차적으로 적용시키면서 배포하는 방식.
한 번에 모두 배포되는 게 아니기 때문에 배포가 되는 과정에서 옛날 버전과 새로운 버전이 공존함.
그렇기에 배포 과정 중 호환성 문제가 발생할 수도 있음
Blue, Green 배포 Blue 혹은 Green 버전 둘 중 하나로 배포되어 있는 상태에서 새로운 버전을 동시에 띄우고 로드밸런서를 통해서 스위칭 하는 방식.
한 번에 두개의 버전을 동시에 띄우기 때문에 시스템 자원도 두 배로 든다는 단점이 존재함

또한 위 배포 과정 말고 Canary(카나리) 배포도 있다. 단계적 배포라고도 하며, 일부 사용자들에게만 새로운 버전을 배포하고 안정성을 테스트한다.

이번 포스팅에서는 Blue, Green 무중단 배포에 대해서 실습해보자.


▶︎ Blue, Green 배포 과정

  • 오늘 실습할 Blue, Green 배포 과정을 정리해보자면 아래 순서와 같다.

1. 8000번 포트로 연결된 앱 컨테이너
image


2. 새로운 버전의 컨테이너를 8001번 포트로 띄우기
image


3. nginx.conf 파일 수정 후 reload (upstream 8001로 변경)
image


4. 기존 8000번 포트의 컨테이너는 삭제
image


위 개념을 정리해보자면

새로 배포할 때마다 새로운 컨테이너들을 띄우고

nginx upstream 연결을 기존 컨테이너IP:PORT에서 새로 띄운 컨테이너IP:PORT로 변경 후

이전 컨테이너는 삭제한다.


▶︎ 무중단 배포 구현

  • Nginx는 서버에서 별도로 설치되어 운영 된다. (컨테이너 X)

이제 docker-compose.yaml을 작성해보면서 위처럼 무중단 배포를 구현해보자.


‣ nginx.conf 작성

Nginx가 green 컨테이너를 사용할 때에는 8000번 포트로,

blue 컨테이너를 사용할 때에는 8001 포트로 연결이 되어야 한다.

먼저 /etc/nginx 하위에 nginx.blue.confnginx.green.conf 설정파일 두 개를 아래와 같이 생성해주자.

blue 컨테이너를 사용할 때에는 nginx.blue.conf를, green 컨테이너를 사용할 때에는 nginx.green.conf를 사용할 것이다.

cat << EOF | tee etc/nginx/{nginx.green.conf,nginx.blue.conf}
events {
    worker_connections  1024;
    }

http {
  upstream jh-svc {
    server http://127.0.0.1:8000;
  }

  server {
    listen 80;

    location / {
      proxy_pass http://jh-svc;
    }
  }
}
EOF

nginx.blue.conf 파일도 위와 동일하나 8001 포트로 연결하는 부분만 다르다.

  upstream jh-svc {
    server http://127.0.0.1:8001;
  }

‣ docker-compose 작성

  • 실습에서 배포할 앱의 경우 앱의 버전을 리턴하는 아주 간단한 앱이다.
  • 앱 이름에 각각 blue, green을 추가

docker-compose.yaml

services:
  app-green:
    image: "yshrim12/deploy-test:1.0"
    restart: always
    ports:
      - "8000:8000"
    container_name: deploytest-green

  app-blue:
    image: "yshrim12/deploy-test:2.0"
    restart: always
    ports:
      - "8001:8001"
    container_name: deploytest-blue

  nginx:
    image: "yshrim12/deploy-nginx:latest"
    restart: always
    ports:
      - "80:80"
    container_name: nginx

Dockerhub에서 yshrim12 레포지토리의 deploy-test 라는 이름으로 배포된 이미지를 내려받는 방식으로 진행하였다.

그리고 blue, green 구분을 위해 container_name 속성에서 앱 이름-blue 형식으로 구분 지어주도록 하자.


‣ deploy 스크립트 작성

  • 위 두 개의 docker-compose.yaml을 번갈아가며 띄울 수 있는 deploy.sh 스크립트를 작성해보자.

deploy.sh

#!/usr/bin/bash

# 프로젝트 폴더로 이동
PROJECT_PATH="/home/dev/blue-green"
cd "$PROJECT_PATH"

# Blue를 기준으로 현재 떠있는 컨테이너 추출
BLUE_CT="$(docker ps --filter "name=blue" --filter "status=running")"

# 컨테이너 스위칭
if [[ -z "$BLUE_CT" ]]; then
    echo "=== BLUE => GREEN ==="
    echo "1. green 앱의 새로운 이미지를 pulling .."
    docker-compose pull app-green

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

    echo "3. green 컨테이너 헬스체크 중 ..." 
    count=0
    while true; do
        sleep 2
        RQ=$(curl localhost:8000) # 서비스 테스트
        if [[ -n "$RQ" ]]; then
            echo "헬스체크 성공!!!"
            break
        fi
        (( count ++ ))
        echo "헬스체크 시도 중 ... ($count)"
    done    

    echo "4. reload nginx"
    sudo cp /etc/nginx/nginx.green.conf /etc/nginx/nginx.conf
    sudo nginx -s rel
    
    echo "5. blue 컨테이너 종료"
    docker-compose stop app-blue
else
    echo "=== GREEN => BLUE ==="
    echo "1. blue 앱의 새로운 이미지를 pulling .."
    docker-compose pull app-blue

    echo "2. blue 컨테이너 실행 ..."
    docker-compose up -d app-blue

    echo "3. blue 컨테이너 헬스체크 중 ..." 
    count=0
    while true; do
        sleep 2
        RQ=$(curl localhost:8001) # 서비스 테스트
        if [[ -n "$RQ" ]]; then
            echo "헬스체크 성공!!!"
            break
        fi
        (( count ++ ))
        echo "헬스체크 시도 중 ... ($count)"
    done    

    echo "4. reload nginx"
    sudo cp /etc/nginx/nginx.blue.conf /etc/nginx/nginx.conf
    sudo nginx -s rel
    
    echo "5. green 컨테이너 종료"
    docker-compose stop app-green
fi

▶︎ 마무리

지금까지 도커를 사용하는 환경에서 무중단 배포를 어떻게 하는지 기록해보았다.

오늘 이해한 내용을 기반으로 컨테이너 오케스트레이션 도구들을 공부해보면 좋을 것 같다.


Loading script...