배포 전략: Rolling·Blue-Green·Canary 비교와 롤백·DB 마이그레이션

개요

새 버전을 배포한다는 것은 곧 위험을 감수하는 일이다. 문제는 “어떻게 사용자 영향을 최소화하며 새 버전으로 전환하고, 잘못됐을 때 얼마나 빨리 되돌리는가”이다. 대표 전략인 Rolling, Blue-Green, Canary는 각각 자원 비용·롤백 속도·위험 노출을 다르게 절충한다. 어느 하나가 절대 우월한 것이 아니라, 서비스의 특성(트래픽·상태·SLO)과 인프라 예산에 맞춰 선택하는 문제다. Kubernetes에서 이들을 선언형으로 구현하는 방법은 Kubernetes - GitOps와 ArgoCD의 Argo Rollouts와 함께 본다.

핵심 개념과 원리

세 전략 비교

전략방식장점단점추가 자원
Rolling인스턴스를 소수씩 순차 교체추가 자원 최소, 단순롤백 느림, 신·구 버전 혼재거의 없음
Blue-Green새 환경(Green) 전체를 띄우고 트래픽 일괄 전환즉시 전환·즉시 롤백, 혼재 없음순간적으로 자원 2배약 2배
Canary소수 트래픽에 먼저 노출 후 점진 확대위험 최소화, 실사용 지표 관측파이프라인·라우팅 복잡소량 추가

Rolling 배포

Rolling은 기존 인스턴스를 한 번에 몇 개씩(예: 25%씩) 새 버전으로 교체한다. 추가 인프라가 거의 필요 없고 Kubernetes Deployment의 기본 전략이라 가장 흔하다. 대신 교체 도중에는 신·구 버전이 동시에 트래픽을 받으므로(버전 혼재), 두 버전이 같은 API·DB 스키마와 호환돼야 한다. 또한 문제를 발견했을 때 되돌리려면 다시 역방향 롤링이 필요해 롤백이 느리다.

교체 속도는 두 파라미터로 조절한다.

  • maxSurge: 목표 복제본을 초과해 임시로 더 띄울 수 있는 수(전환 속도↑).
  • maxUnavailable: 교체 중 동시에 내려가도 되는 수(가용성↓ 허용치).

maxUnavailable: 0으로 두고 readinessProbe를 붙이면 진짜 무중단 롤링이 된다(Kubernetes - Pod와 워크로드).

Blue-Green 배포

Blue(현재 운영)와 Green(새 버전) 두 개의 완전한 환경을 나란히 두고, Green이 준비·검증되면 라우터(로드밸런서·Ingress)를 Green으로 일괄 전환한다. 전환은 순간적이라 사용자는 버전 혼재를 경험하지 않고, 문제가 생기면 라우팅을 Blue로 되돌리기만 하면 되므로 롤백이 즉각적이다. 대가는 자원이다. 전환 기간 동안 동일 규모의 환경을 두 벌 유지해야 해 비용이 약 2배가 된다. 상태가 없는 웹/API에 적합하며, 공유 데이터베이스가 있으면 스키마 호환성 문제가 그대로 남는다.

Canary 배포

Canary(탄광의 카나리아)는 새 버전을 극소수 트래픽(예: 5%)에만 먼저 노출하고, 실제 사용자 트래픽에서 나오는 지표를 관측하며 문제가 없으면 비중을 단계적으로 늘린다. 세 전략 중 위험 노출이 가장 낮은데, 결함이 있어도 전체가 아니라 소수 사용자에게만 영향을 주고 관측되는 즉시 되돌릴 수 있기 때문이다. 대신 트래픽 가중치 분배와 지표 수집·판단을 위한 파이프라인이 필요해 구현이 가장 복잡하다.

Canary 판단: 지표 기반 단계 확대

Canary의 핵심은 “다음 단계로 확대할지”를 감(感)이 아니라 **지표(metric)**로 판단하는 것이다. 각 단계에서 새 버전(canary)과 기존 버전(baseline)의 지표를 비교해 임계값을 넘지 않으면 확대, 넘으면 자동 롤백한다.

5% ──관측──▶ 25% ──관측──▶ 50% ──관측──▶ 100%
   지표 정상        지표 정상        지표 정상
      │이상            │이상            │이상
      └──── 자동 롤백 ◀─┴───────────────┘

판단 지표는 대개 다음을 본다.

  • 에러율: HTTP 5xx 비율, 예외 발생률
  • 지연(latency): p95/p99 응답 시간
  • 비즈니스 KPI: 결제 성공률, 전환율 등 도메인 지표
  • 리소스: CPU·메모리 이상 증가

이렇게 지표로 승격/롤백을 자동화하는 방식을 progressive delivery라 하며, Kubernetes에서는 Argo Rollouts나 Flagger가 Prometheus 지표를 쿼리해 이 판단을 대신한다.

롤백 전략

전략롤백 방법속도
Rollingkubectl rollout undo(역방향 재롤링)보통(교체 시간 소요)
Blue-Green라우팅을 Blue로 되돌림즉시
Canary트래픽 가중치를 0%로, 이전 버전 유지즉시

