Deployment

Deployment (Deployments)

출처: Kubernetes 공식 문서 — Deployments

Deployment는 보통 상태를 유지하지 않는(stateless) 애플리케이션 워크로드를 실행하기 위한 Pod 집합을 관리해요.

Deployment는 Pod와 ReplicaSet에 대해 선언적(declarative) 업데이트를 제공해요.

여러분이 Deployment에 원하는 상태(desired state)를 기술하면, Deployment 컨트롤러가 통제된 속도로 실제 상태를 원하는 상태로 변경해요. Deployment를 정의해서 새 ReplicaSet을 만들 수도 있고, 기존 Deployment를 제거하면서 그 리소스를 모두 새 Deployment로 인계받게 할 수도 있지요.

참고: Deployment가 소유한 ReplicaSet을 직접 관리하지 마세요. 아래에서 다루지 않는 사용 사례가 있다면 Kubernetes 메인 저장소에 이슈를 열어보시는 걸 권장해요.

사용 사례 (Use Case)

Deployment의 대표적인 사용 사례는 다음과 같아요:

  • Deployment를 만들어 ReplicaSet을 롤아웃해요. ReplicaSet이 백그라운드에서 Pod를 생성해요. 롤아웃의 상태를 확인해서 성공했는지 여부를 보세요.
  • Deployment의 PodTemplateSpec을 업데이트해서 Pod의 새 상태를 선언해요. 새 ReplicaSet이 생성되고, Deployment는 새 ReplicaSet을 점진적으로 확장하면서 이전 ReplicaSet을 축소해요. 이렇게 하면 Pod가 통제된 속도로 교체되죠. 새 ReplicaSet마다 Deployment의 리비전(revision)이 갱신돼요.
  • 현재 Deployment 상태가 불안정하다면 이전 Deployment 리비전으로 롤백해요. 롤백할 때마다 Deployment의 리비전이 갱신돼요.
  • 더 많은 부하를 처리하려고 Deployment를 확장해요.
  • Deployment의 롤아웃을 일시 중지해서 PodTemplateSpec에 여러 수정을 적용한 다음, 다시 재개해서 새 롤아웃을 시작해요.
  • Deployment의 상태를 롤아웃이 막힌(stuck) 것의 지표로 사용해요.
  • 더 이상 필요 없는 이전 ReplicaSet을 정리해요.

Deployment 생성 (Creating a Deployment)

다음은 Deployment의 예시예요. 이 Deployment는 세 개의 nginx Pod를 만들기 위한 ReplicaSet을 생성해요:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

이 예시에서:

  • .metadata.name 필드로 표시된 것처럼 nginx-deployment라는 이름의 Deployment가 생성돼요. 이 이름은 나중에 생성되는 ReplicaSet과 Pod 이름의 기반이 돼요. 자세한 내용은 "Deployment 스펙 작성" 섹션을 참고하세요.
  • Deployment는 .spec.replicas 필드로 표시된 것처럼 세 개의 복제된 Pod를 만드는 ReplicaSet을 생성해요.
  • .spec.selector 필드는 생성된 ReplicaSet이 어떤 Pod를 관리할지 찾는 방법을 정의해요. 이 경우 Pod 템플릿에 정의된 라벨(app: nginx)을 선택하지요. 하지만 Pod 템플릿 자체가 규칙을 충족한다면 더 정교한 선택 규칙도 가능해요.

참고: .spec.selector.matchLabels 필드는 {key,value} 쌍의 맵이에요. matchLabels 맵에 있는 단일 {key,value}matchExpressions의 한 요소와 동등한데, key 필드는 "key", 연산자는 "In", values 배열은 "value" 하나만 담는 형태죠. 매칭되려면 matchLabelsmatchExpressions의 모든 요구사항이 충족되어야 해요.

.spec.template 필드에는 다음과 같은 하위 필드가 포함돼요:

  • .metadata.labels 필드를 사용해서 Pod에 app: nginx 라벨을 붙여요.
  • Pod 템플릿의 명세인 .spec 필드는 Pod들이 한 개의 컨테이너(nginx)를 실행한다는 것을 나타내는데, 이 컨테이너는 Docker Hub의 nginx 이미지 버전 1.14.2를 실행해요.
  • .spec.containers[0].name 필드를 사용해서 컨테이너 하나를 만들고 이름을 nginx로 지정해요.

시작하기 전에 Kubernetes 클러스터가 실행 중인지 확인하세요. 위 Deployment를 만들려면 다음 단계를 따르세요:

kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml

kubectl get deployments를 실행해서 Deployment가 생성됐는지 확인해요. Deployment가 아직 생성 중이라면 출력은 다음과 비슷해요:

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   0/3     0            0           1s

클러스터의 Deployment를 검사하면 다음 필드가 표시돼요:

  • NAME은 네임스페이스에 있는 Deployment의 이름을 나열해요.
  • READY는 사용자에게 사용 가능한 애플리케이션의 복제본 수를 표시해요. ready/desired 패턴을 따라요.
  • UP-TO-DATE는 원하는 상태를 달성하도록 업데이트된 복제본 수를 표시해요.
  • AVAILABLE은 사용자에게 사용 가능한 애플리케이션 복제본 수를 표시해요.
  • AGE는 애플리케이션이 실행된 시간을 표시해요.

