배포 전략 — 롤링 업데이트, 카나리, Blue-Green을 Kubernetes에서 구현하기
새 버전을 배포할 때 "전체 사용자에게 한 번에" vs "일부에게 먼저", 어떤 전략이 안전할까요?
배포 전략에 정답은 없습니다. 서비스 특성, 팀 역량, 리스크 허용 범위에 따라 달라집니다. Kubernetes는 기본 롤링 업데이트 외에도 카나리, Blue-Green 같은 고급 배포 전략을 구현할 수 있는 도구를 제공합니다.
배포 전략 비교
| 전략 | 다운타임 | 리소스 | 롤백 속도 | 복잡도 |
|---|---|---|---|---|
| 롤링 업데이트 | 없음 | 약간 추가 | 중간 | 낮음 |
| Blue-Green | 없음 | 2배 | 매우 빠름 | 중간 |
| 카나리 | 없음 | 약간 추가 | 빠름 | 높음 |
| Recreate | 있음 | 추가 없음 | 느림 | 낮음 |
롤링 업데이트 (기본)
Kubernetes Deployment의 기본 전략입니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 추가로 생성할 수 있는 Pod
maxUnavailable: 0 # 동시에 사용 불가한 Pod 수
readinessProbe는 롤링 업데이트에서 필수입니다. 새 Pod이 Ready 상태가 되어야 기존 Pod 교체가 진행됩니다.
template:
spec:
containers:
- name: app
image: web-app:2.0
readinessProbe: # 필수! 새 Pod이 Ready여야 교체 진행
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
**장점 **: 간단하고 무중단 배포 가능 ** 단점 **: 문제 감지가 느림 — 모든 Pod이 교체될 때까지 확인 어려움
롤링 업데이트 최적화
spec:
minReadySeconds: 30 # Ready 후 30초 대기 — 안정성 확보
progressDeadlineSeconds: 300 # 5분 내 완료 안 되면 실패
strategy:
rollingUpdate:
maxSurge: 25% # 비율로 지정 가능
maxUnavailable: 0 # 무중단 보장
Blue-Green 배포
구 버전(Blue)과 새 버전(Green)을 동시에 배포한 후, 트래픽을 한 번에 전환합니다.
Service 셀렉터 전환 방식
# Blue Deployment (현재 운영)
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app-blue
spec:
replicas: 4
selector:
matchLabels:
app: web-app
version: blue
template:
metadata:
labels:
app: web-app
version: blue
spec:
containers:
- name: app
image: web-app:1.0
배포 설정을 이어서 정의합니다.
---
# Green Deployment (새 버전)
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app-green
spec:
replicas: 4
selector:
matchLabels:
Pod 템플릿과 컨테이너 스펙을 이어서 정의합니다.
app: web-app
version: green
template:
metadata:
labels:
app: web-app
version: green
spec:
containers:
- name: app
image: web-app:2.0
Service의 selector에서 version 값만 변경하면 트래픽이 즉시 전환됩니다. 롤백도 같은 방식으로 이전 버전으로 되돌리면 됩니다.
---
# Service — selector로 Blue/Green 전환
apiVersion: v1
kind: Service
metadata:
name: web-app
spec:
selector:
app: web-app
version: blue # green으로 변경하면 트래픽 전환!
ports:
- port: 80
targetPort: 8080
# 트래픽 전환 (Blue → Green)
kubectl patch svc web-app -p '{"spec":{"selector":{"version":"green"}}}'
# 롤백 (Green → Blue)
kubectl patch svc web-app -p '{"spec":{"selector":{"version":"blue"}}}'
** 장점 **: 즉시 롤백 가능, 전환 전 새 버전 테스트 가능 ** 단점 **: 리소스 2배, 데이터베이스 스키마 호환성 필요
카나리 배포
전체 트래픽의 일부만 새 버전으로 보내서 안전성을 확인한 후 전체 배포합니다.
Nginx Ingress 카나리 어노테이션
# 기존 서비스 Ingress (v1)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-app-stable
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-app-stable
port:
number: 80
카나리 Ingress에 canary 어노테이션을 추가하면 동일한 호스트에서 가중치 기반으로 트래픽을 분할할 수 있습니다.
---
# 카나리 Ingress (v2) — 10% 트래픽
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-app-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10" # 10% 트래픽
spec:
ingressClassName: nginx
나머지 설정을 이어서 정의합니다.
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-app-canary
port:
number: 80
트래픽 비율을 점진적으로 올려가며 모니터링합니다.
# 10% → 25% → 50% → 100%
kubectl annotate ingress web-app-canary \
nginx.ingress.kubernetes.io/canary-weight="25" --overwrite
헤더 기반 카나리
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
nginx.ingress.kubernetes.io/canary-by-header-value: "true"
X-Canary: true 헤더를 보내는 요청만 카나리로 라우팅합니다. 내부 테스트에 유용합니다.
Argo Rollouts — 고급 배포 자동화
Argo Rollouts는 Deployment를 대체하는 Rollout CRD를 제공하여 카나리/Blue-Green을 선언적으로 구현합니다.
# 설치
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
카나리 Rollout
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: web-app
spec:
replicas: 5
strategy:
canary:
steps:
- setWeight: 10 # 10% 트래픽
Ingress 규칙을 이어서 정의합니다.
- pause: { duration: 5m } # 5분 대기
- setWeight: 30 # 30% 트래픽
- pause: { duration: 5m }
- setWeight: 60
- pause: { duration: 5m }
- setWeight: 100 # 전체 배포
canaryService: web-app-canary
stableService: web-app-stable
trafficRouting:
nginx:
stableIngress: web-app-ingress
selector와 template은 일반 Deployment와 동일한 방식으로 정의합니다.
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: app
image: web-app:2.0
Blue-Green Rollout
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: web-app
spec:
replicas: 4
strategy:
blueGreen:
activeService: web-app-active # 현재 활성 Service
previewService: web-app-preview # 미리보기 Service
autoPromotionEnabled: false # 수동 승인 필요
prePromotionAnalysis로 전환 전 자동 메트릭 검증을 추가하고, scaleDownDelaySeconds로 이전 버전을 일정 시간 유지합니다.
prePromotionAnalysis: # 프로모션 전 자동 분석
templates:
- templateName: success-rate
scaleDownDelaySeconds: 300 # 이전 버전 5분 후 스케일 다운
selector:
matchLabels:
app: web-app
template:
spec:
containers:
- name: app
image: web-app:2.0
AnalysisTemplate — 메트릭 기반 자동 판단
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
spec:
metrics:
- name: success-rate
interval: 1m
count: 5 # 5번 측정
successCondition: result[0] >= 0.95 # 성공률 95% 이상
failureLimit: 2 # 2번 실패하면 롤백
provider:
prometheus:
address: http://prometheus:9090
query: |
sum(rate(http_requests_total{status=~"2.."}[5m]))
/ sum(rate(http_requests_total[5m]))
Prometheus에서 성공률을 측정하여 95% 미만이면 자동 롤백합니다.
# Rollout 상태 확인
kubectl argo rollouts get rollout web-app --watch
# 수동 프로모션 (Blue-Green에서 autoPromotionEnabled: false일 때)
kubectl argo rollouts promote web-app
# 중단
kubectl argo rollouts abort web-app
전략 선택 가이드
| 상황 | 추천 전략 |
|---|---|
| 일반적인 서비스 업데이트 | 롤링 업데이트 |
| 빠른 롤백이 중요한 경우 | Blue-Green |
| 점진적 검증이 필요한 경우 | 카나리 |
| DB 스키마 변경이 포함된 경우 | Blue-Green + DB 마이그레이션 |
| 대규모 서비스, 자동화된 검증 필요 | Argo Rollouts |
주의할 점
1. DB 스키마 변경과 롤링 업데이트를 동시에 하면 장애가 난다
롤링 업데이트 중에는 구버전과 신버전 Pod이 동시에 트래픽을 받습니다. 이 상태에서 DB 컬럼을 삭제하거나 이름을 바꾸면, 구버전 Pod이 존재하지 않는 컬럼을 조회하면서 500 에러가 발생합니다. DB 마이그레이션은 반드시 하위 호환을 유지하는 방식(컬럼 추가 → 코드 배포 → 구 컬럼 삭제)으로 분리해서 진행해야 합니다.
2. readinessProbe 없이 카나리 배포하면 불량 Pod에 트래픽이 간다
카나리 배포의 핵심은 "소수의 Pod으로 먼저 검증"하는 것인데, readinessProbe가 없으면 Pod이 뜨자마자 트래픽을 받습니다. 애플리케이션이 아직 초기화 중이거나 에러 상태여도 Service에 등록되어 실제 사용자 요청을 처리하게 됩니다. 카나리 Pod에는 반드시 readinessProbe를 설정하세요.
3. Blue-Green에서 이전 환경을 너무 빨리 제거하면 롤백할 수 없다
Blue-Green 전환 후 "잘 되는 것 같으니" 바로 이전 환경을 삭제하는 경우가 많습니다. 그런데 전환 직후에는 발견되지 않던 문제가 트래픽이 쌓이면서 나타날 수 있습니다. 이전 환경은 최소 1~2시간(트래픽 패턴에 따라 더 길게) 유지한 뒤 제거하세요.
정리
롤링 업데이트는 Kubernetes의 기본이며 대부분의 경우 충분합니다. 더 안전한 배포가 필요하면 카나리(일부 트래픽으로 검증)나 Blue-Green(전체 환경 전환)을 사용하고, Argo Rollouts를 도입하면 메트릭 기반 자동 분석과 프로모션으로 배포 안전성을 한 단계 끌어올릴 수 있습니다.