관리 메뉴

피터의 개발이야기

[kubernetes] Deployment이란? 서비스 중단, 무중단 배포방법 본문

Kubernetes/기초공부

[kubernetes] Deployment이란? 서비스 중단, 무중단 배포방법

기록하는 백앤드개발자 2024. 1. 21. 22:17
반응형

 

ㅁ 관련 글

[kubernetes] 쿠버네티스 목차

 [Kubernetes] Kubernetes환경에서 graceful shutdown이란
  ㄴ Pod가 종료 시 서비스는 50X등의 기타 에러가 발생하면 안된다.

  ㄴ 기타 오류를 방지 하기 위한 graceful shutdown에 대해서 정리하였다.

 [DevOps] 청록색 배포, A/B 테스트 및 카나리아 배포
  ㄴ 클러스터 환경에서 배포의 방식을 이야기 할 때에 흔이 이야기되는 용어에 대해서 설명한 글이다.

  ㄴ 카나리, 블루그린 배포 방식에 대해서 기본적으로 알아두면 좋다.

  ㄴ 카나리는 일정비율로 배포한다는 점에서 RollingUpdate에 상응하고,
      블루그린은 A와 B 그룹 중 하나를 통으로 배포한다는 점에서 Restart에 상응한다.

 

ㅁ 들어가며

애플리케이션 수명주기 관리의 핵심은 신규 애플리케이션의 배포에 있다. Deployment는 그 이름처럼 배포 기능을 세분화하고 있다. 앱의 배포 시 롤링 업데이트, 리스타트를 지원하고 앱 배포 도중 잠시 멈출 수도 롤백할 수도 있다. 이번 글에서는 Deployment의 기능을 쿠버네티스 예제를 기반으로  정리하였다.

 

ㅁ Deployment란?

디플로이먼트(Deployment) 는 파드와 레플리카셋(ReplicaSet)에 대한 선언적 업데이트를 제공한다. 디플로이먼트는 쿠버네티스에서 상태가 없는 앱을 배포할 때 사용하는 가장 기본적인 컨트롤러이다. 배포 전략(.spec.strategy)으로는 RollingUpdate, Recreate가 있다. RollingUpdate기본 값이다. 

ㅇ Recreate: spec.strategy.type==Recreate이면 새 파드를 delete하고 생성한다. (서비스 중단 배포)

RollingUpdate: 파드를  maxUnavailablemaxSurge 기준에 따라 롤링 업데이트 방식으로 업데이트 한다.(서비스 무중단 배포)

 

아래의 글에서 서비스 무중단 배포에 해당하는 RollingUpdate를 알아보고,
서비스 중단 배포인 Recreate와 더불어 파괴적(disruptive) 업데이트에 대해서 알아보도록 하자.

 

디플로이먼트 생성

# 테스트를 위한 3대의 nginx 생성 deployment
$ cat nginx-deployment.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

# apply
$ k apply -f nginx-deloyment.yaml
deployment.apps/nginx-deployment created

 

# deployment 확인
$ k get deployments.apps nginx-deployment
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           3m3s

# replicaset 확인
$ k get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-86dcfdf4c6   3         3         3       3m8s

 

 디플로이 업데이트

  디플로이먼트는 업데이트되는 동안 일정한 수의 파드만 중단되도록 보장한다. 기본적으로 적어도 의도한 파드 수의 75% 이상이 동작하도록 보장한다(최대 25% 불가).

  디플로이먼트 컨트롤러는 각 시간마다 새로운 디플로이먼트에서 레플리카셋이 의도한 파드를 생성하고 띄우는 것을 주시한다. 만약 디플로이먼트가 업데이트되면, 기존 레플리카셋에서 .spec.selector 레이블과 일치하는 파드를 컨트롤 하지만, 템플릿과 .spec.template 이 불일치하면 스케일 다운이 된다.

# nginx 이미지 변경
$ kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
deployment.apps/nginx-deployment image updated

# 롤링아웃 상태 확인
$ k rollout status deployment nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "nginx-deployment" successfully rolled out

 

 

# deployment 확인
$ k get deployments.apps nginx-deployment
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           57m

# replicaset 확인
$ k get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-848dd6cfb5   3         3         3       7m4s  <=== 신규
nginx-deployment-86dcfdf4c6   0         0         0       58m   <=== 예전

dds는 업데이트되는 동안 일정한 수의 파드만 중단되도록 보장한다. 기본적으로 적어도 의도한 파드 수의 75% 이상이 동작하도록 보장한다(최대 25% 불가).