빠른 롤백의 공통 원리는 “이전 버전을 살려둔 채 트래픽만 되돌린다”는 것이다. Blue-Green과 Canary가 빠른 이유가 여기 있다. GitOps 환경에서는 배포 자체가 Git 상태이므로 git revert 한 번이 곧 롤백이 된다(Kubernetes - GitOps와 ArgoCD).

DB 스키마는 배포와 분리: expand-contract

어떤 배포 전략도 데이터베이스 스키마 변경 앞에서는 무력해진다. 배포 중에는 신·구 애플리케이션 버전이 같은 DB를 공유하는 순간이 생기기 때문이다(Rolling의 혼재, Blue-Green의 전환 순간, Canary의 병행). 컬럼을 곧바로 이름 변경/삭제하면 아직 살아 있는 옛 버전이 즉시 깨진다.

해결책은 스키마 변경을 애플리케이션 배포와 분리하고, 하위 호환을 유지하며 여러 단계로 나누는 expand-contract(팽창-수축) 패턴이다.

  1. Expand(팽창): 새 컬럼/테이블을 추가만 한다. 기존 구조는 그대로 두어 옛 버전도 정상 동작.
  2. Migrate(이행): 신·구 버전이 모두 동작하도록 애플리케이션이 새·옛 구조를 함께 읽고, 필요 시 데이터를 백필(backfill)한다. 이 상태에서 새 앱 버전을 배포한다.
  3. Contract(수축): 모든 트래픽이 새 버전으로 넘어가 옛 구조를 아무도 쓰지 않게 된 뒤, 별도 릴리스에서 옛 컬럼/테이블을 제거한다.

핵심 원칙은 “한 번의 배포에서 스키마와 코드를 동시에 깨는 변경을 하지 않는다”이다. 컬럼 이름 변경은 곧바로 하지 않고 “추가 → 양쪽 쓰기 → 옛 것 제거”의 여러 배포로 쪼갠다.

실전

Rolling(Kubernetes 기본)

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 0     # 무중단 우선
kubectl set image deploy/api api=myorg/api:1.5.0
kubectl rollout status deploy/api
kubectl rollout undo deploy/api        # 롤백

Canary(Argo Rollouts, 지표 기반)

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata: { name: api }
spec:
  strategy:
    canary:
      steps:
        - setWeight: 5
        - pause: { duration: 10m }     # 지표 관측 구간
        - analysis:                    # 에러율 SLO 위반 시 자동 롤백
            templates: [{ templateName: error-rate }]
        - setWeight: 25
        - pause: { duration: 10m }
        - setWeight: 100

expand-contract 마이그레이션 순서

-- 1) Expand: 추가만(하위호환 유지)
ALTER TABLE users ADD COLUMN full_name varchar(200);
 
-- 2) Migrate: 앱이 old(first/last)와 new(full_name) 둘 다 쓰도록 배포 + 백필
UPDATE users SET full_name = concat(first_name,' ',last_name)
WHERE full_name IS NULL;
 
-- 3) Contract: 새 버전 100% 전환 확인 후, 별도 릴리스에서 제거
ALTER TABLE users DROP COLUMN first_name, DROP COLUMN last_name;

함정과 베스트 프랙티스

  • Rolling에서 버전 혼재 무시 금지: 배포 중 신·구가 공존하므로 API·스키마는 항상 하위 호환이어야 한다.
  • Blue-Green의 상태·세션 처리: 전환 시 진행 중인 세션·in-flight 요청을 어떻게 넘길지(스티키 세션, 드레이닝) 설계해야 한다.
  • Canary 지표 관측 창 확보: 트래픽이 적은 시간대의 5%는 통계적으로 무의미하다. 충분한 샘플이 쌓일 관측 시간을 둔다.
  • 롤백 리허설: 롤백 절차를 실제로 연습해 둔다. 장애 순간에 처음 해보면 늦다.
  • 스키마 파괴적 변경을 배포와 묶지 말 것: rename/drop을 코드 배포와 한 릴리스에 넣으면 롤백이 불가능해진다. expand-contract로 분리한다.
  • 관측성 선행: 지표 기반 판단은 모니터링이 갖춰져야 성립한다. 관측성 없이 Canary는 그냥 느린 Rolling일 뿐이다.

정리

Rolling은 자원 효율적이지만 롤백이 느리고 버전이 혼재하며, Blue-Green은 즉시 전환·롤백이 가능하지만 자원이 2배 들고, Canary는 위험 노출이 가장 낮지만 지표 파이프라인이 필요하다. 빠른 롤백의 공통 원리는 “이전 버전을 살려두고 트래픽만 되돌린다”이며, 어떤 전략을 쓰든 DB 스키마는 expand-contract로 배포와 분리해 하위 호환을 지켜야 한다. Kubernetes에서의 선언형 구현은 Kubernetes - GitOps와 ArgoCD, 무중단 롤링의 워크로드 설정은 Kubernetes - Pod와 워크로드를 참고한다.