Cloud & DevOps |

Zero Downtime Deployment: 다운타임 배포의 공포에서 무중단 배포로의 여정

Rolling Update, Blue-Green, Canary 등 무중단 배포 전략을 실전 경험과 함께 풀어냅니다. Kubernetes 기반 배포 자동화까지.

By SouvenirList
Zero Downtime Deployment: 다운타임 배포의 공포에서 무중단 배포로의 여정

Zero Downtime Deployment: 다운타임 배포의 공포에서 무중단 배포로의 여정

모든 배포에는 이야기가 있습니다. 이 글은 댄 하먼의 8단 스토리 서클(Story Circle) 구조를 따라, 새벽 배포와 점검 공지의 시대에서 출발해 무중단 배포를 완성하고 돌아오기까지의 여정을 기술적으로 풀어냅니다.


TL;DR

  • 다운타임 배포는 서비스 규모가 커지면 더 이상 용납할 수 없는 방식입니다
  • Rolling Update, Blue-Green, Canary는 각각 다른 트레이드오프를 가진 무중단 배포 전략입니다
  • Health Check, Graceful Shutdown, 무중단 DB 마이그레이션은 무중단 배포의 필수 기반 기술입니다
  • 배포 자동화와 모니터링 체계가 갖춰지면, 배포는 두려움이 아닌 일상이 됩니다

1단계: 안정 (You) — 새벽 배포가 일상이던 시절

모든 이야기는 주인공의 일상에서 시작됩니다.

우리 팀의 배포 프로세스도 그랬습니다. 매주 목요일 새벽 2시, 슬랙에 점검 공지를 올리고, 서버를 내리고, 코드를 올리고, 서버를 다시 켜는 것이 루틴이었습니다.

[목요일 새벽 2:00] 점검 공지 게시
[목요일 새벽 2:10] 서버 중지 (서비스 다운)
[목요일 새벽 2:15] 새 코드 배포
[목요일 새벽 2:30] DB 마이그레이션
[목요일 새벽 2:45] 서버 시작 + 수동 테스트
[목요일 새벽 3:00] 점검 종료 공지 (운 좋으면)
[목요일 새벽 4:30] 롤백 후 재배포 (운 나쁘면)

솔직히, 이 방식은 나름대로 작동했습니다. 사용자가 적었고, 대부분 국내 사용자라 새벽 시간대는 트래픽이 거의 없었습니다. 팀원들은 새벽 배포에 익숙해져 있었고, 가끔 발생하는 롤백도 “그럴 수 있지” 하며 넘어갔습니다.

모든 것이 편안한 컴포트 존이었습니다. 불편하지만 익숙한.


2단계: 욕구 (Need) — 다운타임이 더 이상 허용되지 않는 순간

이야기의 주인공에게는 해결해야 할 문제가 생깁니다.

세 가지 변화가 동시에 찾아왔습니다.

변화 1: 배포 빈도의 증가

피처 개발 속도가 빨라지면서 주 1회 배포로는 부족해졌습니다. 하루에 여러 번 배포해야 하는데, 매번 서비스를 내릴 수는 없었습니다.

변화 2: 글로벌 서비스 확장

해외 사용자가 늘면서 “새벽 시간대”가 사라졌습니다. 한국의 새벽 3시는 미국 동부의 오후 1시입니다.

한국      00:00 ████████░░░░░░░░████████  (새벽 = 저트래픽)
미국 동부  10:00 ░░░░░░░░████████████████  (새벽 = 피크 타임!)
유럽      06:00 ░░░░████████████████░░░░  (새벽 = 출근 시간!)

결론: 전 세계에 "안전한 배포 시간"은 존재하지 않는다

변화 3: 비즈니스 임팩트

30분의 다운타임이 만들어내는 비용이 점점 커졌습니다:

월 매출 10억 기준:
- 30분 다운타임 = 약 70만원 매출 손실
- 주 1회 배포 = 월 280만원
- 거기에 사용자 이탈, 신뢰도 하락, CS 비용까지...

시스템은 분명히 말하고 있었습니다. “새벽 배포는 끝났다.”


3단계: 진입 (Go) — 무중단 배포라는 낯선 세계

주인공은 익숙한 세계를 떠나 새로운 영역에 발을 들입니다.