# deployment 상세 확인
$ k describe deployments.apps nginx-deployment
Name:                   nginx-deployment
Namespace:              default
CreationTimestamp:      Mon, 22 Jan 2024 00:13:39 +0900
Labels:                 app=nginx
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=nginx
Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=nginx
  Containers:
   nginx:
    Image:        nginx:1.16.1
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   nginx-deployment-848dd6cfb5 (3/3 replicas created)
Events:
  Type    Reason             Age    From                   Message
  ----    ------             ----   ----                   -------
  Normal  ScalingReplicaSet  2m11s  deployment-controller  Scaled up replica set nginx-deployment-848dd6cfb5 to 3

 

디플로이먼트 롤백

  롤백은 앱의 버젼 변경 시

  때때로 디플로이먼트의 롤백을 원할 수도 있다. 예를 들어 디플로이먼트가 지속적인 충돌로 안정적이지 않은 경우. 기본적으로 모든 디플로이먼트의 롤아웃 기록은 시스템에 남아있어 언제든지 원할 때 롤백이 가능하다. 다만 이는 디플로이먼트 파드 템플릿 (.spec.template)이 변경되는 경우에만 새로운 수정 버전이 생성된다는 것을 의미한다.

 

# 잘못된 버젼 업데이트 가정, 1.6.1 아닌 1.61
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.161
deployment.apps/nginx-deployment image updated

# rollout 상태 확인 시 고착 상태
$ kubectl rollout status deployment/nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...

# replicaset 확인
$ k get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-79c8677895   1         1         0       2m57s
nginx-deployment-7f857449c7   0         0         0       9m43s
nginx-deployment-848dd6cfb5   3         3         3       34m

# pod 상태확인
$ k get po
NAME                                READY   STATUS             RESTARTS   AGE
nginx                               1/1     Running            0          24h
nginx-deployment-79c8677895-c6jsz   0/1     ImagePullBackOff   0          2m46s  <== 신규 image 버젼
nginx-deployment-848dd6cfb5-gwxtl   1/1     Running            0          34m
nginx-deployment-848dd6cfb5-tcrvp   1/1     Running            0          34m
nginx-deployment-848dd6cfb5-xxzdv   1/1     Running            0          34m

# replicaset 상세 확인
$ k describe rs nginx-deployment-79c8677895
Name:           nginx-deployment-79c8677895
Namespace:      default
Selector:       app=nginx,pod-template-hash=79c8677895
Labels:         app=nginx
                pod-template-hash=79c8677895
Annotations:    deployment.kubernetes.io/desired-replicas: 3
                deployment.kubernetes.io/max-replicas: 4
                deployment.kubernetes.io/revision: 2
Controlled By:  Deployment/nginx-deployment
Replicas:       1 current / 1 desired
Pods Status:    0 Running / 1 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  app=nginx
           pod-template-hash=79c8677895
  Containers:
   nginx:
    Image:        nginx:1.161
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  52s   replicaset-controller  Created pod: nginx-deployment-79c8677895-f8m2k

 

 

디플로이먼트의 롤아웃 기록 확인

# rollout history 확인 중
$ k rollout history deployment nginx-deployment
deployment.apps/nginx-deployment
REVISION  CHANGE-CAUSE
2         <none>
3         <none>
4         <none>

# history revision 상세조회
$ k rollout history deployment/nginx-deployment --revision 2
deployment.apps/nginx-deployment with revision #2
Pod Template:
  Labels:	app=nginx
	pod-template-hash=79c8677895
  Containers:
   nginx:
    Image:	nginx:1.161
    Port:	80/TCP
    Host Port:	0/TCP
    Environment:	<none>
    Mounts:	<none>
  Volumes:	<none>


$ k rollout history deployment/nginx-deployment --revision 4
deployment.apps/nginx-deployment with revision #4
Pod Template:
  Labels:	app=nginx
	pod-template-hash=59b5f554d8
  Containers:
   nginx:
    Image:	nginx:1.1611
    Port:	80/TCP
    Host Port:	0/TCP
    Environment:	<none>
    Mounts:	<none>
  Volumes:	<none>

 ㄴ 쿠버네티스 문서와 비교하면, Change-Cause에 실행했던 명령어 이력이 나타난다. 하지만 deployment에 .metadata.anotation 필드를 정의하지 안하 <node>이라고 표시되었다.

 ㄴ 어노테이션 업데이트은 이곳에 있다.

 ㄴ revision 상세 조회 시 내용은 확인 가능하였다.

 

# 수정 버전의 세부 정보 확인
$ kubectl rollout history deployment/nginx-deployment --revision=4
deployment.apps/nginx-deployment with revision #4
Pod Template:
  Labels:	app=nginx
	pod-template-hash=59b5f554d8
  Containers:
   nginx:
    Image:	nginx:1.1611
    Port:	80/TCP
    Host Port:	0/TCP
    Environment:	<none>
    Mounts:	<none>
  Volumes:	<none>
  
