본문 바로가기
WIKI 기술 지식 베이스

무중단 배포

원문 보기 위키 갱신

무중단 배포 (Zero downtime deployments)

트래픽이 많은 프로덕션 환경에서 롤링 업데이트와 다운스케일링의 영향을 최소화하려면 고려해야 할 사항들을 이 문서에서 알려드릴게요. 앱이 중단 없이 안정적으로 업데이트되도록 안정성 핵심 요소들을 하나씩 다뤄 봅시다.

출처: 문서

본문

배포 전략 (Deployment strategy)

롤링 업데이트 중 사용 불가능한 pod 수를 제한합니다:

apiVersion: apps/v1
kind: Deployment
spec:
  progressDeadlineSeconds: 120
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0

deployment의 기본 progress deadline은 10분입니다. 이 값을 조정해 배포 과정이 더 빨리 실패하도록 만들 것을 고려해 보세요.

Liveness 헬스 체크 (Liveness health check)

앱이 복구할 수 없는 손상 상태로 전환되어 재시작이 필요하다는 것을 Kubernetes가 알 수 있도록, 앱은 Kubernetes가 호출할 수 있는 HTTP 엔드포인트를 노출해야 합니다.

livenessProbe:
  exec:
    command:
    - wget
    - --quiet
    - --tries=1
    - --timeout=4
    - --spider
    - http://localhost:8080/healthz
  timeoutSeconds: 5
  initialDelaySeconds: 5

mTLS를 활성화했다면 kubelet은 서비스 메시의 일부가 아니어서 TLS 인증서에 접근할 수 없으므로, liveness와 readiness 검사에 exec를 사용해야 합니다.

Readiness 헬스 체크 (Readiness health check)

앱이 트래픽을 받을 준비가 되었는지 Kubernetes가 알 수 있도록, 앱은 Kubernetes가 호출할 수 있는 HTTP 엔드포인트를 노출해야 합니다.

readinessProbe:
  exec:
    command:
    - wget
    - --quiet
    - --tries=1
    - --timeout=4
    - --spider
    - http://localhost:8080/readyz
  timeoutSeconds: 5
  initialDelaySeconds: 5
  periodSeconds: 5

앱이 외부 서비스에 의존한다면 Kubernetes가 앱 인스턴스에 트래픽을 라우팅하기 전에 해당 서비스들이 사용 가능한지 확인해야 합니다. Envoy 사이드카가 앱보다 느리게 시작될 수 있다는 점을 기억하세요. 즉 앱 시작 시 외부 연결을 최소 몇 초간 재시도해야 합니다.

정상 종료 (Graceful shutdown)

pod가 종료되기 전에 Kubernetes는 모든 컨테이너에 SIGTERM 신호를 보내고 모든 컨테이너가 정상 종료될 때까지 일정 시간(기본 30초) 기다립니다. 앱이 SIGTERM 신호를 처리하지 않거나 유예 기간 안에 종료되지 않으면 Kubernetes가 컨테이너를 강제 종료하며, 앱이 처리 중이던 진행 중(inflight) 요청들은 실패하게 됩니다.

apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - name: app
        lifecycle:
          preStop:
            exec:
              command:
              - sleep
              - "10"

앱 컨테이너에는 컨테이너 종료를 지연시키는 preStop 훅이 있어야 합니다. 그러면 앱이 사용 불가능해지기 전에 서비스 메시가 트래픽을 비우고(drain) 모든 다른 Envoy 사이드카에서 이 pod를 제거할 수 있습니다.

Envoy 종료 지연 (Delay Envoy shutdown)

앱이 SIGTERM에 반응하고 종료 전에 진행 중인 요청을 완료하려고 해도, 응답이 호출자에게 돌아간다는 보장은 없습니다. 앱보다 Envoy 사이드카가 먼저 종료되면 호출자는 503 오류를 받게 됩니다.

이 문제를 완화하려면 Istio 프록시에 preStop 훅을 추가하고 Envoy가 종료되기 전에 기본 앱이 종료되기를 기다리면 됩니다.

#!/bin/bash
set -e
if ! pidof envoy &>/dev/null; then
  exit 0
fi

if ! pidof pilot-agent &>/dev/null; then
  exit 0
fi

while [ $(netstat -plunt | grep tcp | grep -v envoy | wc -l | xargs) -ne 0 ]; do
  sleep 1;
done

exit 0

위 스크립트로 직접 Envoy docker 이미지를 빌드하고 preStop 지시어로 Istio 주입 웹훅을 수정해야 합니다.

503을 최소화하는 데 도움을 준 Stono의 훌륭한 팁에 감사드립니다.

리소스 요청과 한도 (Resource requests and limits)

모든 워크로드에 CPU와 메모리 requests/limits를 설정하는 것은 프로덕션 시스템을 운영한다면 필수 단계입니다. limits가 없으면 노드가 메모리를 모두 소진하거나 CPU 고갈로 응답하지 않게 될 수 있습니다. CPU와 메모리 requests가 없으면 Kubernetes 스케줄러는 pod를 어느 노드에 배치할지 결정할 수 없습니다.

apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: app
        resources:
          limits:
            cpu: 1000m
            memory: 1Gi
          requests:
            cpu: 100m
            memory: 128Mi

리소스 requests가 없으면 horizontal pod autoscaler가 앱을 언제 스케일 업할지 결정할 수 없다는 점을 참고하세요.

오토스케일링 (Autoscaling)

프로덕션 환경은 서비스 품질에 영향을 주지 않으면서 트래픽 버스트를 처리할 수 있어야 합니다. 이는 Kubernetes 오토스케일링 기능으로 달성할 수 있습니다. Kubernetes의 오토스케일링은 두 가지 차원이 있습니다: 노드 스케일링 작업을 다루는 Cluster Autoscaler와 deployment의 pod 수를 자동으로 조정하는 Horizontal Pod Autoscaler입니다.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: app
  minReplicas: 2
  maxReplicas: 4
  metrics:
  - type: Resource
    resource:
      name: cpu
      targetAverageValue: 900m
  - type: Resource
    resource:
      name: memory
      targetAverageValue: 768Mi

위 HPA는 pod가 CPU나 메모리 한도에 도달하기 전에 앱이 스케일 업되도록 보장합니다.

Ingress 재시도 (Ingress retries)

다운스케일링 작업의 영향을 최소화하려면 Envoy 재시도 기능을 활용할 수 있습니다.

apiVersion: flagger.app/v1beta1
kind: Canary
spec:
  service:
    port: 9898
    gateways:
    - istio-system/public-gateway
    hosts:
    - app.example.com
    retries:
      attempts: 10
      perTryTimeout: 5s
      retryOn: "gateway-error,connect-failure,refused-stream"

HPA가 앱을 스케일 다운하면 사용자가 503 오류를 만날 수 있습니다. 위 구성은 게이트웨이 오류로 실패한 HTTP 요청을 Envoy가 재시도하게 만듭니다.

더 알아보기 (Learn more)