Kubernetes RollingUpdate & Rollbacks, 제대로 이해하기

2024. 4. 7. 23:59BACKEND

반응형

본 포스팅에서는 Kubernetes의 RollingUpdate의 개념을 이해하는 목표를 가집니다.

 

🔗  Kubernetes Series

모든 Kubernetes 시리즈를 확인하시려면 위를 참고해 주세요.

 


 

 

 

 

지난 포스팅에서 Deployment에 대해 알아보았습니다. 

다시 짚어보자면, Deployment는 이름 그대로, 쿠버네티스에서 배포를 위한 객체입니다.

기능적으로는, Pod와 ReplicaSet를 배포를 관리합니다.

 

이번 포스팅에서는 배포 이후의 업데이트 과정을 다뤄보겠습니다.

앱 버전 1을 배포한 상태에서 버전 2로 업데이트할 때를 생각해보겠습니다.

 

5개의 Pod가 떠 있는 상태에서 해당 Pod 를 어떻게 교체해야 할까요?또, 교체되는 과정이 어떻게 될지 살펴보도록 하겠습니다.

 

 

 

Rollout

Deployment를 생성하면, Deployment는 새로운 배포 Revision을 생성합니다.

이후, 새로운 버전을 배포하고자 Deployment를 수정하면,

Deployment 생성을 시작하고, 이는 새 배포에 대한 Revision을 생성합니다.

 

덕분에 배포 변화를 추적할 수 있을 뿐만 아니라,

필요 시 이전 배포 버전으로 쉽게 되돌릴 수 (Rollback) 있습니다.

 

 

Deployment Strategy

배포 방법에는 2가지가 존재합니다.

 

 

첫 번째, Recreate

: 배포된 모든 앱 제거 후, 새로운 버전 앱 한 번에 생성

 

 

가령, 배포된 이전 앱 5개를 모두 지우고 난 후,

새로운 앱 5개를 새로 띄우는 것입니다.

 

                                    🆕     🆕      🆕      🆕
 ️    ️  Old Version         ⬆️️      ⬆️️      ⬆️️       ⬆️
 🔴     🔴     🔴     🔴     🟠     🟠      🟠      🟠
 ⬇️      ⬇️     ⬇️      ⬇️        New Version

 

 

하지만, 이 방식엔 문제가 있습니다.

모든 배포 앱을 다운시키고 나서 새로운 앱이 업로드 될 때까지 사용자의 접근이 불가합니다.

무중단 배포 (Zero Downtime Deployment)가 불가한 상황입니다.

 

 


 

 

 

개발자들은 사용자들이 해당 앱을 항상 사용가능하게 만들게끔 하고 싶죠.

동시에, 새로운 버전을 하루에도 여러 번 배포할 수 있게 하고자 합니다.

쿠버네티스에서는 이를 RollingUpdate 로 해결합니다.

 

 

 

두 번째, Rolling Update

: 제거 및 생성을 부분적으로 점차 진행

 

 

Deployment 생성 시 디폴트 배포 전략입니다.

 

Rolling Update는 현재 배포된 버전의 Pod를 새로운 Pod로 점진적으로 교체합니다.
새로운 Pod는 리소스가 유효한 노드에 예약되어,

이전 Pod를 제거하기 전에 새로운 Pod가 시작되기를 기다립니다.

 

 

구 버전을 부분적으로 내려, 그 만큼의 새 버전을 배포하며,

 

         🆕               🆕
 ️    ️    ⬆️️               ⬆️️ 
 🔴    🟠     🔴      🟠      🔴     🔴
 ⬇️              ⬇️ 

 

 

모두 새 버전이 될 때까지 반복합니다.

 

 ️    ️                          🆕                 🆕
 ️    ️                          ⬆️️                 ⬆️️ 
🟠      🟠      🔴      🟠      🔴      🟠
                      ⬇️               ⬇️ 

 

 

앱이 다운되지 않고 업데이트 가능하죠.

 

좌측 상단 부터 1, 2, 3, 4 / https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/

 

 

쿠버네티스에서는 업데이트가 버전화되고 배포 업데이트는 이전(안정적) 버전으로 되돌릴 수 있습니다.

롤링 업데이트를 통해 다운타임 없이 배포 업데이트를 수행할 수 있습니다.

 

 

 

 

 

RollingUpdate Strategy

위의 두 전략은 Deployment 정의 시 .spec.strategy.type에 지정합니다.

즉, .spec.strategy.type 필드에 Recreate 또는 RollingUpdate를 입력할 수 있으며, "RollingUpdate"가 기본값입니다.

 