# Pod 정상확인
$ k get po
NAME                                READY   STATUS    RESTARTS   AGE
nginx                               1/1     Running   0          34h
nginx-deployment-848dd6cfb5-gpzhs   1/1     Running   0          9h
nginx-deployment-848dd6cfb5-nw8j9   1/1     Running   0          9h
nginx-deployment-848dd6cfb5-qfxxb   1/1     Running   0          9h

 

ㅇ 이전 버전으로 롤백

# 특정 revision으로 롤백하는 경우
$ kubectl rollout undo deployment/nginx-deployment --to-revision=6
deployment.apps/nginx-deployment rolled back

 

디플로이먼트 스케일링

# scale out
$ kubectl scale deployment/nginx-deployment --replicas=5
deployment.apps/nginx-deployment scaled

 

 

autoscale

클러스터에서 horizontal Pod autoscaling를 설정 한 경우 디플로이먼트에 대한 오토스케일러를 설정할 수 있다. 그리고 기존 파드의 CPU 사용률을 기준으로 실행할 최소 파드 및 최대 파드의 수를 선택할 수 있다.

# autoscale 지정
$ k autoscale deployment nginx-deployment --min=2 --max=5 --cpu-percent=80
horizontalpodautoscaler.autoscaling/nginx-deployment autoscaled

 

 

비례적 스케일링(Proportional Scaling)

디플로이먼트 롤링업데이트는 여러 버전의 애플리케이션을 동시에 실행할 수 있다. 롤아웃 중 안정성을 위해 ASIS-TOBE사이에 비율을 가지고 균형을 조절할 수 있다.

# 현재 비율 지정
$ k get deployments.apps nginx-deployment -o yaml
.................
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
.................

# 잘못된 이미지 적용
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.161
deployment.apps/nginx-deployment image updated

# replicaset 확인
$ k get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-79c8677895   1         1         0       10h <=== 신규 생성된
nginx-deployment-86dcfdf4c6   2         2         2       66m

# 롤백
$ k rollout undo deployment nginx-deployment
deployment.apps/nginx-deployment rolled back

# replicaset 확인
# t
$ k get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-79c8677895   0         0         0       10h <=== 신규 생성, 0으로 정지
nginx-deployment-86dcfdf4c6   2         2         2       70m <=== 기존 유지

 ㄴ 전체 Pod 중 25%, 4개면 1개만 신규 Pod 생성한다. 

 ㄴ 위의 경우처럼 잘못된 이미지로 인해 정상이 1개발생. 4개중 1개면 작업이 홀링된다.

 ㄴ 위험 감지 후 롤백 시 기존에 Pod가 유지된 상태이기 때문에 시스템 안정성을 확보할 수 있다.

 

디플로이먼트 롤아웃 일시 중지와 재개

 디플로이먼트를 업데이트하면 바로 Pod가 업데이트 되었다. 디플로이먼트 롤아웃을  일시 중지 상태로 변경하면 새 업데이트가 있어서 Pod에 영향을 주지 않는다.

 

일시정지

# 디플로이먼트 일시중지
$ kubectl rollout pause deployment/nginx-deployment
deployment.apps/nginx-deployment paused

# 업데이트 테스트
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
deployment.apps/nginx-deployment image updated

# replicaSet 확인
$ k get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-59b5f554d8   0         0         0       10h <== 기존
nginx-deployment-79c8677895   0         0         0       10h <== 기존
nginx-deployment-848dd6cfb5   0         0         0       10h <== 기존
nginx-deployment-86dcfdf4c6   2         2         2       90m <== 기존

 ㅇ 디플로이먼트 일시중지 후 업데이트가 안되는지 확인해 보았다.

 ㅇ 기존 replicaset만 있고 새로운 rs가 생성되지 않았다.

 

# 디플로이먼트를 편집하기
# 기존 리소스를 업데이트를 진행하는 과정 예시
$ kubectl set resources deployment/nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi
deployment.apps/nginx-deployment resource requirements updated

 ㅇ 바로바로 반영되지 않으니 편하게 deployment를 편집해도 서비스에는 영향이 없다.

 ㅇ 이후 업데이트가 완료되면 일시정지를 해제한다.

 

재개

# 재개
$ kubectl rollout resume deployment/nginx-deployment
deployment.apps/nginx-deployment resumed

# rs 확인
$ k get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-59b5f554d8   0         0         0       10h
nginx-deployment-6bfc44755c   2         2         2       8s <== 신규 rs 
nginx-deployment-79c8677895   0         0         0       11h
nginx-deployment-848dd6cfb5   0         0         0       11h
nginx-deployment-86dcfdf4c6   0         0         0       95m

# 업데이트 상태 확인: 완료되었다.
$ k rollout status deployment nginx-deployment
deployment "nginx-deployment" successfully rolled out


