무중단 배포를 적용해보자.
- 개발
- 2026. 2. 26. 23:43
502 게이트웨이를 없애보자. (이론편)
배포를 진행하게 되면 일정 시간 동안 딜레이가 발생합니다.현재 화면은 배포 전 상태이며, localhost인 이유는 로컬 환경에서 배포를 진행했기 때문입니다.문제는 배포가 완료되는 과정에서 잠시
b-programmer.tistory.com
무중단 배포에는 대표적으로 세 가지 방법이 있습니다. Rolling, Blue-Green, Canary 방식입니다. Rolling 배포는 인스턴스를 하나씩 교체하는 방식으로, 서비스 중단 없이 배포가 가능하다는 장점이 있습니다. 하지만 배포 과정에서 구버전과 신버전이 동시에 존재할 수 있어 버전 불일치 문제가 발생할 수 있습니다. 이러한 문제를 보완하기 위해 등장한 방식이 Blue-Green 배포입니다. 기존 환경과 새로운 환경을 동시에 준비한 뒤, 트래픽을 한 번에 전환하는 방식입니다. 하지만 이 방법 역시 새로운 환경에 치명적인 버그가 존재할 경우 전체 사용자에게 동시에 영향을 줄 수 있다는 위험이 존재합니다. 이를 해결하기 위해 일부 사용자에게 먼저 배포하여 안정성을 검증하는 Canary 배포가 등장했습니다.
이번 파트에서는 이 세 가지 전략 중 Rolling과 Blue-Green 배포를 직접 실습해 보며, 우리 환경에 어떻게 적용할 수 있을지 고민해 보겠습니다.
rolling 배포
Rolling 배포는 인스턴스를 하나씩 교체하며 점진적으로 업데이트하는 방식입니다.
쿠버네티스에서는 RollingUpdate가 기본 전략으로 제공되기 때문에, 비교적 쉽게 롤링 배포를 적용할 수 있습니다.
하지만 저는 현재 쿠버네티스를 사용하고 있지 않습니다. 그렇다면 롤링 배포를 적용할 방법이 없을까요?
결론부터 말하면, 가능합니다. 쿠버네티스가 자동으로 처리해 주는 인스턴스 교체 과정을,
우리가 직접 도커 기반으로 구성하고 트래픽을 제어하면 됩니다.
Rolling 배포를 위해서는 최소 두 개 이상의 인스턴스가 필요합니다.
배포 중에도 항상 하나 이상의 인스턴스가 요청을 처리해야 하기 때문입니다.
따라서 이번 실습에서는 Docker(Compose)를 이용해 동일한 애플리케이션 인스턴스를 두 개 띄우는 것부터 시작해 보겠습니다.
기존 Docker compose를 확인해 봅시다.
services:
dragons-api:
build:
context: .
dockerfile: Dockerfile
image: dragons:${PROFILE:-dev}
container_name: dragons
restart: unless-stopped
ports:
- "8083:8083"
env_file:
- .env
인스턴스가 2개 이상 필요하기 때문에 단순히 docker compose down 후 up 하는 방식으로는,
배포 순간에 애플리케이션이 내려가면서 502가 발생할 수 있습니다.
따라서 먼저 동일한 애플리케이션을 두 개의 인스턴스로 실행할 수 있도록 구성해야 합니다.
가장 단순한 방법은 기존 dragons-api를 복제하여 dragons-api-1, dragons-api-2처럼 두 개의 서비스로 나누는 것입니다.
예를 들어 다음과 같이 컨테이너 이름과 포트만 분리해 두 인스턴스를 동시에 띄울 수 있습니다.
services:
dragons-api1:
build:
context: .
dockerfile: Dockerfile
image: dragons:${PROFILE:-dev}
container_name: dragons1
restart: unless-stopped
ports:
- "8083:8083"
env_file:
- .env
dragons-api2:
build:
context: .
dockerfile: Dockerfile
image: dragons:${PROFILE:-dev}
container_name: dragons2
restart: unless-stopped
ports:
- "8084:8083"
env_file:
- .env
이제 인스턴스가 2개가 되었습니다. 이렇게 해서 띄운다고 해서 rolling배포가 되는 것은 아닙니다.
그전에 정상적으로 인스턴스가 띄워졌는지 확인해 봅시다.