.spec.replicas 필드에 따라 원하는 복제본 수가 3인 것을 확인할 수 있어요.

롤아웃 상태를 보려면 kubectl rollout status deployment/nginx-deployment를 실행해요. 출력은 다음과 비슷해요:

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment "nginx-deployment" successfully rolled out

몇 초 후에 다시 kubectl get deployments를 실행하면 출력은 다음과 비슷해요:

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           18s

Deployment가 세 개의 복제본을 모두 만들었고, 모든 복제본이 최신 상태(최신 Pod 템플릿 포함)이며 사용 가능하다는 것을 볼 수 있어요.

Deployment가 생성한 ReplicaSet(rs)을 보려면 kubectl get rs를 실행해요. 출력은 다음과 비슷해요:

NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-75675f5897   3         3         3       18s

ReplicaSet 출력은 다음 필드를 보여줘요:

  • NAME은 네임스페이스에 있는 ReplicaSet의 이름을 나열해요.
  • DESIRED는 Deployment를 만들 때 정의한 애플리케이션의 원하는 복제본 수를 표시해요. 이것이 원하는 상태예요.
  • CURRENT는 현재 실행 중인 복제본 수를 표시해요.
  • READY는 사용자에게 사용 가능한 애플리케이션 복제본 수를 표시해요.
  • AGE는 애플리케이션이 실행된 시간을 표시해요.

ReplicaSet의 이름은 항상 [DEPLOYMENT-NAME]-[HASH] 형식이라는 것을 주목하세요. 이 이름은 생성되는 Pod 이름의 기반이 돼요. HASH 문자열은 ReplicaSet의 pod-template-hash 라벨과 같아요.

각 Pod에 자동으로 생성된 라벨을 보려면 kubectl get pods --show-labels를 실행해요:

NAME                                READY     STATUS    RESTARTS   AGE       LABELS
nginx-deployment-75675f5897-7ci7o   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
nginx-deployment-75675f5897-kzszj   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
nginx-deployment-75675f5897-qqcnn   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897

생성된 ReplicaSet은 세 개의 nginx Pod가 있음을 보장해요.

참고: Deployment에서 적절한 selector와 Pod 템플릿 라벨(이 경우 app: nginx)을 지정해야 해요. 다른 컨트롤러(다른 Deployment 및 StatefulSet 포함)와 라벨이나 selector를 겹치게 하지 마세요. Kubernetes는 겹치는 것을 막지 않지만, 여러 컨트롤러가 겹치는 selector를 가지면 서로 충돌하고 예기치 않게 동작할 수 있어요.

pod-template-hash 라벨

주의: 이 라벨을 변경하지 마세요.

pod-template-hash 라벨은 Deployment 컨트롤러가 Deployment가 만들거나 인계받은 모든 ReplicaSet에 추가해요. 이 라벨은 Deployment의 하위 ReplicaSet들이 겹치지 않도록 보장해요. ReplicaSet의 PodTemplate을 해시하고 그 결과 해시를 라벨 값으로 사용해서 ReplicaSet의 selector, Pod 템플릿 라벨, 그리고 ReplicaSet이 가질 수 있는 기존 Pod에 추가해요.

Deployment 업데이트 (Updating a Deployment)

참고: Deployment의 롤아웃은 Deployment의 Pod 템플릿(즉 .spec.template)이 변경될 때에만 트리거돼요. 예를 들어 템플릿의 라벨이나 컨테이너 이미지를 업데이트할 때 그렇지요. 확장 같은 다른 업데이트는 롤아웃을 트리거하지 않아요.

다음 단계를 따라 Deployment를 업데이트해요. nginx Pod가 nginx:1.14.2 이미지 대신 nginx:1.16.1 이미지를 사용하도록 업데이트해 봅시다.

kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1

여기서 deployment/nginx-deployment는 Deployment를, nginx는 업데이트가 일어날 컨테이너를, nginx:1.16.1은 새 이미지와 그 태그를 나타내요. 출력은 다음과 비슷해요:

deployment.apps/nginx-deployment image updated

또는 Deployment를 편집해서 .spec.template.spec.containers[0].imagenginx:1.14.2에서 nginx:1.16.1로 변경할 수도 있어요:

kubectl edit deployment/nginx-deployment

롤아웃 상태를 보려면 다음 명령을 실행해요:

kubectl rollout status deployment/nginx-deployment

출력은 다음과 비슷해요:

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...

또는:

deployment "nginx-deployment" successfully rolled out

롤아웃 성공 후 kubectl get deployments를 실행해서 Deployment를 확인해요. 출력은 다음과 비슷해요:

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           36s

kubectl get rs를 실행하면 Deployment가 새 ReplicaSet을 만들고 이를 3개 복제본으로 확장하며, 이전 ReplicaSet을 0개로 축소해서 Pod를 업데이트한 것을 볼 수 있어요:

NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-1564180365   3         3         3       6s
nginx-deployment-2035384211   0         0         0       36s

kubectl get pods를 실행하면 이제 새 Pod만 표시돼요:

NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-1564180365-khku8   1/1       Running   0          14s
nginx-deployment-1564180365-nacti   1/1       Running   0          14s
nginx-deployment-1564180365-z9gth   1/1       Running   0          14s