⚠️ .spec.strategy 이전 포드를 새 포드로 대체하는 데 사용되는 전략을 지정합니다. 

 

만약 RollingUpdate 를 지정했을 시, 

maxUnavailable 과 maxSurge를 설정해 롤링 업데이트 과정을 설정할 수 있습니다.

 

기본적으로 업데이트 중에 이용 불가한 Pod의 최대 개수 (maxUnavailable) 와,

한 번에 생성 가능한 새로운 Pod의 최대 수 (maxSurge)는 한 개 입니다.

두 옵션 모두 숫자 또는 백분율(Pods) 로 설정할 수 있습니다.

 

 

Max Unavailable

: 업데이트 중, 이용 불가한 Pod의 최대 개수


.spec.strategy.rollingUpdate.maxUnavailable 은 업데이트 프로세스 중에 사용할 수 없는 최대 파드의 수를 지정하는 선택적 필드입니다.

해당 값은 5와 같이 특정 개수를 명시하거나, 10%와 같이 비율로 기입할 수 있습니다.

기본 값은 25% 입니다. 즉 75%의 Pod는 사용을 보장받는 것이죠.

 

만약, .spec.strategy.rollingUpdate.maxSurge 가 0이면 값이 0이 될 수 없습니다.

 

가령 30%로 설정 하면 RollingUpdate 시작 시,

이전 ReplicaSet의 크기를 즉각적으로 Desired Pod의 70%로 Scale Down할 수 있습니다.

새 Pod가 준비되면 기존 ReplicaSet을 Scale Down할 수 있으며,

업데이트 중에 항상 사용 가능한 전체 Pod의 수는 새로운 Pod 수의 70% 이상이 되도록 새 레플리카셋을 Scale Up할 수 있습니다.

 

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
 strategy:
   type: RollingUpdate
   rollingUpdate:
     maxUnavailable: 1

 

 

 

 

Max Surge

: 업데이트 중, 새로 추가되는 최대 파드 수


.spec.strategy.rollingUpdate.maxSurge 는 의도한 파드의 수에 대해

생성할 수 있는 최대 파드의 수를 지정하는 필드입니다.

해당 값은 5와 같이 특정 개수를 명시하거나, 10%와 같이 비율로 기입할 수 있습니다.

MaxUnavailable 값이 0이면 이 값은 0이 될 수 없으며, 기본 값은 25% 입니다.

예를 들어 이 값을 30%로 설정하면 RollingUpdate 시작시,

ReplicaSet의 크기를 즉시 조정해서 기존 혹은 새 Pod의 전체 갯수를 의도한 Pod의 130%를 넘지 않도록 합니다.

기존 파드가 죽으면 새로운 ReplicaSet Scale Up할 수 있으며,

업데이트하는 동안 항상 실행하는 총 Pod의 수는 최대 의도한 Pod의 수의 130%가 되도록 보장합니다.

 

 

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
 strategy:
   type: RollingUpdate
   rollingUpdate:
     maxUnavailable: 1

 

 

 

 

 

 

Applying

위의 변경된 내용을 기반으로 Deployment를 업데이트하는 방법은 두 가지가 존재합니다.

 

 

첫 번째, 필요한 내용 수정 후 kubectl apply 명령어로 변경 사항 반영할 수 있습니다.

 

❯ kubectl apply -f deployment-definition.yml

 

 

새로운 Rollout이 트리거되고, Deployment의 새로운 Revision 가 생성됩니다.

 

 

 

두 번째, 필요한 내용 수정 후 kubectl set image 명령 사용할 수 있습니다.

 

 

❯ kubectl set image deployment/myapp-deployment nginx-container=nginx:1.9.1
# kubectl set image ≪deployment-name≫ ≪container-name≫
 

 

Application의 이미지를 업데이트를 함으로써 해당 변경을 적용합니다.

이 방식은 배포 정의 파일의 구성을 달라지게 하기 때문에,

정의 파일을 유지하고 싶을 때에는 주의해야 합니다.

 


 

 

Update

그렇다면, 실제 Update 시 Pod 들에 어떤 변화가 있을까요?

 

결론적으로, 쿠버네티스 Deployment 객체가 내부적으로 새 ReplicaSet을 만들고 컨테이너를 배포하기 시작합니다.

 

가령, 새로운 앱 5개를 배포한다고 할 때, 먼저 자동으로 ReplicaSet를 생성하고,

