사전 디플로이먼트
인터페이스

디플로이먼트

gabury1

디플로이먼트는 쿠버네티스에 애플리케이션을 이렇게 띄워 두라고 적어 내는 리소스입니다. 몇 벌을 띄울지와 어떤 컨테이너 이미지를 쓸지를 적습니다. 옛 파드를 새 파드로 갈아 끼우는 일도 이 리소스가 맡습니다.

상세

디플로이먼트는 파드 한 묶음을 관리해서 애플리케이션 워크로드를 돌립니다. 쿠버네티스 문서는 그 워크로드가 대개 상태를 유지하지 않는 쪽이라고 적습니다. 그리고 디플로이먼트가 파드와 레플리카셋에 대한 선언형 갱신을 제공한다고 정의합니다.

선언형이라는 말은 절차를 적지 않는다는 뜻입니다. 사용자는 디플로이먼트에 원하는 상태를 적어 둡니다. 그러면 디플로이먼트 컨트롤러가 실제 상태를 그 원하는 상태로 바꿉니다. 다만 한꺼번에 바꾸지 않고 정해진 속도로 바꿉니다. 그 속도를 정하는 필드는 아래 형태 절이 받습니다.

쥐는 대상은 파드지만 직접 쥐지는 않습니다. 사용자는 디플로이먼트를 정의해서 새 레플리카셋을 만들게 할 수 있습니다. 기존 디플로이먼트를 지우고 그 리소스를 전부 새 디플로이먼트가 넘겨받게 할 수도 있습니다. 레플리카셋은 디플로이먼트가 소유합니다. 그 레플리카셋이 파드를 제어합니다.

그래서 쿠버네티스 문서는 디플로이먼트가 소유한 레플리카셋을 사람이 직접 관리하지 말라고 못 박습니다.

형태

다른 쿠버네티스 설정과 마찬가지로 디플로이먼트에는 .apiVersion · .kind · .metadata 필드가 필요합니다. 여기에 .spec 절이 더 필요합니다. .spec 안에서 반드시 있어야 하는 필드는 .spec.template 과 .spec.selector 둘뿐입니다.

필드 무엇을 정하나 기본값
.spec.template 만들어질 파드를 기술합니다. template.spec.restartPolicy 에 허용되는 값은 Always 하나입니다 필수
.spec.selector 이 디플로이먼트가 겨냥하는 파드의 레이블 셀렉터입니다. 이 셀렉터가 고른 파드를 쥔 기존 레플리카셋이 이 디플로이먼트의 영향을 받습니다 필수
.spec.replicas 원하는 파드 개수입니다 1
.spec.strategy 기존 파드를 새 파드로 교체할 때 쓸 전략입니다. 기본값이 정해진 것은 아래 소절이 받는 .spec.strategy.type 입니다 —
.spec.minReadySeconds 새로 만든 파드가 컨테이너 크래시 없이 준비 상태로 있어야 가용으로 쳐 주는 최소 초입니다 0. 준비되는 즉시 가용으로 칩니다
.spec.revisionHistoryLimit 롤백에 쓰려고 남겨 두는 옛 레플리카셋 개수입니다 10
.spec.progressDeadlineSeconds 진행에 실패한 것으로 볼 때까지 기다리는 최대 초입니다. 디플로이먼트가 멈춰 있는(paused) 동안에는 진행을 추정하지 않습니다 600s
.spec.paused 디플로이먼트가 멈춰 있음을 나타냅니다 —

HorizontalPodAutoscaler 나 그와 비슷한 수평 스케일링 API(Application Programming Interface, 응용 프로그램 인터페이스)가 이 디플로이먼트의 스케일링을 관리하고 있다면 .spec.replicas 를 설정하지 않습니다. 쿠버네티스 컨트롤 플레인이 그 필드를 자동으로 관리하게 둡니다.

셀렉터와 파드 템플릿의 짝

.spec.selector 는 .spec.template.metadata.labels 와 맞아야 합니다. 맞지 않으면 API 가 거부합니다. apps/v1 에서는 .spec.selector 와 .metadata.labels 가 .spec.template.metadata.labels 값으로 기본 설정되지 않습니다. 그래서 둘 다 명시해야 합니다.

apps/v1 에서 .spec.selector 는 디플로이먼트를 만든 뒤에는 불변입니다. 쿠버네티스 문서는 레이블 셀렉터를 갱신하는 일이 일반적으로 권장되지 않으며 셀렉터를 미리 계획하라고 적습니다. 셀렉터는 kubectl patch · kubectl edit · kubectl apply 로도, helm upgrade 같은 도구로도 갱신할 수 없습니다. 반드시 바꿔야 하면 디플로이먼트를 지우고 다시 만들어야 합니다.

교체 전략과 그 폭