다음에 이 Pod들을 업데이트하고 싶다면, Deployment의 Pod 템플릿만 다시 업데이트하면 돼요.

Deployment는 업데이트 중에 일정 수의 Pod만 다운되도록 보장해요. 기본적으로 원하는 Pod 수의 최소 75%가 실행 중인 상태(최대 25% 사용 불가)를 보장해요. 또한 Deployment는 원하는 Pod 수보다 일정 수만큼만 더 생성되도록 보장해요. 기본적으로 원하는 Pod 수의 최대 125%(최대 25% 서지)까지만 실행된 상태를 보장해요.

예를 들어 위 Deployment를 자세히 보면, 먼저 새 Pod를 하나 만들고, 이전 Pod를 하나 삭제하고, 또 다른 새 Pod를 만드는 것을 볼 수 있어요. 충분한 수의 새 Pod가 올라올 때까지 이전 Pod를 죽이지 않고, 충분한 수의 이전 Pod가 죽을 때까지 새 Pod를 만들지 않아요. 최소 3개의 Pod가 사용 가능하고, 최대 4개의 Pod가 사용 가능하도록 보장하지요. 4개 복제본인 Deployment의 경우 Pod 수는 3개에서 5개 사이가 돼요.

kubectl describe deployments를 실행해서 Deployment 세부 정보를 확인해요.

참고: Kubernetes는 availableReplicas(반드시 replicas - maxUnavailablereplicas + maxSurge 사이여야 함)를 계산할 때 종료 중인(terminating) Pod를 세지 않아요. 결과적으로 롤아웃 중에는 예상보다 더 많은 Pod가 있고, 종료되는 Pod의 terminationGracePeriodSeconds가 만료될 때까지 Deployment가 소비하는 총 리소스가 replicas + maxSurge보다 많을 수 있다는 점을 알게 될 수 있어요.

롤오버 (Rollover, 여러 업데이트 진행)

Deployment 컨트롤러가 새 Deployment를 관찰할 때마다 원하는 Pod를 만들기 위해 ReplicaSet이 생성돼요. Deployment가 업데이트되면 라벨이 .spec.selector와 일치하지만 템플릿이 .spec.template과 일치하지 않는 Pod를 제어하는 기존 ReplicaSet이 축소돼요. 결국 새 ReplicaSet은 .spec.replicas로 확장되고 모든 이전 ReplicaSet은 0으로 축소되죠.

기존 롤아웃이 진행되는 동안 Deployment를 업데이트하면, Deployment는 업데이트에 따라 새 ReplicaSet을 만들고 그 ReplicaSet을 확장하기 시작하며, 이전에 확장하고 있던 ReplicaSet을 롤오버해요. 즉 이전 ReplicaSet 목록에 추가하고 축소하기 시작하지요.

예를 들어 nginx:1.14.2의 5개 복제본을 만들도록 Deployment를 만들었는데, nginx:1.14.2의 3개 복제본만 만들어졌을 때 nginx:1.16.1의 5개 복제본을 만들도록 Deployment를 업데이트했다고 가정해 봅시다. 이 경우 Deployment는 즉시 만든 3개의 nginx:1.14.2 Pod를 죽이기 시작하고 nginx:1.16.1 Pod를 만들기 시작해요. nginx:1.14.2의 5개 복제본이 만들어질 때까지 기다리지 않지요.

라벨 선택기 업데이트 (Label selector updates)

일반적으로 라벨 선택기 업데이트는 권장되지 않으며, 선택기를 미리 계획하는 것이 좋아요. Deployment의 라벨 선택기는 생성 후 변경할 수 없어요. kubectl patch, kubectl edit, kubectl apply, helm upgrade 같은 도구로도 업데이트할 수 없지요.

선택기를 반드시 변경해야 한다면 Deployment를 삭제하고 다시 만들어야 해요. 기본적으로 Deployment를 삭제하면 실행 중인 Pod도 함께 삭제되어 다운타임이 발생해요. Deployment를 다시 만드는 동안 Pod가 계속 실행되도록 하려면 --cascade=orphan을 사용하세요(아래 영향 참고). 큰 주의를 기울이고 다음 영향들을 반드시 이해해야 해요:

  • 추가 (Additions): 더 좁은 선택기로 새 Deployment를 만들 때, 새 Deployment는 적절한 Pod 템플릿도 있어야 해요. 기존 매니페스트를 편집해서 선택기를 좁히려면 그 Deployment 안의 Pod 템플릿 메타데이터를 편집해서 일치하는 새 라벨을 추가해야 해요. 그렇지 않으면 API 서버가 검증 오류를 반환하지요. 이는 겹치지 않는(non-overlapping) 변경이라서, 새 Deployment는 이전 Pod(새 라벨이 없음)를 "보지" 못해 이전 ReplicaSet이 고아(orphan)가 되고 완전히 새로운 ReplicaSet이 생성돼요.
  • 값 업데이트 (Value updates): 선택기 키의 기존 값을 변경하는 것(예: v1에서 v2로)은 추가(고아화 및 재생성)와 같은 동작을 만들어요.
  • 제거 (Removals): Deployment 선택기에서 기존 키를 제거하는 것은 Pod 템플릿 라벨 변경이 필요 없어요. 이는 겹치는(overlapping) 변경이라서, 더 넓은 새 선택기가 이전 Pod와 일치해요. 기존 ReplicaSet은 고아가 되지 않고 새 ReplicaSet도 생성되지 않지만, 제거된 라벨이 기존 Pod와 ReplicaSet에는 여전히 존재한다는 점에 주의하세요. Deployment의 롤아웃을 트리거해서 정리할 수 있어요.