다행히 올라와있는 것을 확인할 수 있었습니다.
지금 단계에서는 단순히 인스턴스를 2개 띄운 것뿐입니다. 그렇다면 여기서 어떻게 Rolling 배포가 가능해질까요?
Rolling 배포를 하려면, 사용자 요청이 특정 인스턴스 하나에만 고정되지 않고 여러 인스턴스로 분산될 수 있어야 합니다.
즉, 앞단에서 트래픽을 분산해 주는 로드밸런싱이 필요합니다.
하지만 저는 별도의 로드밸런서를 사용하고 있지 않습니다. 대신 리버스 프락시 서버(Nginx)를 사용하고 있습니다.
리버스 프락시는 서버 앞단에서 요청을 받아, 내부 애플리케이션 서버로 전달하는 역할을 합니다.
그리고 설정에 따라 여러 인스턴스로 트래픽을 분산시킬 수 있기 때문에,
결과적으로 로드밸런서 역할도 함께 수행할 수 있습니다.
즉, 이번 실습에서는 별도의 로드밸런서 대신 Nginx를 이용해 트래픽을 분산시켜 Rolling 배포를 구현해 보겠습니다.
이를 해결하기 위해 nginx의 upstream기능을 사용할 수 있습니다.
nginx upstream
간단히 말해서 upstream은 여러 서버(인스턴스)를 하나의 서비스 풀로 묶는 설정입니다.
그렇다면 어떻게 적용할 수 있을까요? Nginx 설정 파일을 다음과 같이 변경하였습니다.
http {
upstream dragons {
server dragons1:8083;
server dragons2:8083;
}
# -------------------------
# HTTPS
# -------------------------
server {
...
# }
# -------------------------
# API (backend)
# -------------------------
location /api {
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_pass http://dragons;
...
}
여기서 proxy_pass의 대상이 기존에는 특정 컨테이너(서버)를 직접 가리키는 방식이었다면,
이제는 upstream 그룹 이름(dragons)을 바라보도록 변경되었습니다.
즉, nginx가 dragons라는 서비스 풀에 포함된 인스턴스들로 요청을 분산하게 됩니다.
또한 proxy_next_upstream 설정을 통해 특정 인스턴스가 일시적으로 응답하지 못하는 경우,
다른 인스턴스로 요청을 재시도하여 502 발생 가능성을 줄일 수 있습니다.
이렇게 되면 Rolling 배포를 진행하기 위한 준비 과정은 완료되었습니다.
이제 본격적으로 배포 테스트를 진행해 봅시다.
while true; do
curl -k -s -o /dev/null -w "%{http_code}\n" https://localhost/api
sleep 0.3
위 코드를 통해 /api 요청을 지속적으로 보내보겠습니다.
요청 결과로 401이 출력되었습니다. 이는 서버가 죽었다는 의미가 아니라, 인증이 필요한 API에 로그인 없이 접근했기 때문에 발생한 것으로 추측됩니다.
그렇다면 여기서 dragons1 인스턴스를 종료하면 어떻게 될까요? 502가 발생할까요, 아니면 여전히 401이 발생할까요?
결과적으로 401이 계속 발생했습니다.
즉, 한쪽 인스턴스를 종료하더라도 Nginx가 다른 인스턴스로 요청을 전달하면서 서비스가 유지되고 있음을 확인할 수 있었습니다.
Bule-Green 배포
Rolling 배포가 같은 서비스 풀 안에서 인스턴스를 하나씩 교체하는 방식이라면, Blue-Green 배포는 두 개의 세트를 동시에 실행해 두고,
전환 순간에 하나의 세트만 활성화하는 방식입니다.
즉, 기존 환경(Blue)과 새로운 환경(Green)을 동시에 준비해 두고, 트래픽을 한 번에 전환하는 전략입니다.
그렇다면 여기서 이런 의문이 생길 수 있습니다.
그렇다면 Docker Compose에서 만들어지는 인스턴스와 큰 차이가 없는 것 아닌가?
실제로 물리적인 구성만 놓고 보면 크게 다르지 않습니다. 컨테이너를 두 개 이상 띄운다는 점에서는 Rolling과 유사해 보일 수 있습니다.
하지만 차이는 어디에 트래픽을 보내느냐에 있습니다.
- Rolling은 두 인스턴스를 동시에 활성화한 상태에서 하나씩 교체합니다.
- Blue-Green은 두 세트를 모두 띄워두되, 항상 한 세트만 활성화합니다.
즉, 구조는 비슷해 보이지만 트래픽 제어 방식과 배포 철학이 다르다는 점이 핵심입니다.
인스턴스는 다음과 같이 진행하였습니다.
services:
dragons-api1:
build:
context: .
dockerfile: Dockerfile
image: dragons:${PROFILE:-dev}
container_name: dragons1
restart: unless-stopped
ports:
- "8083:8083"
env_file:
- .env
dragons-api2:
build:
context: .
dockerfile: Dockerfile
image: dragons:${PROFILE:-dev}
container_name: dragons2
restart: unless-stopped
ports:
- "8084:8083"
env_file:
- .env
Blue-Green 배포 방식은 트래픽을 전환하는 방식입니다.
즉, 새로운 버전을 배포할 때 기존 서버를 하나씩 교체하는 것이 아니라, 트래픽이 향하는 대상을 바꾸는 것이 핵심입니다.
이를 위해 서비스 풀을 Blue(기존)와 Green(신규) 두 세트로 나누어 준비해야 합니다.
그리고 전환 순간에는 두 세트 중 하나의 풀만 활성화하여 사용자 트래픽을 처리하도록 구성합니다.
저는 nginx_blue.conf와 nginx_green.conf로 구분을 짓겠습니다.
nginx_blue.conf
upstream dragons {
server dragons1:8083;
}
nginx_green.conf
upstream dragons {
server dragons2:8083;
}
핵심은
기존 nginx.conf에서
include /etc/nginx/conf.d/active.conf;
입니다.
이 설정은 Nginx가 모든 설정을 한 파일에 고정해 두는 것이 아니라,
active.conf라는 파일을 동적으로 포함(include) 하도록 만들어줍니다.
즉, active.conf가 어떤 파일을 가리키느냐에 따라 Blue 또는 Green으로 트래픽을 전환할 수 있습니다.
(참고로 모든 설정은 모두 같습니다.)
결론
시간 관계상 Blue-Green 배포는 끝까지 테스트를 진행하지 못했습니다. 어쩌면 그 결과는 블로그에 모두 담지 못할 수도 있을 것 같습니다.
하지만 Rolling과 Blue-Green 배포를 직접 구성해보며 한 가지를 분명히 느꼈습니다.
무중단 배포는 막연히 어렵고 복잡한 기술이라고 생각했지만, 핵심 원리를 이해하고 나니 생각보다 단순한 구조라는 점이었습니다.
결국 Rolling이든, Blue-Green이든 본질은 동일합니다.
- 인스턴스를 준비하고
- 트래픽을 제어하고
- 전환을 설계하는 것
이 세 가지를 이해하면, 배포 전략은 더 이상 두려운 개념이 아닙니다.
이번 실습을 통해 무중단 배포는 특정 도구의 기능이 아니라, 설계와 트래픽 제어의 문제라는 것을 깨닫게 되었습니다.
'개발' 카테고리의 다른 글
| minikube 간단하게 사용해보기 (0) | 2026.03.02 |
|---|---|
| 완벽한 일관성은 존재하지 않는다. (0) | 2026.02.27 |
| 도커 네트워크 이해해보기 (1) | 2026.02.25 |
| 502 게이트웨이를 없애보자. (이론편) (0) | 2026.02.24 |
| 어떻게 트랜잭셕은 Read only 여부를 알 수 있을까? (0) | 2026.02.22 |