배포 롤백 전략: 안전하게 되돌리는 자동화 설계
배포는 새 기능을 세상에 내보내는 순간이지만, 동시에 장애가 가장 많이 발생하는 순간이기도 합니다. 아무리 테스트를 촘촘히 짜도 프로덕션 트래픽과 실데이터 앞에서는 예상치 못한 문제가 튀어나옵니다. 이때 진짜 실력을 가르는 건 “얼마나 배포를 안 터뜨리느냐”가 아니라 “터졌을 때 얼마나 빨리, 안전하게 되돌리느냐”입니다.
롤백을 수동으로 하려 들면 장애 한복판에서 사람이 당황한 채로 명령어를 입력하게 됩니다. 그 자체가 2차 장애의 씨앗입니다. 이 글에서는 롤백을 사람의 판단 없이도 안전하게 되돌아가는 자동화된 안전장치로 설계하는 방법을 다룹니다. 배포 전략 선택부터 데이터베이스 마이그레이션이라는 까다로운 문제까지 실전 관점에서 정리했습니다.
롤백을 전제로 배포를 설계하라
롤백이 어려운 배포는 대부분 “앞으로만 가는” 구조로 설계되어 있습니다. 컨테이너 이미지를 태그로 고정하지 않고 latest로 밀어버리거나, 이전 버전 아티팩트를 보관하지 않거나, 상태가 여기저기 흩어져 있는 경우입니다. 롤백 가능한 배포의 첫 조건은 모든 배포가 불변(immutable)이고 버전이 고정되어 있어야 한다는 것입니다.
- 이미지 태그는 항상 커밋 SHA 또는 시맨틱 버전으로 고정한다.
- 이전 N개 버전의 아티팩트를 레지스트리에 보관한다.
- 설정과 시크릿은 코드와 함께 버전 관리한다.
# 절대 하면 안 되는 방식 - 되돌릴 수 없다
docker build -t myapp:latest .
docker push myapp:latest
# 롤백 가능한 방식 - SHA로 고정
GIT_SHA=$(git rev-parse --short HEAD)
docker build -t registry.int4.io/myapp:${GIT_SHA} .
docker push registry.int4.io/myapp:${GIT_SHA}
# 롤백은 그저 이전 SHA로 재배포하는 것
kubectl set image deployment/myapp app=registry.int4.io/myapp:a1b2c3d
블루-그린과 카나리, 무엇을 언제 쓸까
롤백 자동화의 핵심은 배포 전략 선택에서 시작됩니다. 블루-그린은 신구 두 환경을 동시에 띄워놓고 트래픽을 한 번에 전환하는 방식입니다. 롤백이 트래픽 스위치를 되돌리는 것뿐이라 가장 빠르지만, 인프라를 두 벌 유지해야 해서 비용이 큽니다. 카나리는 일부 트래픽만 새 버전에 흘려보내며 지표를 관찰하다가 점진적으로 확대하는 방식입니다. 장애 폭발 반경이 작지만 구성이 복잡합니다.
정답은 없습니다. 상태가 거의 없고 빠른 전환이 중요하면 블루-그린, 리스크가 크고 점진 검증이 필요하면 카나리가 유리합니다. Argo Rollouts로 카나리를 선언적으로 정의하면 지표 기반 자동 롤백까지 붙일 수 있습니다.
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: myapp
spec:
replicas: 6
strategy:
canary:
steps:
- setWeight: 20
- pause: {duration: 2m}
- analysis:
templates:
- templateName: error-rate
- setWeight: 50
- pause: {duration: 5m}
- setWeight: 100
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: registry.int4.io/myapp:a1b2c3d
지표 기반 자동 롤백 게이트
사람이 대시보드를 지켜보다 롤백을 결정하면 늦습니다. 배포 파이프라인에 지표 게이트를 심어, 에러율이나 지연시간이 임계치를 넘으면 자동으로 이전 버전으로 되돌리게 해야 합니다. Argo Rollouts의 AnalysisTemplate은 Prometheus 쿼리 결과로 성공/실패를 판정합니다.
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: error-rate
spec:
metrics:
- name: error-rate
interval: 30s
count: 4
# 실패 조건: 5xx 비율이 5%를 넘으면 롤백
failureCondition: result > 0.05
failureLimit: 2
provider:
prometheus:
address: http://prometheus.monitoring:9090
query: |
sum(rate(http_requests_total{app="myapp",status=~"5.."}[2m]))
/
sum(rate(http_requests_total{app="myapp"}[2m]))
임계치는 절대적인 숫자가 아니라 기존 버전 대비 상대적 악화로 잡는 편이 안전합니다. 평소 5xx가 1%인 서비스라면 절대 5% 임계치는 너무 느슨할 수 있습니다.
데이터베이스 마이그레이션이라는 함정
코드 롤백은 이미지 태그만 바꾸면 되지만, 스키마는 되돌리기 어렵습니다. DROP COLUMN을 실행한 뒤 롤백하면 데이터가 이미 사라진 상태입니다. 그래서 마이그레이션은 반드시 확장-수축(expand-contract) 패턴으로 분리해야 합니다. 파괴적 변경을 배포와 같은 릴리스에 묶지 않는 것이 핵심입니다.
- Expand: 새 컬럼/테이블을 추가만 한다. 구버전 코드도 계속 동작한다.
- Migrate: 신구 코드가 공존하는 동안 데이터를 채운다.
- Contract: 구버전이 완전히 사라진 뒤, 별도 릴리스에서 옛 컬럼을 제거한다.
-- 릴리스 1 (expand): 안전하게 추가만, 롤백해도 무해
ALTER TABLE users ADD COLUMN email_verified boolean DEFAULT false;
-- 애플리케이션 코드는 신구 컬럼 모두 읽을 수 있게 배포
-- 릴리스 2 (contract): 며칠 뒤, 구버전 트래픽이 0이 된 것을 확인 후
ALTER TABLE users DROP COLUMN legacy_verified;
이 원칙을 지키면 어떤 릴리스에서도 코드만 되돌려 즉시 정상화할 수 있습니다. 반대로 이 원칙을 어기면 아무리 자동화가 훌륭해도 롤백이 불가능해집니다.
롤백을 리허설하라
한 번도 실행해본 적 없는 롤백 스크립트는 장애 상황에서 십중팔구 실패합니다. 정기적으로 스테이징에서 롤백 훈련을 돌려야 합니다. 배포 파이프라인에 원클릭 롤백 잡을 만들어두고, 실제로 눌러보며 소요 시간(MTTR)을 측정하세요.
#!/usr/bin/env bash
# rollback.sh - 원클릭 롤백, CI에서도 로컬에서도 동일하게 동작
set -euo pipefail
DEPLOYMENT=${1:-myapp}
NAMESPACE=${2:-production}
echo "[rollback] 현재 리비전 확인"
kubectl rollout history deployment/${DEPLOYMENT} -n ${NAMESPACE}
echo "[rollback] 직전 리비전으로 되돌림"
kubectl rollout undo deployment/${DEPLOYMENT} -n ${NAMESPACE}
echo "[rollback] 롤아웃 완료 대기"
kubectl rollout status deployment/${DEPLOYMENT} -n ${NAMESPACE} --timeout=120s
echo "[rollback] 완료. 헬스체크 확인:"
kubectl get pods -n ${NAMESPACE} -l app=${DEPLOYMENT}
알림과 사후 기록의 자동화
자동 롤백이 발동했다면 그 사실이 즉시 사람에게 도달해야 합니다. 조용히 되돌아가면 같은 배포를 반복 시도하게 됩니다. 롤백 이벤트를 슬랙 등으로 알리고, 어떤 지표가 임계치를 넘겨서 롤백됐는지를 함께 기록하면 사후 분석이 빨라집니다.
on_rollback:
- notify:
channel: "#deploy-alerts"
message: |
:rotating_light: 자동 롤백 발동
서비스: myapp
원인: error-rate 5.2% (임계치 5%)
롤백 대상: a1b2c3d -> 9f8e7d6
MTTR: 78s
- create_incident:
severity: sev2
auto_assign: on-call
마무리
안전한 롤백은 특별한 도구가 아니라 배포를 설계하는 사고방식에서 나옵니다. 불변 아티팩트, 확장-수축 마이그레이션, 지표 기반 자동 게이트, 그리고 정기 리허설. 이 네 가지가 갖춰지면 배포가 실패해도 서비스는 몇 초 안에 정상으로 돌아옵니다. 롤백은 예외적 상황이 아니라 정상적인 운영 흐름의 일부여야 합니다. 그렇게 만들어둔 팀만이 배포 앞에서 두려움 없이 새 기능을 밀어낼 수 있습니다.