Deployment 롤백 (Rolling Back a Deployment)

때로는 Deployment를 롤백하고 싶을 때가 있어요. 예를 들어 Deployment가 불안정할 때(크래시 루프 등) 그렇지요. 기본적으로 모든 Deployment 롤아웃 기록이 시스템에 보관되어서 언제든 롤백할 수 있어요(리비전 기록 제한을 수정해서 변경 가능).

참고: Deployment의 리비전은 Deployment의 롤아웃이 트리거될 때 생성돼요. 즉, Deployment의 Pod 템플릿(.spec.template)이 변경될 때에만 새 리비전이 생성되며, 예를 들어 템플릿의 라벨이나 컨테이너 이미지를 업데이트할 때 그렇지요. 확장 같은 다른 업데이트는 Deployment 리비전을 만들지 않아서 동시에 수동 또는 자동 확장을 용이하게 할 수 있어요. 즉 이전 리비전으로 롤백할 때 Deployment의 Pod 템플릿 부분만 롤백돼요.

이미지 이름을 nginx:1.16.1 대신 nginx:1.161로 넣는 오타를 냈다고 가정해 봅시다:

kubectl set image deployment/nginx-deployment nginx=nginx:1.161

롤아웃이 막힙니다. 롤아웃 상태를 확인해서 검증할 수 있어요:

kubectl rollout status deployment/nginx-deployment

출력은 다음과 비슷해요:

Waiting for rollout to finish: 1 out of 3 new replicas have been updated...

Ctrl-C를 눌러 위 롤아웃 상태 감시를 중지할 수 있어요. 이전 복제본 수(nginx-deployment-1564180365nginx-deployment-2035384211의 복제본 수 합산)가 3이고, 새 복제본 수(nginx-deployment-3066724191)가 1인 것을 볼 수 있어요.

kubectl get pods를 실행하면 새 ReplicaSet이 만든 1개의 Pod가 이미지 풀 루프(ImagePullBackOff)에 빠진 것을 볼 수 있어요:

NAME                                READY     STATUS             RESTARTS   AGE
nginx-deployment-1564180365-70iae   1/1       Running            0          25s
nginx-deployment-1564180365-jbqqo   1/1       Running            0          25s
nginx-deployment-1564180365-hysrc   1/1       Running            0          25s
nginx-deployment-3066724191-08mng   0/1       ImagePullBackOff   0          6s

참고: Deployment 컨트롤러는 잘못된 롤아웃을 자동으로 중지하고 새 ReplicaSet 확장을 멈춰요. 이는 지정한 rollingUpdate 매개변수(특히 maxUnavailable)에 따라 달라요. Kubernetes 기본값은 25%예요.

이를 해결하려면 안정적인 이전 Deployment 리비전으로 롤백해야 해요.

Deployment 롤아웃 기록 확인 (Checking Rollout History of a Deployment)

다음 단계에 따라 롤아웃 기록을 확인해요. 먼저 이 Deployment의 리비전을 확인해요:

kubectl rollout history deployment/nginx-deployment

출력은 다음과 비슷해요:

deployments "nginx-deployment"
REVISION    CHANGE-CAUSE
1           <none>
2           <none>
3           <none>

CHANGE-CAUSE는 생성 시 Deployment 어노테이션 kubernetes.io/change-cause에서 리비전으로 복사돼요. CHANGE-CAUSE 메시지를 지정하는 방법은 다음과 같아요:

  • kubectl annotate deployment/nginx-deployment kubernetes.io/change-cause="image updated to 1.16.1"로 Deployment에 어노테이션을 추가하기.
  • 리소스 매니페스트를 수동으로 편집하기.
  • 어노테이션을 자동으로 설정하는 도구 사용하기.

참고: 이전 Kubernetes 버전에서는 kubectl 명령에 --record 플래그를 사용해서 CHANGE-CAUSE 필드를 자동으로 채울 수 있었어요. 이 플래그는 더 이상 사용되지 않으며(더프리케이트) 향후 릴리스에서 제거될 예정이에요.

각 리비전의 세부 정보를 보려면 다음을 실행해요:

kubectl rollout history deployment/nginx-deployment --revision=2

이전 리비전으로 롤백 (Rolling Back to a Previous Revision)

현재 버전에서 이전 버전(버전 2)으로 롤백하려면:

kubectl rollout undo deployment/nginx-deployment

또는 --to-revision을 지정해서 특정 리비전으로 롤백할 수도 있어요:

kubectl rollout undo deployment/nginx-deployment --to-revision=2

롤백 관련 명령에 대한 자세한 내용은 kubectl rollout을 참고하세요.

