🔧 DevOps

블루-그린 배포

Blue-Green Deployment

두 개의 동일한 프로덕션 환경(블루/그린)을 교대로 사용하는 무중단 배포 전략입니다. 새 버전을 비활성 환경에 배포 후 로드밸런서로 트래픽을 전환하여 다운타임 없이 배포하고, 문제 발생 시 즉시 롤백할 수 있습니다.

📖 상세 설명

블루-그린 배포(Blue-Green Deployment)는 두 개의 동일한 프로덕션 환경을 유지하면서 배포하는 전략입니다. 현재 트래픽을 받는 환경을 '블루', 새 버전을 배포할 유휴 환경을 '그린'이라 부릅니다. 새 버전 배포 후 테스트를 완료하면 로드밸런서나 DNS를 통해 트래픽을 그린으로 전환합니다.

이 기법은 2010년 Jez Humble과 David Farley의 책 "Continuous Delivery"에서 대중화되었습니다. Netflix, Amazon, Facebook 등 대규모 서비스를 운영하는 기업들이 무중단 배포를 위해 채택했고, Kubernetes와 클라우드 서비스의 발전으로 더욱 쉽게 구현할 수 있게 되었습니다.

핵심 원리는 단순합니다. 블루 환경이 라이브 상태일 때, 그린 환경에 새 버전을 배포하고 충분히 테스트합니다. 모든 검증이 완료되면 로드밸런서 설정만 변경하여 트래픽을 그린으로 전환합니다. 문제가 발생하면 다시 블루로 트래픽을 되돌리는 것으로 즉시 롤백됩니다. 전환 시간은 수 초에 불과합니다.

실무에서 블루-그린 배포는 고가용성이 중요한 서비스에 필수입니다. 다만 인프라 비용이 2배 필요하고, 데이터베이스 스키마 변경 시 추가 고려가 필요합니다. AI 모델 서빙에서도 널리 사용되며, 새 모델 버전을 그린 환경에 배포하고 A/B 테스트 후 전환하는 패턴이 일반적입니다.

┌─────────────────────────────────────────────────────────┐
│                    Load Balancer                        │
│                         │                               │
│            ┌────────────┴────────────┐                  │
│            ▼                         ▼                  │
│    ┌──────────────┐          ┌──────────────┐          │
│    │  🔵 BLUE     │          │  🟢 GREEN    │          │
│    │  (v1.0)      │  ──────▶ │  (v2.0)      │          │
│    │  [ACTIVE]    │  Switch  │  [STANDBY]   │          │
│    └──────────────┘          └──────────────┘          │
│                                                         │
│    배포 후 트래픽 전환: Blue → Green                    │
│    롤백 필요시: Green → Blue (수 초 내 완료)            │
└─────────────────────────────────────────────────────────┘
                    

📊 배포 전략 비교

전략 다운타임 롤백 속도 인프라 비용 적합한 상황
블루-그린 없음 즉시 (수 초) 2배 미션 크리티컬 서비스
카나리 없음 빠름 (분) 1.1~1.5배 점진적 검증 필요 시
롤링 없음 느림 (분~시간) 1배 비용 민감 환경
재생성 있음 느림 1배 개발/스테이징 환경

💻 코드 예제

Kubernetes와 AWS에서 블루-그린 배포를 구현하는 예제입니다.

# blue-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-blue
  labels:
    app: myapp
    version: blue
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      version: blue
  template:
    metadata:
      labels:
        app: myapp
        version: blue
    spec:
      containers:
      - name: myapp
        image: myapp:1.0.0
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
---
# green-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-green
  labels:
    app: myapp
    version: green
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      version: green
  template:
    metadata:
      labels:
        app: myapp
        version: green
    spec:
      containers:
      - name: myapp
        image: myapp:2.0.0  # 새 버전
        ports:
        - containerPort: 8080
---
# service.yaml (블루로 트래픽 라우팅)
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  selector:
    app: myapp
    version: blue  # 이 값을 green으로 변경하면 전환!
  ports:
  - port: 80
    targetPort: 8080

# 전환 명령어
# kubectl patch service myapp-service -p '{"spec":{"selector":{"version":"green"}}}'

# 롤백 명령어
# kubectl patch service myapp-service -p '{"spec":{"selector":{"version":"blue"}}}'
# appspec.yml (AWS CodeDeploy Blue-Green)
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "arn:aws:ecs:region:account:task-definition/myapp:2"
        LoadBalancerInfo:
          ContainerName: "myapp"
          ContainerPort: 8080
        PlatformVersion: "LATEST"

Hooks:
  - BeforeInstall: "LambdaFunctionToValidateBeforeInstall"
  - AfterInstall: "LambdaFunctionToValidateAfterInstall"
  - AfterAllowTestTraffic: "LambdaFunctionToRunTests"
  - BeforeAllowTraffic: "LambdaFunctionToValidateBeforeTraffic"
  - AfterAllowTraffic: "LambdaFunctionToValidateAfterTraffic"

# deployment-config.json
{
  "deploymentConfigName": "CodeDeployDefault.ECSLinear10PercentEvery1Minutes",
  "computePlatform": "ECS",
  "trafficRoutingConfig": {
    "type": "TimeBasedLinear",
    "timeBasedLinear": {
      "linearInterval": 1,
      "linearPercentage": 10
    }
  }
}