무중단 배포(Zero Downtime Deployment)란, 사용자가 서비스 중단을 체감하지 않으면서 새 버전을 배포하는 방식입니다. 말은 간단하지만 실현하려면 여러 퍼즐 조각이 맞아야 합니다.

무중단 배포의 핵심 전제 조건

┌─────────────────────────────────────────────────────┐
│              무중단 배포 필수 조건                      │
├─────────────────────────────────────────────────────┤
│  1. 로드 밸런서: 트래픽을 유연하게 분배               │
│  2. Health Check: 인스턴스 상태를 실시간 확인         │
│  3. Graceful Shutdown: 진행 중인 요청을 안전하게 처리  │
│  4. 하위 호환 DB 스키마: 신/구 버전이 공존 가능        │
│  5. Stateless 설계: 세션을 인스턴스에 저장하지 않음    │
└─────────────────────────────────────────────────────┘

이 전제 조건 중 하나라도 빠지면 무중단 배포는 “무중단인 척하는 배포”가 됩니다. 우리는 하나씩 파헤치기로 했습니다.


4단계: 탐색 (Search) — Rolling, Blue-Green, Canary 전략 학습

낯선 세계에서 주인공은 시행착오를 겪으며 적응합니다.

전략 1: Rolling Update

가장 기본적인 무중단 배포 전략입니다. 인스턴스를 하나씩 순차적으로 교체합니다.

[시작]  v1 v1 v1 v1    (4개 인스턴스 모두 구 버전)
[1단계] v2 v1 v1 v1    (1개 교체)
[2단계] v2 v2 v1 v1    (2개 교체)
[3단계] v2 v2 v2 v1    (3개 교체)
[완료]  v2 v2 v2 v2    (전체 교체 완료)

Kubernetes에서는 Deployment 리소스가 이를 기본으로 지원합니다:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # 최대 1개 추가 Pod 허용
      maxUnavailable: 0   # 동시에 사용 불가 Pod 0개 (핵심!)
  template:
    spec:
      containers:
      - name: api
        image: myapp:2.0.0
        readinessProbe:
          httpGet:
            path: /health/ready
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
        livenessProbe:
          httpGet:
            path: /health/live
            port: 8080
          initialDelaySeconds: 15
          periodSeconds: 10

maxUnavailable: 0이 핵심입니다. 새 Pod가 Ready 상태가 되기 전까지 기존 Pod를 절대 제거하지 않습니다.

전략 2: Blue-Green Deployment

두 개의 동일한 환경(Blue/Green)을 유지하고, 트래픽을 한 번에 전환합니다.

[배포 전]
  로드밸런서 ──→ Blue (v1) ✅ 라이브
                 Green (비활성)

[배포 중]
  로드밸런서 ──→ Blue (v1) ✅ 라이브
                 Green (v2) 🔄 배포 + 테스트 중

[전환]
  로드밸런서 ──→ Green (v2) ✅ 라이브
                 Blue (v1) 대기 (즉시 롤백 가능)

Kubernetes Service를 활용한 Blue-Green 구현:

# blue-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server-blue
  labels:
    app: api-server
    version: blue
spec:
  replicas: 4
  selector:
    matchLabels:
      app: api-server
      version: blue
  template:
    metadata:
      labels:
        app: api-server
        version: blue
    spec:
      containers:
      - name: api
        image: myapp:1.0.0
---
# green-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server-green
  labels:
    app: api-server
    version: green
spec:
  replicas: 4
  selector:
    matchLabels:
      app: api-server
      version: green
  template:
    metadata:
      labels:
        app: api-server
        version: green
    spec:
      containers:
      - name: api
        image: myapp:2.0.0
---
# 트래픽 전환: selector만 변경하면 즉시 전환
apiVersion: v1
kind: Service
metadata:
  name: api-server
spec:
  selector:
    app: api-server
    version: green   # blue → green으로 변경하면 전환 완료
  ports:
  - port: 80
    targetPort: 8080

전환 스크립트도 간단합니다:

#!/bin/bash
# blue-green-switch.sh

CURRENT=$(kubectl get svc api-server -o jsonpath='{.spec.selector.version}')
if [ "$CURRENT" = "blue" ]; then
  TARGET="green"
else
  TARGET="blue"
fi