이제 Deployment가 이전 안정 리비전으로 롤백됐어요. Deployment 컨트롤러가 리비전 2로 롤백하는 DeploymentRollback 이벤트를 생성한 것을 볼 수 있어요.

롤백이 성공했고 Deployment가 예상대로 실행 중인지 확인하려면:

kubectl get deployment nginx-deployment

Deployment 확장 (Scaling a Deployment)

다음 명령으로 Deployment를 확장할 수 있어요:

kubectl scale deployment/nginx-deployment --replicas=10

클러스터에서 horizontal Pod autoscaling이 활성화되어 있다면, Deployment용 오토스케일러를 설정해서 기존 Pod의 CPU 사용률에 따라 실행하려는 Pod의 최소/최대 수를 선택할 수 있어요:

kubectl autoscale deployment/nginx-deployment --min=10 --max=15 --cpu-percent=80%

비례 확장 (Proportional scaling)

RollingUpdate Deployment는 동시에 애플리케이션의 여러 버전을 실행할 수 있어요. 여러분이나 오토스케일러가 롤아웃(진행 중이거나 일시 중지된) 중인 RollingUpdate Deployment를 확장하면, Deployment 컨트롤러는 기존 활성 ReplicaSet(Pod가 있는 ReplicaSet)에 추가 복제본을 분배해서 위험을 완화해요. 이를 비례 확장(proportional scaling)이라고 해요.

예를 들어 10개 복제본, maxSurge=3, maxUnavailable=2인 Deployment를 실행 중이라고 가정해 봅시다. 새 이미지로 업데이트했는데 그 이미지가 클러스터 안에서 해석할 수 없는 경우, 이미지 업데이트는 새 ReplicaSet으로 새 롤아웃을 시작하지만 위에서 언급한 maxUnavailable 요구사항 때문에 차단돼요. 그런 다음 오토스케일러가 Deployment 복제본을 15로 늘리는 새 요청이 들어와요. Deployment 컨트롤러는 이 5개 복제본을 어디에 추가할지 결정해야 해요.

비례 확장을 사용하지 않으면 5개 모두 새 ReplicaSet에 추가되지만, 비례 확장을 사용하면 추가 복제본을 모든 ReplicaSet에 분산해요. 복제본이 가장 많은 ReplicaSet에 더 큰 비율이 가고, 복제본이 적은 ReplicaSet에는 더 작은 비율이 가요. 남는 복제본은 복제본이 가장 많은 ReplicaSet에 추가돼요. 복제본이 0개인 ReplicaSet은 확장되지 않아요.

위 예시에서는 이전 ReplicaSet에 3개, 새 ReplicaSet에 2개의 복제본이 추가돼요. 새 복제본이 건강해지면 롤아웃 프로세스는 결국 모든 복제본을 새 ReplicaSet으로 옮겨요.

Deployment 롤아웃 일시 중지 및 재개 (Pausing and Resuming a rollout of a Deployment)

Deployment를 업데이트하거나 업데이트할 계획이 있다면, 하나 이상의 업데이트를 트리거하기 전에 그 Deployment의 롤아웃을 일시 중지할 수 있어요. 변경 사항을 적용할 준비가 되면 Deployment의 롤아웃을 재개해요. 이 방식은 불필요한 롤아웃을 트리거하지 않고 일시 중지와 재개 사이에 여러 수정을 적용할 수 있게 해줘요.

예를 들어 다음과 같이 일시 중지합니다:

kubectl rollout pause deployment/nginx-deployment

그런 다음 이미지를 업데이트합니다:

kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1

새 롤아웃이 시작되지 않은 것을 확인할 수 있어요. 일시 중지된 롤아웃 이전의 Deployment 초기 상태는 계속 기능하지만, Deployment 롤아웃이 일시 중지된 동안에는 새 업데이트가 효과가 없어요.

마지막으로 롤아웃을 재개하고 모든 새 업데이트가 포함된 새 ReplicaSet이 올라오는 것을 관찰해요:

kubectl rollout resume deployment/nginx-deployment

kubectl get rs --watch로 롤아웃 상태를 감시해요.

참고: 일시 중지된 Deployment는 재개할 때까지 롤백할 수 없어요.

Deployment 상태 (Deployment status)

Deployment는 수명주기 동안 다양한 상태로 들어가요. 새 ReplicaSet을 롤아웃하는 동안 진행(progressing) 중일 수 있고, 완료(complete)될 수 있으며, 진행에 실패(fail to progress)할 수도 있어요.

진행 중인 Deployment (Progressing Deployment)

Kubernetes는 다음 작업 중 하나가 수행될 때 Deployment를 진행 중으로 표시해요:

  • Deployment가 새 ReplicaSet을 생성한다.
  • Deployment가 최신 ReplicaSet을 확장 중이다.
  • Deployment가 이전 ReplicaSet을 축소 중이다.
  • 새 Pod가 준비되거나 사용 가능해진다(최소 MinReadySeconds 동안 준비 상태).

롤아웃이 "진행 중"이 되면 Deployment 컨트롤러는 Deployment의 .status.conditions에 다음 속성의 조건을 추가해요:

  • type: Progressing, status: "True", reason: NewReplicaSetCreated | FoundNewReplicaSet | ReplicaSetUpdated

kubectl rollout status로 Deployment의 진행 상황을 모니터링할 수 있어요.