# 히스토리 확인
$ k rollout history deployment nginx-deployment
deployment.apps/nginx-deployment
REVISION  CHANGE-CAUSE
4         <none>
9         <none>
10        <none>
11        <none>
12        <none>

# 히스토리 상세 확인
$ k rollout history deployment nginx-deployment --revision 12
deployment.apps/nginx-deployment with revision #12
Pod Template:
  Labels:	app=nginx
	pod-template-hash=6bfc44755c
  Containers:
   nginx:
    Image:	nginx:1.16.1
    Port:	80/TCP
    Host Port:	0/TCP
    Limits:
      cpu:	200m
      memory:	512Mi
    Environment:	<none>
    Mounts:	<none>
  Volumes:	<none>

 

 ㅇ 일시정지 시 적용한 resource가 재개와 함께 적용되었다.

 ㅇ 적용되용은 history에 반영되고, revision 12에서 확인할 수 있었다.

 

ㅁ 파괴적(disruptive) 업데이트

 지금까지 디플로이먼트는 .spec.strategy.type==RollingUpdate 인 경우였다. 이 방식은 파드를 롤링 업데이트 방식으로 업데이트 하고 maxUnavailable와 maxSurge 를 명시해서 롤링 업데이트 프로세스를 제어할 수 있다. 경우에 따라, 한 번 초기화를 거쳐 업데이트를 해야하는 경우가 있다. DB에 필수 컬럼이 추가되는 경우, 혹은 즉시 업데이트를 수행하는 경우 파괴적 업데이트를 진행할 수 있다.

 

Receate

# deployment 수정
$ k edit deployments.apps nginx-deployment
.... ASIS ....
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
.... TOBE ....
  strategy:
    type: Recreate
.............

# 업데이트
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
deployment.apps/nginx-deployment image updated

# Pod 조회: 한꺼번에 전체 Pod가 신규 생성됨.
$ k get po
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-5b8d6b8dbf-dnbms   1/1     Running   0          27s
nginx-deployment-5b8d6b8dbf-sm6rt   1/1     Running   0          27s
nginx-deployment-5b8d6b8dbf-w9cx6   1/1     Running   0          27s

 

Replace --force

# replace --force
$ k replace -f nginx-deloyment.yaml --force
deployment.apps "nginx-deployment" deleted
deployment.apps/nginx-deployment replaced

# Pod 모니터링
$ k get po
NAME                                READY   STATUS              RESTARTS   AGE
nginx-deployment-58bf96d6bd-kjns7   0/1     Terminating         0          28s
nginx-deployment-58bf96d6bd-qhtjj   0/1     ContainerCreating   0          2s
nginx-deployment-58bf96d6bd-rt8qn   0/1     ContainerCreating   0          2s
nginx-deployment-58bf96d6bd-tlk7p   0/1     Terminating         0          28s
nginx-deployment-58bf96d6bd-vtrx8   0/1     ContainerCreating   0          2s

$ k get po
NAME                                READY   STATUS              RESTARTS   AGE
nginx-deployment-58bf96d6bd-qhtjj   1/1     Running             0          4s
nginx-deployment-58bf96d6bd-rt8qn   0/1     ContainerCreating   0          4s
nginx-deployment-58bf96d6bd-vtrx8   0/1     ContainerCreating   0          4s

$ k get po
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-58bf96d6bd-qhtjj   1/1     Running   0          5s
nginx-deployment-58bf96d6bd-rt8qn   1/1     Running   0          5s
nginx-deployment-58bf96d6bd-vtrx8   1/1     Running   0          5s

 ㄴ --force는 기본적으로 이전 Object를 delete를 수행한다.

 ㄴ deployment를 삭제하고 새로 생성하면서 서비스 중단이 발생한다.

카나리(canary) 디플로이먼트

만약 디플로이먼트를 이용해서 일부 사용자 또는 서버에 릴리스를 롤아웃 하기 위해서는 리소스 관리에 설명된 카나리 패던에 따라 각 릴리스 마다 하나씩 여러 디플로이먼트를 생성할 수 있다. 새 릴리스가 완전히 롤아웃되기 전에 실제 운영 트래픽을 수신할 수 있도록 새로운 애플리케이션 릴리스(파드 템플리트의 이미지 태그를 통해 지정됨)의 카나리 를 이전 릴리스와 나란히 배포하는 것이 일반적이다.
  참고로 [DevOps] 청록색 배포, A/B 테스트 및 카나리아 배포 글은 배포 방식에 대한 글이다. 카나리아 배포 방식이 무엇인지 의미를 알 수 있다.

 

ㅁ 함께 보면 좋은 사이트

 쿠버네티스 문서 - 디플로이먼트

HorizontalPodAutoscaler 연습

 

반응형
Comments