본문 바로가기
서버 인프라 실무

Nginx 무중단 배포: Blue-Green/Canary, graceful reload, 안전 롤백(OSS 기준 완전 가이드)

by yamoojin83 2025. 10. 27.

Nginx 무중단 배포: Blue-Green/Canary, graceful reload, 안전 롤백(OSS 기준 완전 가이드)

사용자 접속을 끊지 않고 새 버전을 노출하려면 Nginx의 graceful reload업스트림 전략이 핵심입니다. 이 글은 오픈소스 Nginx(무료판) 기준으로 Blue-Green, Canary, passive health(오류 전환), systemctl reload nginx를 이용한 무중단 교체, 그리고 즉시 롤백 루틴까지 한 번에 정리합니다.


1) 큰 그림: “요청은 계속 흘러가고, 설정만 바뀐다”

  • graceful reload: nginx -t 검증 후 reload 시, 기존 워커는 처리 중 요청을 마무리하고 새 워커가 설정을 반영합니다.
  • 업스트림 다중 포트: 앱을 2개 이상 포트(또는 서버)로 띄운 뒤, Nginx가 분산합니다.
  • Blue-Green: 파란색(Blue)과 초록색(Green) 두 묶음을 준비하고 트래픽 스위칭만 바꿉니다.
  • Canary: 일부분만 신버전으로 보내며 지표를 관찰합니다.
# 점검 & 무중단 적용
sudo nginx -t && sudo systemctl reload nginx

2) 업스트림 기본 템플릿(least_conn + keepalive)

# /etc/nginx/conf.d/app-upstream.conf
upstream app_pool {
  least_conn;                # 바쁜 서버에 덜 보내기
  server 127.0.0.1:8081 max_fails=3 fail_timeout=10s;
  server 127.0.0.1:8082 max_fails=3 fail_timeout=10s;
  keepalive 64;              # 연결 재사용
}
# /etc/nginx/sites-available/app.example.com (발췌)
server {
  listen 443 ssl http2;
  server_name app.example.com;

  location / {
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    proxy_next_upstream error timeout http_502 http_503 http_504;
    proxy_next_upstream_tries 3;

    proxy_read_timeout 65s;
    proxy_connect_timeout 5s;
    proxy_send_timeout 30s;

    proxy_pass http://app_pool;
  }
}

오픈소스 Nginx는 능동(Active) 헬스체크가 없습니다. 대신 위와 같이 passive로 오류/타임아웃 시 다른 노드로 전환합니다(max_fails, fail_timeout, proxy_next_upstream).


3) Blue-Green: 포트 스위칭으로 무중단 전환

앱을 2개 포트로 띄웁니다(예: Blue=8081, Green=8082). 배포는 항상 “유휴 쪽에 새 버전을 올리고 → 스위치 → 구버전 종료” 순서입니다.

# (예시) systemd 서비스 2개 - /etc/systemd/system/myapp-blue.service
[Service]
ExecStart=/usr/bin/java -jar app-blue.jar --server.port=8081
Restart=on-failure

# /etc/systemd/system/myapp-green.service
[Service]
ExecStart=/usr/bin/java -jar app-green.jar --server.port=8082
Restart=on-failure

스위칭 방법 A: upstream 가중치 변경

# /etc/nginx/conf.d/app-upstream.conf (전환 전)
upstream app_pool {
  least_conn;
  server 127.0.0.1:8081 weight=10;  # Blue: 활성
  server 127.0.0.1:8082 weight=0;   # Green: 대기
}
# 전환 후 (Green 활성화)
upstream app_pool {
  least_conn;
  server 127.0.0.1:8081 weight=0;
  server 127.0.0.1:8082 weight=10;
}
# 적용
sudo nginx -t && sudo systemctl reload nginx

스위칭 방법 B: 심볼릭 링크 원자 교체(정적/SSR)

# /etc/nginx/sites-available/app.example.com (정적 루트만 발췌)
root /var/www/current;   # current → blue or green

# 전환 스크립트
ln -sfn /var/www/green /var/www/current \
 && sudo nginx -t && sudo systemctl reload nginx

4) Canary 릴리즈: 일부 트래픽만 신버전으로

쿠키/헤더/비율로 분기합니다. 장애 시 즉시 0%로 되돌리면 사실상 롤백입니다.

4-1) 비율 기반(사용자 무관, split_clients)

split_clients "${remote_addr}${request_uri}" $canary_bucket {
  5%   "canary";
  *    "stable";
}