완료된 Deployment (Complete Deployment)

Kubernetes는 다음 특성을 가질 때 Deployment를 완료로 표시해요:

  • Deployment와 관련된 모든 복제본이 지정한 최신 버전으로 업데이트됐다(요청한 업데이트가 모두 완료됨).
  • Deployment와 관련된 모든 복제본이 사용 가능하다.
  • Deployment의 이전 복제본이 실행 중이 아니다.

롤아웃이 "완료"되면 Deployment 컨트롤러는 .status.conditionstype: Progressing, status: "True", reason: NewReplicaSetAvailable 조건을 설정해요.

이 Progressing 조건은 새 롤아웃이 시작될 때까지 "True" 상태 값을 유지해요. 복제본 사용 가능 여부가 변해도(이것은 Available 조건에 영향을 미치지만) 조건은 유지돼요.

kubectl rollout status로 Deployment 완료 여부를 확인할 수 있어요. 롤아웃이 성공적으로 완료되면 kubectl rollout status는 종료 코드 0을 반환해요.

실패한 Deployment (Failed Deployment)

Deployment는 새 ReplicaSet 배포를 완료하지 못하고 막힐 수 있어요. 이는 다음과 같은 요인 때문일 수 있어요:

  • 쿼터 부족 (Insufficient quota)
  • 준비성 프로브 실패 (Readiness probe failures)
  • 이미지 풀 오류 (Image pull errors)
  • 권한 부족 (Insufficient permissions)
  • 리밋 레인지 (Limit ranges)
  • 애플리케이션 런타임 잘못된 구성 (Application runtime misconfiguration)

이 상태를 감지하는 방법 중 하나는 Deployment 스펙에 마감 시간 매개변수(.spec.progressDeadlineSeconds)를 지정하는 거예요. .spec.progressDeadlineSeconds는 Deployment 컨트롤러가 Deployment 진행이 지연됐음을(Deployment 상태에서) 나타내기 전에 기다리는 시간(초)을 나타내요.

kubectl patch deployment/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":600}}'

마감 시간이 지나면 Deployment 컨트롤러는 .status.conditionstype: Progressing, status: "False", reason: ProgressDeadlineExceeded 속성을 가진 DeploymentCondition을 추가해요.

참고: Kubernetes는 막힌 Deployment에 대해 reason: ProgressDeadlineExceeded로 상태 조건을 보고하는 것 외에는 아무 조치도 취하지 않아요. 상위 수준 오케스트레이터가 이를 활용해서 예를 들어 Deployment를 이전 버전으로 롤백할 수 있어요.

참고: Deployment 롤아웃을 일시 중지하면 Kubernetes는 지정된 마감 시간에 대해 진행 상황을 확인하지 않아요. 롤아웃 중간에 안전하게 일시 중지하고 마감 시간 초과 조건을 트리거하지 않고 재개할 수 있어요.

kubectl rollout status deployment/nginx-deployment로 Deployment가 진행에 실패했는지 확인할 수 있어요. Deployment가 진행 마감 시간을 초과하면 kubectl rollout status는 0이 아닌 종료 코드를 반환해요.

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
error: deployment "nginx" exceeded its progress deadline

실패한 Deployment에 대한 작업 (Operating on a failed deployment)

완료된 Deployment에 적용되는 모든 작업은 실패한 Deployment에도 적용돼요. 확장/축소하거나, 이전 리비전으로 롤백하거나, Deployment Pod 템플릿에 여러 수정을 적용해야 한다면 일시 중지할 수도 있어요.

정리 정책 (Clean up Policy)

Deployment에 .spec.revisionHistoryLimit 필드를 설정해서 보관하려는 이전 ReplicaSet 수를 지정할 수 있어요. 나머지는 백그라운드에서 가비지 컬렉션돼요. 기본값은 10이에요.

참고: 이 필드를 명시적으로 0으로 설정하면 Deployment의 모든 기록이 정리되어 롤백할 수 없게 돼요.

정리는 Deployment가 완료 상태에 도달한 후에만 시작돼요. .spec.revisionHistoryLimit를 0으로 설정해도 Kubernetes가 이전 것을 제거하기 전에 롤아웃이 여전히 새 ReplicaSet 생성을 트리거해요.

0이 아닌 리비전 기록 제한을 가져도 설정한 제한보다 더 많은 ReplicaSet을 가질 수 있어요. 예를 들어 Pod가 크래시 루프 중이고 시간이 지나며 여러 롤링 업데이트 이벤트가 트리거되면 Deployment가 완료 상태에 도달하지 못해서 .spec.revisionHistoryLimit보다 더 많은 ReplicaSet이 생길 수 있어요.

카나리아 배포 (Canary Deployment)

Deployment를 사용해서 하위 집합의 사용자나 서버에 릴리스를 롤아웃하려면, 관리 리소스에 설명된 카나리아 패턴에 따라 각 릴리스에 대해 하나씩 여러 Deployment를 만들 수 있어요.

Deployment 스펙 작성 (Writing a Deployment Spec)

다른 모든 Kubernetes 구성과 마찬가지로 Deployment에는 .apiVersion, .kind, .metadata 필드가 필요해요. 구성 파일 작업에 대한 일반 정보는 애플리케이션 배포, 컨테이너 구성, kubectl로 리소스 관리 문서를 참고하세요.