# AWS CLI 배포 명령
# aws deploy create-deployment \
#   --application-name MyApp \
#   --deployment-group-name MyDeploymentGroup \
#   --revision revisionType=S3,bucket=mybucket,key=myapp.zip
#!/bin/bash
# blue-green-switch.sh - Nginx 블루-그린 전환 스크립트

NGINX_CONF="/etc/nginx/conf.d/upstream.conf"
BLUE_PORT=8080
GREEN_PORT=8081
CURRENT=$(grep "server 127.0.0.1:" $NGINX_CONF | grep -oP '\d+')

echo "현재 활성 환경: Port $CURRENT"

if [ "$CURRENT" == "$BLUE_PORT" ]; then
    NEW_PORT=$GREEN_PORT
    NEW_ENV="GREEN"
else
    NEW_PORT=$BLUE_PORT
    NEW_ENV="BLUE"
fi

# 새 환경 헬스체크
echo "헬스체크: $NEW_ENV (Port $NEW_PORT)..."
if ! curl -sf http://127.0.0.1:$NEW_PORT/health > /dev/null; then
    echo "❌ 헬스체크 실패! 전환 중단"
    exit 1
fi
echo "✅ 헬스체크 통과"

# Nginx 설정 업데이트
echo "트래픽 전환: $CURRENT → $NEW_PORT"
sed -i "s/server 127.0.0.1:$CURRENT/server 127.0.0.1:$NEW_PORT/" $NGINX_CONF

# Nginx 리로드 (무중단)
nginx -t && nginx -s reload

echo "✅ 전환 완료! 현재 활성: $NEW_ENV (Port $NEW_PORT)"

# 롤백 스크립트 생성
cat > /tmp/rollback.sh << EOF
sed -i "s/server 127.0.0.1:$NEW_PORT/server 127.0.0.1:$CURRENT/" $NGINX_CONF
nginx -t && nginx -s reload
echo "롤백 완료: Port $CURRENT"
EOF
chmod +x /tmp/rollback.sh
echo "롤백 명령: /tmp/rollback.sh"

🗣️ 실무 대화 예시

💬 배포 회의에서

"오늘 v2.0 배포는 블루-그린으로 진행합니다. 현재 블루가 라이브고, 그린에 새 버전 배포 후 QA 완료되면 17시에 전환할게요. 혹시 모르니 롤백 담당자 지정해주세요. 전환은 kubectl patch 한 줄이면 됩니다."

💬 장애 대응에서

"그린 환경에서 메모리 누수 발견됐습니다. 지금 바로 블루로 롤백하겠습니다. 서비스 셀렉터만 바꾸면 되니까 10초 안에 복구됩니다. 롤백 완료되면 그린 환경에서 원인 분석할게요."

💬 아키텍처 리뷰에서

"블루-그린의 단점은 인프라 비용이 2배라는 점인데요, 저희 AI 추론 서버처럼 GPU 인스턴스 비용이 높으면 부담됩니다. 카나리 배포로 10%씩 트래픽 이동시키는 게 비용 효율적일 수 있어요."

⚠️ 주의사항

데이터베이스 스키마 변경 시 주의

블루와 그린이 같은 DB를 공유할 때, 스키마 변경은 양쪽 버전과 호환되어야 합니다. 컬럼 삭제는 두 버전 모두 사용하지 않은 후에만 진행하세요.

세션 상태 관리 누락

인메모리 세션을 사용하면 전환 시 사용자 세션이 유실됩니다. Redis나 DB 기반 세션 스토어를 사용하거나 JWT 같은 Stateless 인증을 적용하세요.

올바른 접근

전환 전 그린 환경에서 충분한 테스트(스모크 테스트, 로드 테스트)를 수행하세요. 헬스체크 엔드포인트를 구현하고, 전환 후에도 모니터링을 강화하여 문제 발생 시 신속히 롤백하세요.

🔥 실제 장애 사례

Knight Capital 2012

45분 만에 4억 4천만 달러 손실 - 배포 실패

원인: 8개 서버 중 1개에 새 코드 미배포, 구버전과 신버전 혼재

영향: 잘못된 주문 대량 발생, 45분간 4.4억 달러 손실, 회사 파산

교훈: 블루-그린 배포로 전체 환경 일관성 보장, 배포 검증 자동화 필수

📎 상세 분석
GitLab 2017

데이터베이스 삭제 사고 - 롤백 불가

원인: 운영 DB에서 실수로 rm -rf 실행, 백업 미작동

영향: 6시간 데이터 손실, 18시간 서비스 중단

교훈: 블루-그린은 애플리케이션 롤백에는 효과적이나, DB 장애에는 별도 백업 전략 필요

📎 GitLab 공식 포스트모템

📝 이해도 퀴즈

Q1. 블루-그린 배포의 가장 큰 장점은?

Q2. 블루-그린 배포 시 데이터베이스 스키마 변경에 대한 올바른 접근은?

Q3. 카나리 배포와 비교했을 때 블루-그린의 특징은?

🔗 관련 용어

📚 더 배우기