.spec.strategy.type 은 Recreate 와 RollingUpdate 두 값을 가질 수 있습니다. 기본값은 RollingUpdate 입니다. Recreate 는 새 파드를 만들기 전에 기존 파드를 전부 죽입니다. RollingUpdate 는 옛 레플리카셋을 점진적으로 스케일 다운하고 새 레플리카셋을 스케일 업하는 방식으로 교체합니다.

RollingUpdate 를 고르면 교체 폭을 두 필드로 정합니다.

필드 무엇을 정하나 기본값
.spec.strategy.rollingUpdate.maxSurge 원하는 파드 수보다 위로 더 스케줄될 수 있는 최대 파드 수입니다. 절대 수나 원하는 파드 수의 백분율로 적습니다. 백분율에서 절대 수를 구할 때는 올림합니다 25%
.spec.strategy.rollingUpdate.maxUnavailable 갱신 중에 가용하지 않아도 되는 최대 파드 수입니다. 절대 수나 원하는 파드 수의 백분율로 적습니다. 백분율에서 절대 수를 구할 때는 내림합니다 25%

두 필드는 서로 묶여 있습니다. maxUnavailable 이 0 이면 maxSurge 는 0 일 수 없고, maxSurge 가 0 이면 maxUnavailable 도 0 일 수 없습니다.

동작

디플로이먼트를 고쳤다고 언제나 교체가 도는 것은 아닙니다. 쿠버네티스 문서는 롤아웃이 디플로이먼트의 파드 템플릿, 그러니까 .spec.template 이 바뀔 때에만 걸린다고 적습니다. 템플릿의 레이블이나 컨테이너 이미지가 갱신되는 경우가 그렇습니다. 스케일 조정 같은 다른 갱신은 롤아웃을 걸지 않습니다.

롤아웃이 걸리면 안에서 도는 순서는 이렇습니다.

flowchart TD
    A["파드 템플릿 변경"] --> B["새 레플리카셋 생성"]
    B --> C["옛 레플리카셋 스케일 다운 · 새 레플리카셋 스케일 업"]
    C --> D["새 레플리카셋은 원하는 파드 수, 옛 레플리카셋은 0"]

디플로이먼트 컨트롤러는 새 디플로이먼트를 관찰할 때마다 원하는 파드를 띄우려고 레플리카셋을 하나 만듭니다. 디플로이먼트가 갱신되면, .spec.selector 에는 레이블이 맞지만 .spec.template 에는 맞지 않는 파드를 쥔 기존 레플리카셋이 스케일 다운됩니다. 결국 새 레플리카셋이 .spec.replicas 까지 올라가고 옛 레플리카셋은 전부 0 으로 내려갑니다.

교체가 도는 중에는 두 레플리카셋이 함께 보입니다.

터미널
$ kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-1564180365   3         3         3       6s
nginx-deployment-2035384211   0         0         0       36s

디플로이먼트는 갱신하는 동안 일정 수의 파드만 내려가게 합니다. 기본값에서는 원하는 파드 수의 최소 75% 가 떠 있도록 보장합니다. 최대 25% 가 가용하지 않을 수 있다는 뜻입니다. 위쪽도 마찬가지로 일정 수만 더 만듭니다. 기본값에서는 원하는 파드 수의 최대 125% 까지만 떠 있게 합니다.

롤아웃 중에 또 갱신하는 경우

롤아웃이 도는 중에 디플로이먼트를 또 갱신하면, 디플로이먼트는 갱신 내용대로 새 레플리카셋을 만들어 스케일 업하기 시작합니다. 그리고 직전까지 스케일 업하던 레플리카셋을 옛 레플리카셋 목록에 넣고 스케일 다운하기 시작합니다.

쿠버네티스 문서가 드는 예는 이렇습니다. nginx:1.14.2 를 5 벌 만드는 디플로이먼트를 만듭니다. 그런데 nginx:1.14.2 파드가 3 벌만 만들어진 시점에 nginx:1.16.1 을 5 벌 만들도록 갱신합니다. 그러면 디플로이먼트는 이미 만든 nginx:1.14.2 파드 3 벌을 즉시 죽이기 시작하고 nginx:1.16.1 파드를 만들기 시작합니다. nginx:1.14.2 5 벌이 다 만들어지기를 기다리지 않고 방향을 바꿉니다.

실패

디플로이먼트는 수명 동안 여러 상태를 지납니다. 새 레플리카셋을 롤아웃하는 동안에는 진행 중일 수 있고, 완료일 수도 있고, 진행에 실패할 수도 있습니다.