컨트롤 플레인이 Deployment용 새 Pod를 만들 때, Deployment의 .metadata.name은 그 Pod 이름을 정하는 기반의 일부가 돼요. Deployment 이름은 유효한 DNS 서브도메인 값이어야 하지만, 이는 Pod 호스트 이름에 예기치 않은 결과를 만들 수 있어요. 최상의 호환성을 위해 이름은 더 엄격한 DNS 라벨 규칙을 따라야 해요.

Deployment에는 .spec 섹션도 필요해요.

Pod 템플릿 (Pod Template)

.spec.template.spec.selector.spec의 유일한 필수 필드예요.

.spec.template은 Pod 템플릿이에요. Pod와 정확히 같은 스키마를 갖지만, 중첩되어 있고 apiVersion이나 kind가 없어요.

Pod에 필요한 필드 외에도 Deployment의 Pod 템플릿은 적절한 라벨과 적절한 재시작 정책을 지정해야 해요. 라벨의 경우 다른 컨트롤러와 겹치지 않도록 주의하세요(selector 참고).

.spec.template.spec.restartPolicyAlways만 허용되며, 지정하지 않으면 기본값이에요.

복제본 (Replicas)

.spec.replicas는 선택적 필드로 원하는 Pod 수를 지정해요. 기본값은 1이에요.

Deployment를 수동으로 확장한 다음(예: kubectl scale deployment deployment --replicas=X) 매니페스트를 기반으로 업데이트하면(예: kubectl apply -f deployment.yaml), 해당 매니페스트 적용이 이전에 했던 수동 확장을 덮어써요.

HorizontalPodAutoscaler(또는 수평 확장을 위한 유사한 API)가 Deployment의 확장을 관리한다면 .spec.replicas를 설정하지 마세요. 대신 Kubernetes 컨트롤 플레인이 .spec.replicas 필드를 자동으로 관리하도록 두세요.

선택기 (Selector)

.spec.selector는 이 Deployment가 대상으로 하는 Pod의 라벨 선택기를 지정하는 필수 필드예요.

.spec.selector.spec.template.metadata.labels와 일치해야 하며, 그렇지 않으면 API에 의해 거부돼요.

apps/v1 API 버전에서 .spec.selector.metadata.labels는 설정하지 않으면 .spec.template.metadata.labels로 기본 설정되지 않아요. 따라서 명시적으로 설정해야 해요. 또한 apps/v1에서 .spec.selector는 Deployment 생성 후 변경할 수 없어요.

Deployment는 선택기와 일치하지만 템플릿이 .spec.template과 다르거나 총 수가 .spec.replicas를 초과하는 Pod를 종료할 수 있어요. Pod 수가 원하는 수보다 적으면 .spec.template으로 새 Pod를 만들지요.

참고: 이 선택기와 라벨이 일치하는 다른 Pod를 직접 만들거나, 다른 Deployment를 만들거나, ReplicaSet이나 ReplicationController 같은 다른 컨트롤러를 만들어서 만들면 안 돼요. 그렇게 하면 첫 번째 Deployment가 이런 다른 Pod를 자신이 만든 것으로 생각해요. Kubernetes는 이것을 막지 않아요.

겹치는 선택기를 가진 여러 컨트롤러가 있으면 컨트롤러가 서로 싸우고 제대로 동작하지 않아요.

전략 (Strategy)

.spec.strategy는 이전 Pod를 새 Pod로 교체하는 데 사용되는 전략을 지정해요. .spec.strategy.type"Recreate""RollingUpdate"일 수 있어요. "RollingUpdate"가 기본값이에요.

Recreate Deployment

.spec.strategy.type==Recreate일 때 모든 기존 Pod는 새 Pod가 생성되기 전에 죽어요.

참고: 이것은 업그레이드에 대해 Pod가 생성되기 전에 종료되는 것만 보장해요. Deployment를 업그레이드하면 이전 리비전의 모든 Pod가 즉시 종료돼요. 새 리비전의 어떤 Pod가 생성되기 전에 성공적인 제거를 기다려요. Pod를 수동으로 삭제하면 수명주기는 ReplicaSet에 의해 제어되며 교체가 즉시 생성돼요(이전 Pod가 여전히 Terminating 상태여도). Pod에 "at most" 보장이 필요하다면 StatefulSet을 사용하는 것을 고려해야 해요.

Rolling Update Deployment

.spec.strategy.type==RollingUpdate일 때 Deployment는 롤링 업데이트 방식으로 Pod를 업데이트해요(이전 ReplicaSet을 점진적으로 축소하고 새 것을 확장). maxUnavailablemaxSurge를 지정해서 롤링 업데이트 프로세스를 제어할 수 있어요.

Max Unavailable.spec.strategy.rollingUpdate.maxUnavailable은 업데이트 프로세스 중 사용 불가할 수 있는 최대 Pod 수를 지정하는 선택 필드예요. 값은 절대 수(예: 5) 또는 원하는 Pod의 백분율(예: 10%)일 수 있어요. 절대 수는 백분율에서 내림하여 계산돼요. .spec.strategy.rollingUpdate.maxSurge가 0이면 이 값은 0일 수 없어요. 기본값은 25%예요.

