무중단 배포란?
서비스가 중단 없이 새로운 버전의 애플리케이션을 배포하는 방법
1. 롤링 배포(Rolling Deployment)
기존 서버에서 하나씩 서비스에서 제외한 후 새로운 버전으로 점진적으로 교체하는 방식
모든 인스턴스를 순차적으로 업데이트하여 전체가 새로운 버전으로 바뀔 때까지 반복
장점
- 별도의 추가 인스턴스 자원이 필요 없습니다
- 문제가 생기면 바로 직전 단계로 롤백이 쉬움
단점
- 업데이트 되는 동안에 구버전과 신버전이 함께 존재하므로 호한성 문제 발생 가능
- 한순간 처리할 수 있는 인스턴스 수가 줄어서 트래픽이 급증하는 상황에서 서비스에 영향이 생길 수 있음
2. 블루-그린 배포(Blue-Green Deployment)
기존 인스턴스와 동일한 새로운 인스턴스를 미리 준비한 뒤 새로운 버전이 모두 준비되면 트래픽을 한번에 Green버전으로 전환
이와 같은 식으로 현재 구동되는 버전을 구버전으로, 새로 업데이트를 신버전으로 배포
장점
- 전환이 즉시 일어나서 서비스 다운타임이 없습니다.
- 문제가 생기면 바로 Blue(구버전)로 빠르게 롤백할 수 있습니다.
단점
- 동시에 두 배 이상의 인스턴스 자원이 필요해 비용이 증가할 수 있습니다.
- 소규모 시스템에서는 적용이 부담스러울 수 있습니다
3. 카나리 배포(Canary Deployment)
신버전을 소수 인스턴스에만 먼저 배포하여 일부 트래픽만 신버전으로 유도합니다.
문제가 없으면 신버전 인스턴스와 트래픽 비율을 점진적으로 늘려 전체로 확장합니다.
장점
- 실서비스 환경에서 신버전 테스트가 가능하므로, 문제 발생 시 피해를 최소화할 수 있습니다.
- 점진적으로 전환되므로 안정성이 높습니다.
단점
- 트래픽 분배, 버전 관리 등 인프라 관리가 더 필요합니다.
- 구버전과 신버전이 공존하는 구간이 많이 생김
Docker를 활용한 Blue-Green 배포
서버 2대는 아니고 동일인스턴스 상에서 Docker 컨테이너를 통하여 다음과 같이 설계
1. 애플리케이션 컨테이너 2개 준비하기
SpringBoot일 경우 해당 작업을 이미지 파일로 올린다
깃허브 액션을 통해 올리기를 시작
그리고 각각의 서비스 이름은
docker-compose에 blue와 green으로 설정한다
이미지는 둘 다 동일하게 latest라고 작성해주었습니다

