
블루그린 배포란? 무중단 배포 원리와 nginx 실전 구현 방법
블루그린 배포는 똑같은 앱을 두 벌 띄워 트래픽만 갈아타는 무중단 배포 방식이다. 서버 한 대에서 nginx로 직접 돌려 본 원리와 구현, DB 마이그레이션 함정까지 짚었다.
금요일 밤에 배포 한 번 했다가 사이트가 30초쯤 먹통이 된 적 있으신가요. 새 코드를 올리느라 서버를 잠깐 내렸는데, 하필 그 틈에 들어온 사용자가 흰 화면을 봤다면 그건 그냥 끊김이 아니라 신뢰가 깎이는 순간입니다. 트래픽이 적은 새벽을 노려 배포하는 습관도 결국 "끊김을 사람이 안 보는 시간으로 숨기는" 임시방편일 뿐이죠.
블루그린 배포는 이 문제를 정면으로 없애는 방법입니다. 거창한 쿠버네티스가 있어야 하는 것도 아니고, 서버가 여러 대여야 하는 것도 아닙니다. 저는 지금 미니 PC 한 대로 돌리는 서비스에 이 방식을 그대로 얹어 쓰고 있습니다.

똑같은 앱을 두 벌 띄워 둔다는 발상
핵심 아이디어는 의외로 단순합니다. 운영 중인 앱을 '파란색(blue)'이라고 부르고, 새로 배포할 버전을 '초록색(green)'이라는 이름으로 한 벌 더 띄웁니다. 두 벌이 잠깐 동시에 떠 있는 상태를 만든 다음, 사용자 트래픽을 보내는 스위치만 파란색에서 초록색으로 '탁' 하고 넘기는 거예요.
서버를 껐다 켜는 게 아니라, 이미 완성돼서 대기 중인 새 버전으로 길만 바꿔 주는 겁니다. 그래서 다운타임이 0초에 수렴합니다. 넘기는 데 실패하면? 스위치를 안 넘기면 그만입니다. 사용자는 여전히 멀쩡한 파란색을 보고 있으니까요. 이 "실패하면 그냥 원래 자리에 머문다"는 성질이 블루그린의 진짜 매력입니다. 롤백이 별도 작업이 아니라 '아무것도 안 하기'가 되거든요.
제 경우엔 인프라(DB·Redis·오브젝트 스토리지)와 맨 앞단 nginx는 한 벌만 그대로 두고, 앱 컨테이너만 blue/green 두 벌로 색을 바꿉니다. 포트는 일부러 호스트에 노출하지 않아요. 두 색이 같은 포트를 두고 싸우면 배포가 꼬이기 때문입니다.
트래픽을 갈아타게 하는 스위치, nginx
서버 한 대에서 스위치 역할을 가장 싸게 맡아 주는 게 nginx입니다. nginx에는 upstream이라는, "요청을 실제로 어느 백엔드로 보낼지" 적어 두는 블록이 있습니다. 이 블록이 가리키는 대상만 바꾸고 nginx를 다시 읽히면 그게 곧 트래픽 전환입니다.
실제로 쓰는 구조를 간추리면 이렇습니다. 지금 살아있는 색을 가리키는 파일 하나를 따로 두고, nginx 본체는 그 파일을 include만 합니다.
# active.conf — 지금 살아있는 '색'만 가리키는 파일
upstream backend {
server app-blue:48080; # 배포 때 app-green 으로 바뀌는 줄
}
# nginx.conf 안에서는 이 파일을 끌어다 쓴다
server {
include /etc/nginx/upstreams/active.conf;
location /api/ {
proxy_pass http://backend;
}
}
전환 자체는 스크립트가 순서대로 처리합니다. 새 색을 먼저 띄우고, 그 색이 '준비됐다'고 스스로 답할 때까지 기다린 다음에야 스위치를 넘기는 게 핵심입니다. 이 '준비 확인(readiness)' 단계를 건너뛰면, 아직 부팅도 안 끝난 새 버전으로 트래픽을 보내 버리는 사고가 납니다.
# 1) 새 색(green)을 띄운다 — 포트는 호스트에 노출하지 않는다
docker compose -p app-green up -d
# 2) green 이 'health UP' 이라고 답할 때까지 폴링한다 (readiness)
until curl -sf http://app-green:48080/actuator/health | grep -q '"UP"'; do
sleep 2
done
# 3) active.conf 를 green 으로 바꾸고 → 문법검사 → graceful reload
sed -i 's/app-blue/app-green/' active.conf
nginx -t && nginx -s reload
# 4) 옛 색(blue)은 곧바로 죽이지 않고 60초쯤 기다렸다 내린다
sleep 60
docker compose -p app-blue down
여기서 nginx -s reload가 왜 '무중단'인지가 중요합니다. 이 명령은 처리 중이던 요청을 중간에 끊지 않습니다. 이미 들어와 있던 요청은 옛 설정으로 끝까지 응답하게 두고, 새로 들어오는 요청부터 새 색으로 보냅니다. 그래서 전환 순간에도 에러를 받는 사용자가 없습니다.
readiness 폴링이 끝내 실패하면 저는 새로 띄운 색만 조용히 내리고 스위치는 손도 대지 않습니다. 기존 색이 그대로 서비스하고 있으니 배포가 '조용히 안 먹은' 상태가 될 뿐, 사이트는 멀쩡합니다. 이게 제가 말하는 자동 롤백입니다. 사람이 새벽에 깨서 손댈 필요가 없어요.