echo "Switching traffic: $CURRENT$TARGET"
kubectl patch svc api-server -p "{\"spec\":{\"selector\":{\"version\":\"$TARGET\"}}}"

# 전환 후 헬스 체크
sleep 5
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" https://api.example.com/health)
if [ "$HTTP_CODE" != "200" ]; then
  echo "Health check failed! Rolling back..."
  kubectl patch svc api-server -p "{\"spec\":{\"selector\":{\"version\":\"$CURRENT\"}}}"
  exit 1
fi
echo "Switch complete. $TARGET is now live."

전략 3: Canary Deployment

새 버전을 소수 트래픽에만 먼저 노출하고, 문제가 없으면 점진적으로 확대합니다.

[1단계]  v1: 95% ─────────────────────  v2: 5%  ───
[2단계]  v1: 80% ────────────────       v2: 20% ────
[3단계]  v1: 50% ──────────             v2: 50% ────────
[완료]   v1: 0%                         v2: 100% ─────────────────

Nginx Ingress를 활용한 Canary 트래픽 분배:

# stable-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-server-stable
spec:
  ingressClassName: nginx
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-server-stable
            port:
              number: 80
---
# canary-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-server-canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"  # 10% 트래픽
spec:
  ingressClassName: nginx
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-server-canary
            port:
              number: 80

전략 비교

항목Rolling UpdateBlue-GreenCanary
롤백 속도느림 (재배포)즉시 (트래픽 전환)즉시 (트래픽 차단)
리소스 비용낮음높음 (2배 환경)중간
위험도중간낮음매우 낮음
구현 복잡도낮음중간높음
신/구 버전 공존O (일시적)XO (의도적)
추천 시나리오일반적인 배포크리티컬 서비스대규모 트래픽 서비스

5단계: 발견 (Find) — Health Check, Graceful Shutdown, DB Migration 패턴

시행착오 끝에 주인공은 목표에 도달합니다.

전략을 골랐다고 끝이 아닙니다. 무중단 배포를 실제로 작동시키려면 세 가지 기반 기술이 반드시 필요합니다.

발견 1: Health Check 구현

Kubernetes는 두 가지 헬스 체크로 Pod 상태를 판단합니다:

# Python (FastAPI) Health Check 구현
from fastapi import FastAPI, Response
import asyncpg

app = FastAPI()
db_pool = None

@app.get("/health/live")
async def liveness():
    """Pod가 살아있는가? 실패하면 Pod를 재시작"""
    return {"status": "alive"}

@app.get("/health/ready")
async def readiness():
    """트래픽을 받을 준비가 되었는가? 실패하면 트래픽 차단"""
    try:
        async with db_pool.acquire() as conn:
            await conn.fetchval("SELECT 1")
        return {"status": "ready", "db": "connected"}
    except Exception as e:
        return Response(
            content=f'{{"status": "not_ready", "reason": "{str(e)}"}}',
            status_code=503
        )

발견 2: Graceful Shutdown

배포 중 기존 Pod가 종료될 때, 진행 중인 요청을 안전하게 마무리해야 합니다. 이게 없으면 사용자는 502 에러를 만나게 됩니다.

// Node.js Graceful Shutdown
const express = require('express');
const app = express();

let isShuttingDown = false;
let activeConnections = new Set();

const server = app.listen(8080, () => {
  console.log('Server running on port 8080');
});

// 새 연결 추적
server.on('connection', (conn) => {
  activeConnections.add(conn);
  conn.on('close', () => activeConnections.delete(conn));
});

// 종료 시그널 핸들링 (Kubernetes는 SIGTERM을 보냄)
process.on('SIGTERM', () => {
  console.log('SIGTERM received. Starting graceful shutdown...');
  isShuttingDown = true;

  // 새 요청 거부 (readiness probe 실패 유도)
  app.use((req, res, next) => {
    res.status(503).json({ error: 'Server is shutting down' });
  });

  // 기존 연결이 모두 끝날 때까지 대기
  server.close(() => {
    console.log('All connections closed. Exiting.');
    process.exit(0);
  });

  // 안전장치: 30초 후 강제 종료
  setTimeout(() => {
    console.error('Forced shutdown after timeout');
    process.exit(1);
  }, 30000);
});
# Python (uvicorn + FastAPI) Graceful Shutdown
import signal
import asyncio
from contextlib import asynccontextmanager