2. script 작성하기
우선은 현재 포트를 파악하고 신버전에 대하여 다른 포트를 docker를 통해 다시 시작해주어야한다
if sudo docker ps --filter "name=app-blue" --quiet | grep -E .; then
echo "Blue down, Green Up "
BEFORE_COMPOSE_COLOR="blue"
AFTER_COMPOSE_COLOR="green"
else
echo "Green down, Blue up"
BEFORE_COMPOSE_COLOR="green"
AFTER_COMPOSE_COLOR="blue"
fi
그리고 docker를 통해 Github actions로 빌드 후 푸시했던 최신 이미지를 다운 받아야한다
docker compose pull app-${AFTER_COMPOSE_COLOR}
docker compose up -d --no-deps --force-recreate app-${AFTER_COMPOSE_COLOR}
그리고 이미지 pull 받은 것을 기반으로 새 컨테이너를 시작하도록 설정
이 때 --no-deps 옵션을 통해서 다른 컨테이너들은 건드리지 않도록 설정해야 redis나 nginx 같은 것들을 건드리지 않는다
if docker ps --filter "name=app-${AFTER_COMPOSE_COLOR}" --filter "status=running" --format '{{.Names}}' | grep -q .; then
echo "New app-${AFTER_COMPOSE_COLOR} container is running."
# reload nginx
NGINX_ID=$(sudo docker ps --filter "name=nginx" --quiet)
NGINX_CONFIG="/home/ubuntu/nginx/nginx.conf"
echo "Switching Nginx upstream config..."
if ! sed -i "s/app-${BEFORE_COMPOSE_COLOR}:8080/app-${AFTER_COMPOSE_COLOR}:8080/" $NGINX_CONFIG; then
echo "Error occured: Failed to update Nginx config. Exiting deploy script."
exit 1
fi
새로운 컨테이너가 제대로 동작 중인지를 확인해야하고
잘 동작되지 않으면 새로운 버전 배포는 실패하고 구버전으로 돌아가고 있는 것이다(그래서 서버가 다운 될 일이 없어서 Blue/Green 무중단 배포의 장점이 보임)
2-1. Nginx 설정하기
이제 Nginx를 통하여 새로운 컨테이너롤 8080으로 포워딩 해줘야한다
이 때 실행 중인 docker 파일 내에서 nginx.conf 파일을 바꾸기는 어려워서 mount에서의 내용을 다시 작성하고 docker nginx를 다시 시작하도록 설정
nginx의 기본 default.conf를 편집하든, nginx.conf나 다른 custom을 편집하든 상관없다
나같은 경우 nginx.conf를 편집하였다
events {}
http {
upstream backend {
server blue:8080;
}
server {
listen 80;
location / {
}
}
}
본문과 불필요한 내용은 생략했습니다
위에처럼 포트를 설정할 때 blue:8080 과 green:8080으로 설정해주어야한다
"s/app-${BEFORE_COMPOSE_COLOR}:8080/app-${AFTER_COMPOSE_COLOR}:8080/" $NGINX_CONFIG; then
echo "Error occured: Failed to update Nginx config. Exiting deploy script."
현재가 green이면 green:8080 , blue면 blue:8080로 작성해야함
이때 green과 blue는 docker-compose.yml의 서비스이름이어야함(컨테이너 이름과 혼동 주의)
또한 Nginx도 도커 컨테이너로 설정해주었기 때문에 서비스 이름으로 두는 것이지,
만약에 Nginx를 그냥 서버 상에서 설치했다면 localhost:8080으로 설정해주어야한다
이제 최종적으로 nginx도 제대로 설정되었다면 기존의 컨테이너 닫으면 된다
docker stop app-${BEFORE_COMPOSE_COLOR}
docker rm app-${BEFORE_COMPOSE_COLOR}
docker image prune -af
아래는 작성한 전체 shell script다
#!/bin/bash
set -e
ERR_MSG=''
trap 'echo "Error occured: $ERR_MSG. Exiting deploy script."; exit 1' ERR
# 현재 포트 파악
if sudo docker ps --filter "name=app-blue" --quiet | grep -E .; then
echo "Blue down, Green Up "
BEFORE_COMPOSE_COLOR="blue"
AFTER_COMPOSE_COLOR="green"
else
echo "Green down, Blue up"
BEFORE_COMPOSE_COLOR="green"
AFTER_COMPOSE_COLOR="blue"
fi
# container pull
docker compose pull app-${AFTER_COMPOSE_COLOR}
docker compose up -d --no-deps --force-recreate app-${AFTER_COMPOSE_COLOR}
sleep 10
echo "The $BEFORE_COMPOSE_COLOR version is currently running on the server. Starting the $AFTER_COMPOSE_COLOR version."
# 새로운 컨테이너가 제대로 떴는지 확인
if docker ps --filter "name=app-${AFTER_COMPOSE_COLOR}" --filter "status=running" --format '{{.Names}}' | grep -q .; then
echo "New app-${AFTER_COMPOSE_COLOR} container is running."
# reload nginx
NGINX_ID=$(sudo docker ps --filter "name=nginx" --quiet)
NGINX_CONFIG="/home/ubuntu/nginx/nginx.conf"
echo "Switching Nginx upstream config..."
if ! sed -i "s/app-${BEFORE_COMPOSE_COLOR}:8080/app-${AFTER_COMPOSE_COLOR}:8080/" $NGINX_CONFIG; then
echo "Error occured: Failed to update Nginx config. Exiting deploy script."
exit 1
fi
echo "Reloding Nginx in Container"
if ! docker exec $NGINX_ID nginx -s reload; then
ERR_MSG='Failed to update Nginx config'
exit 1
fi
if ! docker compose restart nginx; then
ERR_MSG='Failed to reload Nginx'
exit 1
fi
# 이전 컨테이너 종료
docker stop app-${BEFORE_COMPOSE_COLOR}
docker rm app-${BEFORE_COMPOSE_COLOR}
docker image prune -af
fi
echo "Deployment success."
exit 0
2. Nginx 설정하기
nginx의 기본 default.conf를 편집하든, nginx.conf나 다른 custom을 편집하든 상관없다
나같은 경우 nginx.conf를 편집하였다
events {}
http {
upstream backend {
server blue:8080;
}
server {
listen 80;
location / {
}
}
}
본문과 불필요한 내용은 생략했습니다
위에처럼 포트를 설정할 때 blue:8080 과 green:8080으로 설정해주어야한다
"s/app-${BEFORE_COMPOSE_COLOR}:8080/app-${AFTER_COMPOSE_COLOR}:8080/" $NGINX_CONFIG; then
echo "Error occured: Failed to update Nginx config. Exiting deploy script."
현재가 green이면 green:8080 , blue면 blue:8080로 작성해야함
이때 green과 blue는 docker-compose.yml의 서비스이름이어야함(컨테이너 이름과 혼동 주의)
또한 Nginx도 도커 컨테이너로 설정해주었기 때문에 서비스 이름으로 두는 것이지,
만약에 Nginx를 그냥 서버 상에서 설치했다면 localhost:8080으로 설정해주어야한다
'백엔드' 카테고리의 다른 글
| [Message Broker] 메시지 큐(Message Queue)와 이벤트 스트림(Event Stream) (1) | 2025.09.04 |
|---|---|
| 블로킹 I/O vs 논블로킹 I/O 비교 (w/ RestTemplate vs WebClient) (3) | 2025.04.03 |