map $canary_bucket $upstream_target {
  default         app_pool_stable;
  canary          app_pool_canary;
}

upstream app_pool_stable {
  least_conn; server 127.0.0.1:8081;
}
upstream app_pool_canary {
  least_conn; server 127.0.0.1:8082;
}

location / {
  proxy_pass http://$upstream_target;
}

4-2) 쿠키 기반(내부 테스트/화이트리스트)

map $http_cookie $is_canary {
  "~*CANARY=1"  1;
  default       0;
}
map $is_canary $upstream_target {
  1 app_pool_canary;
  0 app_pool_stable;
}

: 비율/쿠키 스위치를 모두 넣어 두고 운영 중엔 비율 0%로 두면 즉시 롤백 동작을 만들 수 있습니다.


5) 배포/검증 루틴(복붙용)

  1. 사전 헬스: 신버전 포트 직접 체크 curl -sfk http://127.0.0.1:8082/actuator/health
  2. 에러 지표 baseline: 현재 5xx/응답시간 기록
  3. Canary 5% 오픈: 5분 관찰(로그/지표)
  4. 점진 확대: 5 → 20 → 50 → 100%
  5. Blue 종료: 안정 확인 후 구버전 중지
# Nginx/앱 상태 동시 tail
sudo tail -f /var/log/nginx/access.log /var/log/nginx/error.log
journalctl -u myapp-blue.service -f
journalctl -u myapp-green.service -f

6) 안전 롤백: 10초 내 되돌리기

  • 가중치 0%로 즉시 전환(Blue-Green).
  • 심볼릭 링크 원복 + reload.
  • 버전 태그가 기록된 단일 스위치 스크립트를 만들어 둡니다.
# /usr/local/sbin/switch-version.sh
#!/usr/bin/env bash
set -euo pipefail
TARGET="${1:-stable}"  # stable|canary|blue|green
sed -i "s/weight=10;  # ACTIVE/weight=0;   # ACTIVE/" /etc/nginx/conf.d/app-upstream.conf
sed -i "s/weight=0;   # STANDBY/weight=10; # STANDBY/" /etc/nginx/conf.d/app-upstream.conf
nginx -t && systemctl reload nginx
echo "$(date -Is) switched to $TARGET" >> /var/log/deploy-switch.log

7) 자주 터지는 함정과 해결

  • reload 후 502/504: 신버전 포트가 실제로 리스닝 중인지 ss -lntp | grep 8082 확인.
  • 세션 유실: 세션을 서버 메모리에 두면 업스트림이 바뀔 때 로그인이 끊깁니다. 외부 세션 저장소(Redis 등)로 이동.
  • Active health 오해: OSS Nginx에 health_check 디렉티브는 없습니다. passive 전환과 짧은 타임아웃/재시도 조합으로 설계.
  • Keepalive 미설정: upstream keepalive가 없으면 연결 재사용이 안 되어 레이턴시가 증가합니다.
  • 정적/SSR 혼재: 정적은 심볼릭 링크로, API는 업스트림 가중치로 스위치 수단을 분리하세요.

8) 관찰성: 에러·지연을 즉시 감지

  • 지표: Nginx status(exporter)와 앱의 p95, 에러율, 502/504 카운트.
  • 로그: 배포 윈도우 동안 $upstream_addr, $upstream_status 필드 포함 포맷으로 분석.
# /etc/nginx/nginx.conf (log_format 발췌)
log_format main '$remote_addr "$request" $status $body_bytes_sent '
                '$upstream_addr $upstream_status $request_time $upstream_response_time';
access_log /var/log/nginx/access.log main;

9) 체크리스트(배포 전)

  • 신버전 포트 직접 헬스 OK, DB 마이그레이션 선적용
  • Nginx 설정 문법 OK(nginx -t), reload 성공
  • 업스트림 keepalive, proxy_next_upstream 재시도 구성
  • Canary 스위치(비율/쿠키) 준비, 롤백 스크립트 존재
  • 로그 포맷에 $upstream_* 필드 포함(분석 가능)

위 템플릿을 그대로 적용하면 “끊김 없이 배포”와 “즉시 롤백”이 습관이 됩니다. 다음 글에서는 HAProxy/Nginx 혼합 환경에서의 Blue-Green + Canary 설계를 비교하고, 다운타임 없는 DB 마이그레이션 팁까지 확장해 보겠습니다.