shutdown_event = asyncio.Event()

@asynccontextmanager
async def lifespan(app):
    # Startup
    yield
    # Shutdown: 진행 중인 작업 완료 대기
    print("Graceful shutdown initiated...")
    await asyncio.sleep(5)  # 진행 중인 요청 처리 시간 확보
    print("Shutdown complete.")

app = FastAPI(lifespan=lifespan)

Kubernetes에서는 terminationGracePeriodSecondspreStop hook을 함께 설정합니다:

spec:
  terminationGracePeriodSeconds: 60
  containers:
  - name: api
    lifecycle:
      preStop:
        exec:
          command: ["/bin/sh", "-c", "sleep 10"]
          # 10초 대기: 로드밸런서가 이 Pod로 트래픽 보내는 걸 중단할 시간 확보

발견 3: 무중단 DB 마이그레이션

이것이 가장 까다로운 부분입니다. 신/구 버전 코드가 동시에 같은 DB를 사용하기 때문에 스키마 변경이 양쪽 모두와 호환되어야 합니다.

원칙: Expand and Contract 패턴

[Phase 1: Expand] 새 컬럼 추가 (기존 코드에 영향 없음)
[Phase 2: Migrate] 새 코드 배포 + 데이터 이전
[Phase 3: Contract] 구 컬럼 제거 (구 코드가 더 이상 없을 때)

구체적인 예시 - 컬럼명 변경 (usernamedisplay_name):

-- [WRONG] 이렇게 하면 구 버전 코드가 즉시 깨짐
ALTER TABLE users RENAME COLUMN username TO display_name;

-- [RIGHT] 3단계에 걸쳐 안전하게 변경

-- Phase 1: 새 컬럼 추가 (v1 코드 영향 없음)
ALTER TABLE users ADD COLUMN display_name VARCHAR(255);

-- Phase 1.5: 기존 데이터 복사 (백그라운드 배치)
UPDATE users SET display_name = username WHERE display_name IS NULL;

-- Phase 2: 새 코드 배포 (display_name 사용, username에도 동시 쓰기)
-- 이 시점에 v1과 v2가 공존할 수 있음

-- Phase 3: 구 코드가 완전히 사라진 후, 구 컬럼 제거
ALTER TABLE users DROP COLUMN username;

이 세 단계를 최소 2~3번의 별도 배포에 걸쳐 실행해야 합니다. 한 번의 배포에서 모든 것을 하려는 유혹을 이겨내야 합니다.


6단계: 대가 (Take) — 롤백, Feature Flag, 모니터링 체계 구축

원하는 것을 얻었지만, 주인공은 무거운 대가를 치릅니다.

무중단 배포를 안정적으로 운영하려면 단순한 배포 전략 이상이 필요했습니다.

대가 1: 롤백 전략

배포가 잘못됐을 때 빠르게 원복할 수 있어야 합니다:

# Kubernetes 롤백 (Rolling Update)
kubectl rollout undo deployment/api-server

# 특정 리비전으로 롤백
kubectl rollout history deployment/api-server
kubectl rollout undo deployment/api-server --to-revision=3

# 배포 상태 실시간 모니터링
kubectl rollout status deployment/api-server --timeout=300s

대가 2: Feature Flag

새 기능을 코드 배포와 기능 활성화를 분리해야 합니다. 코드는 배포됐지만 기능은 꺼둔 상태에서 점진적으로 활성화합니다:

# Feature Flag 기반 점진적 릴리스
class FeatureFlags:
    def __init__(self, config_store):
        self.store = config_store

    def is_enabled(self, flag_name, user_id=None):
        flag = self.store.get(flag_name)
        if not flag or not flag["enabled"]:
            return False
        # 특정 사용자 그룹에만 활성화
        if flag.get("whitelist") and user_id in flag["whitelist"]:
            return True
        # 점진적 롤아웃 (퍼센트 기반)
        if flag.get("rollout_percentage"):
            return hash(f"{flag_name}:{user_id}") % 100 < flag["rollout_percentage"]
        return flag["enabled"]

# 사용 예시
flags = FeatureFlags(config_store)