예를 들어 이 값이 30%로 설정되면 롤링 업데이트가 시작될 때 이전 ReplicaSet을 원하는 Pod의 70%로 즉시 축소할 수 있어요. 새 Pod가 준비되면 이전 ReplicaSet을 더 축소한 다음 새 ReplicaSet을 확장해서, 업데이트 중 모든 순간에 사용 가능한 총 Pod 수가 원하는 Pod의 최소 70%가 되도록 보장해요.

Max Surge.spec.strategy.rollingUpdate.maxSurge는 원하는 Pod 수 이상으로 생성될 수 있는 최대 Pod 수를 지정하는 선택 필드예요. 값은 절대 수(예: 5) 또는 원하는 Pod의 백분율(예: 10%)일 수 있어요. maxUnavailable이 0이면 이 값은 0일 수 없어요. 절대 수는 백분율에서 올림하여 계산돼요. 기본값은 25%예요.

예를 들어 이 값이 30%로 설정되면 롤링 업데이트 시작 시 새 ReplicaSet을 즉시 확장해서 이전/새 Pod의 총 수가 원하는 Pod의 130%를 초과하지 않도록 할 수 있어요. 이전 Pod가 죽으면 새 ReplicaSet을 더 확장해서 업데이트 중 실행되는 총 Pod 수가 원하는 Pod의 최대 130%가 되도록 보장해요.

Progress Deadline Seconds

.spec.progressDeadlineSeconds는 선택 필드로, 시스템이 Deployment가 진행에 실패했다고 보고하기 전에 Deployment 진행을 기다릴 시간(초)을 지정해요. 이는 리소스 상태에서 type: Progressing, status: "False", reason: ProgressDeadlineExceeded 조건으로 표면화돼요. Deployment 컨트롤러는 Deployment를 계속 재시도해요. 기본값은 600이에요.

지정하는 경우 이 필드는 .spec.minReadySeconds보다 커야 해요.

Min Ready Seconds

.spec.minReadySeconds는 선택 필드로, 새로 생성된 Pod가 컨테이너가 하나도 크래시되지 않고 준비 상태로 유지되어야 하는 최소 시간(초)을 지정해요. 그래야 사용 가능한 것으로 간주돼요. 기본값은 0(준비되자마자 사용 가능한 것으로 간주)이에요. Pod가 언제 준비된 것으로 간주되는지에 대해 더 자세히 알려면 컨테이너 프로브를 참고하세요.

종료 중인 Pod (Terminating Pods)

기능 상태: Kubernetes v1.35부터 Beta; 기본 활성화

종료 중인 Pod는 API 서버와 kube-controller-manager에서 DeploymentReplicaSetTerminatingReplicas 기능 게이트가 활성화된 경우에만 볼 수 있어요.

삭제나 축소로 인해 종료가 시작된 Pod는 종료하는 데 오랜 시간이 걸릴 수 있고 그 동안 추가 리소스를 소비할 수 있어요. 결과적으로 모든 Pod의 총 수가 일시적으로 .spec.replicas를 초과할 수 있어요. 종료 중인 Pod는 Deployment의 .status.terminatingReplicas 필드로 추적할 수 있어요.

리비전 기록 제한 (Revision History Limit)

Deployment의 리비전 기록은 그것이 제어하는 ReplicaSet에 저장돼요.

.spec.revisionHistoryLimit은 롤백을 허용하기 위해 보관할 이전 ReplicaSet 수를 지정하는 선택 필드예요. 이러한 이전 ReplicaSet은 etcd에서 리소스를 소비하고 kubectl get rs 출력을 복잡하게 해요. 각 Deployment 리비전의 구성은 그 ReplicaSet에 저장돼요. 따라서 이전 ReplicaSet이 삭제되면 그 Deployment 리비전으로 롤백하는 능력을 잃게 돼요. 기본적으로 10개의 이전 ReplicaSet이 유지되지만, 이상적인 값은 새 Deployment의 빈도와 안정성에 따라 달라요.

더 구체적으로, 이 필드를 0으로 설정하면 복제본이 0개인 모든 이전 ReplicaSet이 정리돼요. 이 경우 새 Deployment 롤아웃을 취소할 수 없는데, 리비전 기록이 정리되기 때문이에요.

일시 중지 (Paused)

.spec.paused는 Deployment를 일시 중지/재개하기 위한 선택적 불리언 필드예요. 일시 중지된 Deployment와 그렇지 않은 것의 유일한 차이는, 일시 중지된 동안에는 Deployment의 PodTemplateSpec에 대한 변경이 새 롤아웃을 트리거하지 않는다는 것이에요. Deployment는 생성될 때 기본적으로 일시 중지되지 않아요.

다음 단계 (What's next)

  • Pod에 대해 더 알아보세요.
  • Deployment를 사용해서 무상태 애플리케이션을 실행하세요.
  • Deployment API를 이해하려면 Deployment를 읽어보세요.
  • PodDisruptionBudget과 그것을 사용해서 중단 중 애플리케이션 가용성을 관리하는 방법을 읽어보세요.
  • kubectl로 Deployment를 생성해 보세요.