Blue/Green 배포 (2): Kubernetes에서의 구현
순수 Kubernetes 리소스, Argo Rollouts, Istio를 활용한 Blue/Green 배포 구현과 DB 마이그레이션 전략
2026년 1월 27일·12 min read·
infra
deployment
blue-green
kubernetes
argo-rollouts
istio
devops
이 글은 Blue/Green 배포 시리즈의 두 번째 글입니다.
- 기초와 전략
- Kubernetes 구현 (현재 글)
- 클라우드 & CI/CD 통합
- 운영 및 최적화
1. Kubernetes에서의 Blue/Green 개요
Kubernetes는 선언적 배포 관리를 제공하지만, 기본적으로는 Rolling Update 전략을 사용합니다. Blue/Green 배포를 구현하려면 추가적인 설정이 필요합니다.
구현 방법 비교
| 방법 | 복잡도 | 자동화 | 롤백 | 추천 상황 |
|---|---|---|---|---|
| Service Selector | 낮음 | 수동 | 수동 | 학습, 소규모 |
| Argo Rollouts | 중간 | 자동 | 자동 | 프로덕션 권장 |
| Istio | 높음 | 자동 | 자동 | 대규모, 복잡한 라우팅 |
| Flagger | 중간 | 자동 | 자동 | GitOps 환경 |
2. 순수 Kubernetes로 구현
가장 기본적인 방법으로, Service의 selector를 변경하여 트래픽을 전환합니다.
2.1 아키텍처
2.2 Blue Deployment
# blue-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-blue
labels:
app: my-app
color: blue
spec:
replicas: 3
selector:
matchLabels:
app: my-app
color: blue
template:
metadata:
labels:
app: my-app
color: blue
version: v1.0.0
spec:
containers:
- name: app
image: my-app:1.0.0
ports:
- containerPort: 8080
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
2.3 Green Deployment
# green-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-green
labels:
app: my-app
color: green
spec:
replicas: 3
selector:
matchLabels:
app: my-app
color: green
template:
metadata:
labels:
app: my-app
color: green
version: v1.1.0
spec:
containers:
- name: app
image: my-app:1.1.0
ports:
- containerPort: 8080
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
2.4 Service (트래픽 라우터)
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
type: ClusterIP
selector:
app: my-app
color: blue # 이 값을 green으로 변경하여 전환
ports:
- name: http
port: 80
targetPort: 8080
2.5 전환 스크립트
#!/bin/bash
# switch-traffic.sh
NAMESPACE=${NAMESPACE:-default}
SERVICE_NAME=${SERVICE_NAME:-my-app}
TARGET_COLOR=${1:-green}
echo "Switching traffic to $TARGET_COLOR..."
# 현재 상태 확인
CURRENT_COLOR=$(kubectl get svc $SERVICE_NAME -n $NAMESPACE \
-o jsonpath='{.spec.selector.color}')
echo "Current: $CURRENT_COLOR -> Target: $TARGET_COLOR"
# Green Deployment가 Ready인지 확인
READY_REPLICAS=$(kubectl get deployment my-app-$TARGET_COLOR -n $NAMESPACE \
-o jsonpath='{.status.readyReplicas}')
DESIRED_REPLICAS=$(kubectl get deployment my-app-$TARGET_COLOR -n $NAMESPACE \
-o jsonpath='{.spec.replicas}')
if [ "$READY_REPLICAS" != "$DESIRED_REPLICAS" ]; then
echo "Error: Target deployment not ready ($READY_REPLICAS/$DESIRED_REPLICAS)"
exit 1
fi
# 트래픽 전환
kubectl patch svc $SERVICE_NAME -n $NAMESPACE \
-p "{\"spec\":{\"selector\":{\"color\":\"$TARGET_COLOR\"}}}"
echo "Traffic switched to $TARGET_COLOR successfully!"
# 전환 후 상태 확인
kubectl get svc $SERVICE_NAME -n $NAMESPACE -o wide
kubectl get endpoints $SERVICE_NAME -n $NAMESPACE
2.6 수동 구현의 한계
- 휴먼 에러: selector 변경 시 오타 가능성
- 자동화 어려움: CI/CD 파이프라인에서 상태 관리 복잡
- 롤백 자동화 없음: 문제 발생 시 수동 개입 필요
- 모니터링 연동 없음: 자동 롤백 트리거 불가
3. Argo Rollouts로 자동화
Argo Rollouts는 Kubernetes용 Progressive Delivery Controller로, Blue/Green과 Canary 배포를 선언적으로 관리합니다.
3.1 설치
# Argo Rollouts 설치
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts \
-f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
# kubectl 플러그인 설치 (선택)
brew install argoproj/tap/kubectl-argo-rollouts # macOS
# 또는
curl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64
chmod +x kubectl-argo-rollouts-linux-amd64
sudo mv kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts
3.2 Rollout 리소스
# rollout.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: my-app
spec:
replicas: 4
revisionHistoryLimit: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:1.1.0
ports:
- containerPort: 8080
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
strategy:
blueGreen:
# 현재 운영 트래픽을 받는 서비스
activeService: my-app-active
# 새 버전 미리보기용 서비스
previewService: my-app-preview
# 자동 승인 비활성화 (수동 승인 필요)
autoPromotionEnabled: false
# 전환 후 이전 ReplicaSet 유지 시간
scaleDownDelaySeconds: 30
# Preview ReplicaSet 최소 준비 시간
previewReplicaCount: 2
# 자동 롤백 (Analysis 실패 시)
autoPromotionSeconds: 0 # 0 = 자동 승인 비활성화
3.3 Services 구성
# services.yaml
---
# Active Service (현재 운영)
apiVersion: v1
kind: Service
metadata:
name: my-app-active
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
---
# Preview Service (새 버전 테스트용)
apiVersion: v1
kind: Service
metadata:
name: my-app-preview
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
3.4 배포 워크플로우
# 1. 현재 상태 확인
kubectl argo rollouts get rollout my-app -w
# 2. 새 버전 배포 (이미지 변경)
kubectl argo rollouts set image my-app app=my-app:1.2.0
# 3. Preview 환경 테스트
# my-app-preview 서비스로 내부 테스트 수행
# 4. 트래픽 전환 승인
kubectl argo rollouts promote my-app
# 5. 문제 발생 시 롤백
kubectl argo rollouts abort my-app
# 또는 이전 버전으로
kubectl argo rollouts undo my-app
3.5 대시보드에서 상태 확인
# Argo Rollouts 대시보드 실행
kubectl argo rollouts dashboard
# 브라우저에서 http://localhost:3100 접속
3.6 Analysis를 통한 자동 검증
# analysis-template.yaml
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
spec:
args:
- name: service-name
metrics:
- name: success-rate
interval: 30s
count: 5
successCondition: result[0] >= 0.95
failureLimit: 3
provider:
prometheus:
address: http://prometheus.monitoring:9090
query: |
sum(rate(http_requests_total{
service="{{args.service-name}}",
status=~"2.."
}[5m])) /
sum(rate(http_requests_total{
service="{{args.service-name}}"
}[5m]))
# rollout-with-analysis.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: my-app
spec:
# ... (이전 설정 동일)
strategy:
blueGreen:
activeService: my-app-active
previewService: my-app-preview
autoPromotionEnabled: false
# 전환 전 자동 분석
prePromotionAnalysis:
templates:
- templateName: success-rate
args:
- name: service-name
value: my-app-preview
# 전환 후 자동 분석 (롤백 트리거)
postPromotionAnalysis:
templates:
- templateName: success-rate
args:
- name: service-name
value: my-app-active
4. Istio Service Mesh 연동
Istio를 사용하면 더 세밀한 트래픽 제어가 가능합니다.
4.1 아키텍처
4.2 DestinationRule
# destination-rule.yaml
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: my-app
spec:
host: my-app
subsets:
- name: blue
labels:
version: v1.0.0
- name: green
labels:
version: v1.1.0
4.3 VirtualService (Blue/Green)
# virtual-service.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: my-app
spec:
hosts:
- my-app
- my-app.example.com
gateways:
- my-app-gateway
http:
# 헤더 기반 라우팅 (테스트용)
- match:
- headers:
x-canary:
exact: "true"
route:
- destination:
host: my-app
subset: green
port:
number: 80
# 기본 트래픽 (Blue로)
- route:
- destination:
host: my-app
subset: blue
port:
number: 80
weight: 100
- destination:
host: my-app
subset: green
port:
number: 80
weight: 0
4.4 점진적 전환 (Istio + Blue/Green 하이브리드)
# 단계별 전환 예시
# Step 1: 0% Green
- destination:
host: my-app
subset: blue
weight: 100
- destination:
host: my-app
subset: green
weight: 0
# Step 2: 10% Green (테스트)
- destination:
host: my-app
subset: blue
weight: 90
- destination:
host: my-app
subset: green
weight: 10
# Step 3: 100% Green (전환 완료)
- destination:
host: my-app
subset: blue
weight: 0
- destination:
host: my-app
subset: green
weight: 100
4.5 트래픽 미러링 (Shadow Testing)
# 실제 트래픽을 Green에 복제하여 테스트 (응답은 무시)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: my-app
spec:
hosts:
- my-app
http:
- route:
- destination:
host: my-app
subset: blue
weight: 100
mirror:
host: my-app
subset: green
mirrorPercentage:
value: 100.0
5. DB 스키마 마이그레이션 전략
Blue/Green 배포에서 가장 어려운 부분은 DB 스키마 변경입니다. 두 버전이 동시에 같은 DB를 사용하기 때문입니다.
5.1 Expand & Contract 패턴
5.2 단계별 마이그레이션 예시
Phase 1: Expand (확장)
-- 새 컬럼 추가 (nullable)
ALTER TABLE users ADD COLUMN full_name VARCHAR(255);
-- 기본값 설정 (기존 데이터 호환)
UPDATE users SET full_name = user_name WHERE full_name IS NULL;
# v2 애플리케이션: 두 컬럼 모두 지원
class User:
def get_display_name(self):
# 새 컬럼 우선, 없으면 기존 컬럼 사용
return self.full_name or self.user_name
def save(self):
# 두 컬럼 모두 업데이트 (하위 호환)
self.full_name = self.display_name
self.user_name = self.display_name # v1 호환
Phase 2: Migrate (마이그레이션)
# 배치 마이그레이션 스크립트
def migrate_user_names():
users = User.objects.filter(full_name__isnull=True)
for batch in chunked(users, 1000):
for user in batch:
user.full_name = user.user_name
User.objects.bulk_update(batch, ['full_name'])
Phase 3: Contract (축소)
-- v1이 완전히 제거된 후에만 실행
-- 충분한 모니터링 기간 후 (예: 2주)
ALTER TABLE users DROP COLUMN user_name;
5.3 마이그레이션 체크리스트
| 단계 | 작업 | Blue(v1) | Green(v2) | 롤백 가능 |
|---|---|---|---|---|
| 1 | 새 컬럼 추가 | 무시 | 읽기/쓰기 | ✅ |
| 2 | 데이터 복사 | 무시 | 읽기/쓰기 | ✅ |
| 3 | v2로 전환 | - | 활성 | ✅ |
| 4 | v1 제거 | 삭제 | 활성 | ⚠️ |
| 5 | 이전 컬럼 삭제 | - | 활성 | ❌ |
중요: Phase 5는 롤백이 불가능하므로, 충분한 모니터링 기간(최소 1-2주)을 거친 후 실행해야 합니다.
6. 다음 단계
이 글에서는 Kubernetes에서 Blue/Green 배포를 구현하는 다양한 방법을 살펴보았습니다.
다음 글에서는 클라우드 환경과 CI/CD 파이프라인 통합을 다룹니다:
- AWS ALB + Target Group
- GCP Cloud Load Balancing
- GitHub Actions 자동화
- GitLab CI/CD 통합
이전 글: Blue/Green 배포 (1) - 기초와 전략 ← 다음 글: Blue/Green 배포 (3) - 클라우드 & CI/CD →