@app.get("/checkout")
async def checkout(user_id: str):
    if flags.is_enabled("new_checkout_flow", user_id):
        return new_checkout(user_id)   # 새 로직
    return legacy_checkout(user_id)     # 기존 로직

대가 3: 배포 모니터링 체계

배포 직후 이상 징후를 자동으로 감지할 수 있어야 합니다:

# 배포 후 자동 모니터링 항목
deployment_monitoring:
  immediate:  # 배포 직후 5분
    - error_rate: "< 1%"
    - p99_latency: "< 500ms"
    - health_check: "all_pods_ready"

  short_term:  # 배포 후 1시간
    - error_rate_trend: "not_increasing"
    - memory_usage: "< 80%"
    - cpu_usage: "< 70%"

  auto_rollback_triggers:
    - error_rate: "> 5% for 2 minutes"
    - pod_restart_count: "> 3 in 5 minutes"
    - health_check_failures: "> 2 consecutive"

이 모든 것을 세팅하는 데 한 달이 걸렸습니다. 하지만 이 대가를 치르지 않으면 무중단 배포는 “때때로 작동하는 무중단 배포”에 불과합니다.


7단계: 귀환 (Return) — 완전 자동화된 배포 파이프라인

대가를 치른 주인공은 원래 세계로 돌아옵니다.

모든 조각이 맞춰지자, 배포 파이프라인이 완성되었습니다:

┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
│  Git Push │───→│  CI/CD   │───→│  Canary  │───→│  Full    │
│  (main)  │    │  Build   │    │  Deploy  │    │  Rollout │
└──────────┘    │  + Test  │    │  (10%)   │    │  (100%)  │
                └──────────┘    └────┬─────┘    └──────────┘

                                ┌────▼─────┐
                                │ Auto     │
                                │ Monitor  │
                                │ (5 min)  │
                                └────┬─────┘

                              Pass? ──┴── Fail?
                                │           │
                           Continue     Auto Rollback
#!/bin/bash
# deploy.sh - 완전 자동화 배포 스크립트

set -e

IMAGE_TAG=$1
CANARY_WEIGHT=10
MONITOR_DURATION=300  # 5분

echo "=== Phase 1: Canary Deploy (${CANARY_WEIGHT}% traffic) ==="
kubectl set image deployment/api-canary api=myapp:${IMAGE_TAG}
kubectl rollout status deployment/api-canary --timeout=120s

# Canary Ingress 활성화
kubectl annotate ingress api-canary \
  nginx.ingress.kubernetes.io/canary-weight="${CANARY_WEIGHT}" --overwrite

echo "=== Phase 2: Monitoring for ${MONITOR_DURATION}s ==="
END_TIME=$(($(date +%s) + MONITOR_DURATION))
while [ $(date +%s) -lt $END_TIME ]; do
  ERROR_RATE=$(curl -s "http://prometheus:9090/api/v1/query?query=rate(http_errors_total[1m])" \
    | jq -r '.data.result[0].value[1]')

  if (( $(echo "$ERROR_RATE > 0.05" | bc -l) )); then
    echo "ERROR: Error rate ${ERROR_RATE} exceeds threshold!"
    echo "=== AUTO ROLLBACK ==="
    kubectl rollout undo deployment/api-canary
    kubectl annotate ingress api-canary \
      nginx.ingress.kubernetes.io/canary-weight="0" --overwrite
    exit 1
  fi
  sleep 30
done

echo "=== Phase 3: Full Rollout ==="
kubectl set image deployment/api-stable api=myapp:${IMAGE_TAG}
kubectl rollout status deployment/api-stable --timeout=300s

# Canary 비활성화
kubectl annotate ingress api-canary \
  nginx.ingress.kubernetes.io/canary-weight="0" --overwrite

echo "=== Deployment Complete ==="

이제 git push만 하면 코드가 자동으로 빌드되고, 테스트되고, 카나리 배포되고, 모니터링 후 전체 롤아웃됩니다. 새벽 2시에 일어날 필요가 없어졌습니다.


8단계: 변화 (Change) — 배포에 대한 근본적 인식 변화

여정을 마친 주인공은 근본적으로 변화합니다.

무중단 배포를 경험한 후, 배포를 바라보는 관점 자체가 바뀌었습니다.

Before vs After: 사고방식의 변화