이후 ReplicaSet에 따라 여러 개의 Pod가 생성됩니다.

 

 

실제 업데이트를 진행하다 보면, Events 로그에서 업데이트가 다르게 변하고 있음을 확인할 수 있는데요.

실제 Deployment를 두 가지 Strategy로 생성한 후, 업데이트를 했을 때의 로그를 확인해보도록 하겠습니다.

 

 

 

Recreate Events

 

❯ kubectl describe deploy webapp-recreate
Name:               webapp-recreate
...
Selector:           name=webapp
Replicas:           4 desired | 4 updated | 4 total | 0 available | 4 unavailable
StrategyType:       Recreate
MinReadySeconds:    0
Pod Template:
  Labels:  name=webapp
  Containers:
   nginx:
    Image:        nginx:1.14.3
    Port:         80/TCP
    Host Port:    0/TCP
...
Events:
  Type    Reason             Age    From                   Message
  ----    ------             ----   ----                   -------
  Normal  ScalingReplicaSet  4s     deployment-controller  Scaled down replica set webapp-recreate-54f8cdd695 to 0 from 4
  Normal  ScalingReplicaSet  4s     deployment-controller  Scaled up replica set webapp-recreate-7bb89cfdbb to 4

 

 

ScalingReplicaSet 으로 Pod를 0에서 4로 즉시 올리는 것을 확인할 수 있습니다.

 

 

 

Rolling Update Events

 

❯ kubectl describe deployment
Name:                   webapp-rollingupdate
Namespace:              default
...
Replicas:               4 desired | 2 updated | 5 total | 3 available | 2 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  name=webapp
  Containers:
   nginx:
    Image:        nginx:1.24.0
    Port:         80/TCP
....
Events:
  Type    Reason             Age    From                   Message
  ----    ------             ----   ----                   -------
  Normal  ScalingReplicaSet  2m17s  deployment-controller  Scaled up replica set webapp-rollingupdate-54f8cdd695 to 4
  Normal  ScalingReplicaSet  3s     deployment-controller  Scaled up replica set webapp-rollingupdate-b5bc55fdd to 1
  Normal  ScalingReplicaSet  3s     deployment-controller  Scaled down replica set webapp-rollingupdate-54f8cdd695 to 3 from 4
  Normal  ScalingReplicaSet  3s     deployment-controller  Scaled up replica set webapp-rollingupdate-b5bc55fdd to 2 from 1

 

 

ScalingReplicaSet 으로 ScaleUp/Down을 점진적으로 진행하는 것을 확인할 수 있습니다.

 

 

 

 

 

Rollout

마지막으로, 아래 명령어를 통해 Rollout 정보를 얻을 수 있습니다.

 

 

1. Rollout 상태 확인

❯ kubectl rollout status deployment/myapp-deployment
Waiting for deployment "myapp-deployment" rollout to finish: 2 out of 4 new replicas have been updated...

 

 

 

2. Rollout 내역 확인

❯ kubectl rollout history deployment/myapp-deployment
deployment.apps/myapp-deployment 
REVISION  CHANGE-CAUSE
1         <none>
2         <none>
3         <none>

 

 

 

Rollback

업데이트 이후 새로운 버전에서 문제를 발견하면 어떻게 될까요?

 

쿠버네티스 Deployment는 이전 Revision으로의 롤백 지원합니다.

kubectl rollout undo 명령어 뒤 배포 이름을 명시해서 롤백할 수 있습니다.

 

❯ kubectl rollout undo deployment/myapp-deployment

 

 

새 ReplicaSet의 Deployment는 새 ReplicaSet의 Pod를 파괴하고,

이전 버전 ReplicaSet의 예전 Pod를 불러옵니다.

 

실제 kubectl get replicasets 명령어를 확인해보면,

다시 이전 버전의 Pod가 4개로 늘어난 것을 확인할 수 있습니다.

 

frontend-54f8cdd695 가 구버전, frontend-7bb89cfdbb가 신버전입니다.

 

❯ kubectl get replicasets
NAME                  DESIRED   CURRENT   READY   AGE
frontend-54f8cdd695   0         0         0       4m57s
frontend-7bb89cfdbb   4         4         4       4m19s

 

 

해당 앱은 이전 버전으로 돌아갑니다.

 

❯ kubectl get replicasets
NAME                  DESIRED   CURRENT   READY   AGE
frontend-54f8cdd695   4         4         4       4m57s
frontend-7bb89cfdbb   0         0         0       4m19s

 

 

 

 

 

 

 

 

 

반응형