진짜 어려운 건 배포가 아니라 DB였다
블루그린을 처음 붙였을 때 저를 가장 애먹인 건 nginx도 도커도 아니고 데이터베이스였습니다. 생각해 보면 당연합니다. 전환되는 그 몇십 초 동안 파란색과 초록색이 같은 DB 한 벌을 동시에 바라봅니다. 그런데 새 버전 배포라고 컬럼을 지우거나 이름을 바꾸거나 NOT NULL을 바로 걸어 버리면, 아직 살아 있는 옛 버전이 그 순간 깨집니다. 무중단이라고 공들여 만든 게 전환 찰나에 무너지는 거죠.
그래서 스키마 변경은 반드시 하위호환(expand/contract) 으로 쪼갭니다. 한 번에 바꾸지 않고 두 번의 배포로 나누는 방식입니다. 먼저 '확장(expand)' 단계에서 컬럼을 추가만 하고 데이터를 채워 둡니다. 이 상태는 옛 코드도 새 코드도 둘 다 멀쩡히 돕니다. 모든 트래픽이 새 버전으로 넘어간 다음에야, 다음 배포에서 '수축(contract)' 단계로 옛 컬럼을 제거합니다. 저는 Flyway 마이그레이션을 쓸 때 이 원칙을 어겼다가 전환 순간 옛 색이 500을 뱉는 걸 보고 나서야 몸으로 배웠습니다.
세션과 캐시도 같은 함정입니다. 로그인 세션을 앱 메모리에 들고 있으면, 색이 바뀌는 순간 사용자가 로그아웃됩니다. 세션은 Redis 같은 외부 저장소로 빼서 두 색이 공유하게 해야 합니다. 웹소켓이나 롱폴링처럼 오래 붙어 있는 연결은 reload 후에도 한동안 옛 색에 남아 있으니, 옛 컨테이너를 바로 죽이지 말고 잠깐 기다렸다 내리는 60초가 괜히 있는 게 아닙니다.
카나리·롤링과는 뭐가 다를까
무중단 배포에는 블루그린 말고도 카나리와 롤링이 있습니다. 셋을 한 줄로 구분하면 이렇습니다.
| 방식 | 전환 방법 | 장점 | 비용·주의 |
|---|---|---|---|
| 블루그린 | 두 벌을 띄워 트래픽을 통째로 전환 | 롤백이 즉각적, 전환이 깔끔 | 배포 동안 자원 2배 |
| 카나리 | 새 버전에 트래픽 일부(5%)만 흘려 관찰 | 위험을 조금씩 노출, 실측 가능 | 트래픽 분배·모니터링 필요 |
| 롤링 | 인스턴스를 하나씩 교체 | 추가 자원 거의 없음 | 옛·새 버전이 길게 공존 |
블루그린은 "다 바꾸거나 안 바꾸거나"라서 결정이 단순하고, 특정 시점에 옛 버전이 완전히 사라지는 깔끔한 순간이 있습니다. 그 덕에 위에서 말한 contract 단계도 가장 마음 편하게 할 수 있습니다. 대신 배포 동안 서버 자원을 두 배로 쓰는 값을 치르죠. 수천 대 규모라면 이 2배가 부담이라 카나리를 택하지만, 서버 한두 대짜리 서비스라면 블루그린이 얻는 단순함이 그 비용을 압도합니다. 한국의 1인 개발·소규모 스타트업 환경에서 블루그린이 유독 잘 맞는 이유가 여기 있다고 봅니다. 쿠버네티스를 깔 여력도, 운영할 사람도 없는 팀이 "그래도 무중단은 포기 못 해" 할 때 현실적인 답이 블루그린이거든요.
한 가지 덧붙이면, 블루그린을 '복잡한 대기업 기법'으로 오해하고 지레 포기하는 경우를 자주 봅니다. 막상 해보면 핵심은 nginx 설정 파일 한 줄과 readiness 체크, 그리고 DB를 하위호환으로 바꾸는 습관, 이 세 가지가 전부입니다. 도구보다 원리를 쥐고 있으면 도커든 PM2든 심지어 수동 심볼릭 링크 교체든 같은 방식을 그대로 옮길 수 있습니다.
짧게 정리하면
- 블루그린 = 같은 앱 두 벌(blue/green)을 띄우고 트래픽 스위치만 넘기는 무중단 배포
- 서버 1대라면 nginx
upstream전환 + graceful reload로 충분하다 - 넘기기 전에 readiness 체크는 필수, 실패하면 스위치를 안 넘기는 게 곧 롤백이다
- DB는 반드시 expand/contract(하위호환)로, 세션은 외부 저장소로 빼라
- 자원 2배가 부담될 규모면 카나리, 한두 대 서비스면 블루그린
여러분은 배포할 때 다운타임을 어떻게 다루고 계신가요. 새벽 배포로 버티는 쪽인지, 이미 무중단 파이프라인을 굴리는 쪽인지 궁금합니다. 블루그린을 직접 붙여 봤다면 가장 크게 데인 지점이 어디였는지도 댓글로 나눠 주세요.
댓글 0
댓글을 불러오는 중…