[Before] "배포는 위험한 이벤트다"
[After]  "배포는 매일 수십 번 일어나는 일상이다"

[Before] "배포 전에 모든 기능을 한 번에 모아서"
[After]  "작은 변경을 자주, Feature Flag로 점진적으로"

[Before] "배포 실패 = 새벽 긴급 대응"
[After]  "배포 실패 = 자동 롤백, 다음 커밋에서 수정"

[Before] "DB 마이그레이션은 점검 시간에"
[After]  "Expand-Contract 패턴으로 무중단 마이그레이션"

Pros & Cons: 무중단 배포 전환의 현실

ProsCons
서비스 가용성 99.99% 달성초기 인프라 구축 비용
배포 빈도 무제한 증가학습 곡선 (팀 전체가 이해해야 함)
빠른 롤백으로 장애 영향 최소화DB 마이그레이션 복잡도 증가
개발자 삶의 질 향상 (새벽 배포 안녕)모니터링/관측 체계 필수
비즈니스 민첩성 확보신/구 버전 호환성 항상 고려

FAQ

Q: 무중단 배포를 위해 반드시 Kubernetes가 필요한가요?

아닙니다. Kubernetes는 무중단 배포를 편리하게 만들어주는 도구일 뿐, 필수는 아닙니다. AWS ECS, Docker Swarm, 심지어 Nginx와 스크립트만으로도 Blue-Green 배포를 구현할 수 있습니다. 핵심은 로드 밸런서 + Health Check + Graceful Shutdown 조합입니다.

Q: Rolling Update, Blue-Green, Canary 중 어떤 전략을 먼저 도입해야 하나요?

Rolling Update부터 시작하세요. 구현이 가장 단순하고 Kubernetes가 기본으로 지원합니다. 서비스가 성숙해지고 트래픽이 늘면 Canary를 추가하고, 매우 크리티컬한 서비스에는 Blue-Green을 고려하세요.

Q: DB 마이그레이션 중 신/구 버전 코드가 동시에 돌아가면 데이터 정합성은 어떻게 보장하나요?

Expand-Contract 패턴의 핵심은 “두 버전이 공존하는 과도기”를 설계하는 것입니다. 새 컬럼 추가 시 기본값을 설정하고, 코드에서 양쪽 컬럼에 동시에 쓰는 (dual-write) 방식을 사용합니다. 과도기가 끝나면 구 컬럼을 제거합니다.

Q: Feature Flag이 너무 많아지면 관리가 어렵지 않나요?

맞습니다. Feature Flag에는 반드시 만료일을 설정하세요. 완전히 롤아웃된 기능의 플래그는 코드에서 제거하는 것이 원칙입니다. LaunchDarkly, Unleash 같은 Feature Flag 전용 도구를 사용하면 생명주기 관리가 훨씬 수월합니다.

Q: Canary 배포에서 “충분히 안전하다”고 판단하는 기준은 뭔가요?

정량적 기준을 미리 정의해야 합니다. 일반적으로 에러율 < 1%, p99 레이턴시 변화 < 10%, Pod 재시작 0회를 5분간 유지하면 통과로 판단합니다. 이 기준은 서비스 특성에 따라 조정하되, 반드시 자동화해야 합니다. 사람이 매번 판단하면 결국 “대충 괜찮아 보이네”가 됩니다.


마치며

댄 하먼의 스토리 서클은 “변화는 대가를 수반한다” 는 것을 가르쳐줍니다.

무중단 배포도 마찬가지입니다. 새벽 배포라는 익숙한 루틴을 떠나, 인프라 구축과 학습이라는 대가를 치르고, 배포를 일상으로 만드는 자유를 얻어 돌아옵니다. 그리고 그 여정을 통해 시스템과 팀 모두가 성장합니다.

Bottom Line: 무중단 배포는 선택이 아닌 필수입니다. 하지만 하루아침에 완성되는 것이 아닙니다. Rolling Update로 시작해서, Health Check과 Graceful Shutdown을 갖추고, 모니터링을 자동화하는 순서로 점진적으로 도입하세요. 가장 중요한 것은 “배포가 무섭지 않은 문화”를 만드는 것입니다.


함께 읽으면 좋은 글

Tags: zero-downtime deployment blue-green canary kubernetes devops rolling-update

Related Articles