쿠버네티스는 다음 중 하나가 일어나면 디플로이먼트를 진행 중으로 표시합니다. 디플로이먼트가 새 레플리카셋을 만들 때, 가장 새 레플리카셋을 스케일 업할 때, 더 오래된 레플리카셋을 스케일 다운할 때, 새 파드가 준비되거나 가용해질 때입니다. 여기서 가용은 minReadySeconds 동안 준비 상태였다는 뜻입니다.

완료로 표시되는 조건은 셋입니다. 디플로이먼트에 딸린 레플리카가 전부 지정한 최신 버전으로 갱신됐고, 전부 가용하고, 옛 레플리카가 하나도 돌고 있지 않을 때입니다.

롤아웃이 끝나지 않는 경우

디플로이먼트가 가장 새 레플리카셋을 배포하려다 끝내 완료하지 못하고 멈춰 있을 수 있습니다. 쿠버네티스 문서는 그런 일이 다음 요인 가운데 몇 가지 때문에 일어날 수 있다고 적습니다.

  • 할당량 부족
  • readiness probe 실패
  • 이미지 pull 오류
  • 권한 부족
  • 리밋 레인지
  • 애플리케이션 런타임 설정 오류

이 상태를 알아채는 한 가지 방법은 디플로이먼트 스펙에 데드라인 파라미터를 지정하는 것입니다. .spec.progressDeadlineSeconds 는 디플로이먼트 컨트롤러가 진행이 멈췄다고 상태에 표시하기 전까지 기다리는 초를 뜻합니다. 데드라인을 넘기면 컨트롤러가 디플로이먼트의 .status.conditions 에 컨디션을 하나 더합니다.

속성 값
type Progressing
status "False"
reason ProgressDeadlineExceeded

멈춘 디플로이먼트에 대해 쿠버네티스가 하는 일은 reason: ProgressDeadlineExceeded 로 상태 컨디션을 보고하는 것뿐입니다. 그 밖에는 아무 조치도 취하지 않습니다. 상위 오케스트레이터가 그 컨디션을 활용해 대응할 수 있습니다. 디플로이먼트를 이전 버전으로 롤백하는 것이 그 예입니다.

호출에서 보이는 것

완료 여부와 실패 여부는 kubectl rollout status 로 확인합니다. 이 명령은 기본적으로 가장 최근 롤아웃의 상태를 끝날 때까지 지켜봅니다. 기다리지 않으려면 --watch=false 를 씁니다. 지켜보는 중에 새 롤아웃이 시작되면 명령은 가장 최신 리비전을 계속 지켜봅니다.

롤아웃이 성공적으로 완료되면 kubectl rollout status 는 0 종료 코드를 돌려줍니다. 디플로이먼트가 진행 데드라인을 넘겼으면 0 이 아닌 종료 코드를 돌려줍니다.

터미널
$ kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
error: deployment "nginx" exceeded its progress deadline

$ echo $?
1

예시

쿠버네티스 문서의 controllers/nginx-deployment.yaml 한 벌입니다.

YAML
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

replicas: 3 이 파드를 3 벌 띄우라고 정합니다. selector.matchLabels 의 app: nginx 와 template.metadata.labels 의 app: nginx 가 짝을 이룹니다. 컨테이너 이름은 nginx, 이미지는 nginx:1.14.2, 컨테이너 포트는 80 입니다.

만들고 상태를 보는 명령

kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml
터미널
$ kubectl get deployments
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           18s

이 명령은 디플로이먼트를 READY · UP-TO-DATE · AVAILABLE · AGE 칸으로 보여줍니다.

터미널
$ 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

이미지만 바꾸는 한 줄

nginx 파드가 nginx:1.14.2 대신 nginx:1.16.1 이미지를 쓰게 하는 명령입니다.

kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1

파드 템플릿의 이미지를 바꾸는 갱신이라 이 한 줄이 롤아웃을 겁니다.

관련 항목

디플로이먼트를 이루는 구성 요소

파드 · 레플리카셋 · 파드 템플릿 · 셀렉터 · 레이블 · 컨테이너 이미지 · 원하는 상태 · nginx

디플로이먼트가 거치는 갱신 단계

롤아웃 · 롤백 · 리비전 · 스케일 업 · 스케일 다운 · 선언형 갱신

디플로이먼트에서 자주 나는 오류·장애

할당량 · readiness probe · 권한 · 리밋 레인지 · 컨디션

디플로이먼트를 호출·실행하는 명령

kubectl · kubectl apply · kubectl rollout status · kubectl set image · kubectl get · helm upgrade

디플로이먼트에 관여하는 역할·참여자

컨트롤러 · HorizontalPodAutoscaler · 수평 스케일링 · API

디플로이먼트가 속하는 쿠버네티스 리소스 분류

워크로드 · 배포 · 쿠버네티스 · StatefulSet · PodDisruptionBudget

다른 이름: Kubernetes Deployment · 쿠버네티스 디플로이먼트 · apps/v1 Deployment