10 Kubernetes Basics - Performing a Rolling Update
<출처>
https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/
Objectives
- kubectl 을 이용해 롤링 업데이트를 수행한다.
Updating an application
사용자들은 애플리케이션이 항상 가용한 상태일 것이라 여기고 개발자들은 하루에 여러번씩 새로운 버전을 배포하도록 요구 받고있다. 쿠버네티스에서는 이것을 롤링 업데이트를 통해 이루고 있다. 롤링 업데이트는 파드 인스턴스를 점진적으로 새로운 것으로 업데이트하여 디플로이먼트 업데이트가 서비스 중단 없이 이루어질 수 있도록 해준다. 새로운 파드는 가용한 자원을 보유한 노드로 스케줄될 것이다.
이전 모듈에서 여러 개의 인스턴스를 동작시키도록 애플리케이션을 스케일했다. 이것은 애플리케이션의 가용성에 영향을 미치지 않으면서 업데이트를 수행하는 것에 대한 요구이다. 기본적으로, 업데이트가 이루어지는 동안 이용 불가한 파드의 최대 개수와 생성 가능한 새로운 파드의 최대 개수는 하나다. 두 옵션은 (파드에 대한) 개수 또는 백분율로 구성될 수 있다. 쿠버네티스에서, 업데이트는 버전으로 관리되고 어떠한 디플로이먼트 업데이트라도 이전의 (안정적인) 버전으로 원복이 가능하다.
롤링 업데이트는 파드 인스턴스를 점진적으로 새로운 것으로 업데이트하여 디플로이먼트 업데이트가 서비스 중단 없이 이루어질 수 있도록 해준다.
Rolling updates overview
애플리케이션 스케일링과 유사하게, 디플로이먼트가 외부로 노출되면, 서비스는 업데이트가 이루어지는 동안 오직 가용한 파드에게만 트래픽을 로드밸런스 할 것이다. 가용한 파드란 애플리케이션의 사용자들에게 가용한 상태의 인스턴스를 말한다.
롤링 업데이트는 다음 동작들을 허용해준다.
- 하나의 환경에서 또 다른 환경으로의 애플리케이션 프로모션 (컨테이너 이미지 업데이트를 통해)
- 이전 버전으로의 롤백
- 서비스 중단 없는 애플리케이션의 지속적인 통합과 지속적인 전달
디플로이먼트가 외부로 노출되면, 서비스는 업데이트가 이루어지는 동안 오직 가용한 파드에게만 트래픽을 로드밸런스 할 것이다.
다음 대화형 강좌에서, 새로운 버전의 애플리케이션을 업데이트 하고 나서, 롤백을 수행하도록 해보자.
Interactive Tutorial - Updating Your App
이번 시나리오의 목표는 kubectl set image 로 배포된 어플리케이션을 업데이트 하고 rollout undo 명령을 통해 롤백을 해보는 것이다.
Step 1: Update the version of the app
"get deployments" 명령을 이용하여 디플로이먼트를 확인한다.
1 2 3 | root@zerobig-vm:~# kubectl get deployments NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE kubernetes-bootcamp 2 2 2 2 21h | cs |
"get pods" 명령을 이용하여 동작중인 파드를 확인한다.
1 2 3 4 | root@zerobig-vm:~# kubectl get pods NAME READY STATUS RESTARTS AGE kubernetes-bootcamp-5c69669756-d4kgp 1/1 Running 2 21h kubernetes-bootcamp-5c69669756-jlqrw 1/1 Running 0 35m | cs |
현재 앱에 대한 애플리케이션의 이미지 버전을 보려면, 파드에 대해 "describe" 명령을 수행한다(이미지 필드를 살편본다).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | root@zerobig-vm:~# kubectl describe pods Name: kubernetes-bootcamp-5c69669756-d4kgp Namespace: default Node: minikube/10.0.2.15 Start Time: Thu, 09 Aug 2018 00:31:44 +0900 Labels: app=v1 pod-template-hash=1725225312 run=kubernetes-bootcamp Annotations: <none> Status: Running IP: 172.17.0.5 Controlled By: ReplicaSet/kubernetes-bootcamp-5c69669756 Containers: kubernetes-bootcamp: Container ID: docker://19a99d11a3053c3e42be636c3fc877a800ce054fc35f081f052f34cd98569843 Image: gcr.io/google-samples/kubernetes-bootcamp:v1 Image ID: docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64af Port: 8080/TCP Host Port: 0/TCP State: Running Started: Thu, 09 Aug 2018 20:50:28 +0900 Last State: Terminated Reason: Error Exit Code: 255 Started: Thu, 09 Aug 2018 09:25:51 +0900 Finished: Thu, 09 Aug 2018 20:49:43 +0900 Ready: True Restart Count: 2 Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from default-token-7d4h2 (ro) Conditions: Type Status Initialized True Ready True PodScheduled True Volumes: default-token-7d4h2: Type: Secret (a volume populated by a Secret) SecretName: default-token-7d4h2 Optional: false QoS Class: BestEffort Node-Selectors: <none> Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s node.kubernetes.io/unreachable:NoExecute for 300s Events: <none> Name: kubernetes-bootcamp-5c69669756-jlqrw Namespace: default Node: minikube/10.0.2.15 Start Time: Thu, 09 Aug 2018 21:27:14 +0900 Labels: pod-template-hash=1725225312 run=kubernetes-bootcamp Annotations: <none> Status: Running IP: 172.17.0.8 Controlled By: ReplicaSet/kubernetes-bootcamp-5c69669756 Containers: kubernetes-bootcamp: Container ID: docker://4fe92425350cfbb3a694630760066690534ca459394a25e67dc57bc6dac3ca70 Image: gcr.io/google-samples/kubernetes-bootcamp:v1 Image ID: docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64af Port: 8080/TCP Host Port: 0/TCP State: Running Started: Thu, 09 Aug 2018 21:27:16 +0900 Ready: True Restart Count: 0 Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from default-token-7d4h2 (ro) Conditions: Type Status Initialized True Ready True PodScheduled True Volumes: default-token-7d4h2: Type: Secret (a volume populated by a Secret) SecretName: default-token-7d4h2 Optional: false QoS Class: BestEffort Node-Selectors: <none> Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s node.kubernetes.io/unreachable:NoExecute for 300s Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 36m default-scheduler Successfully assigned kubernetes-bootcamp-5c69669756-jlqrw to minikube Normal SuccessfulMountVolume 36m kubelet, minikube MountVolume.SetUp succeeded for volume "default-token-7d4h2" Normal Pulled 36m kubelet, minikube Container image "gcr.io/google-samples/kubernetes-bootcamp:v1" already present on machine Normal Created 36m kubelet, minikube Created container Normal Started 36m kubelet, minikube Started container | cs |
(참고로 이미지 버전만을 확인하기 위해서라면 아래 명령이 보다 적합할 듯 하다.)
1 2 3 4 5 | root@zerobig-vm:~# kubectl describe pods | grep Image Image: gcr.io/google-samples/kubernetes-bootcamp:v1 Image ID: docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64af Image: gcr.io/google-samples/kubernetes-bootcamp:v1 Image ID: docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64af | cs |
애플리케이션의 버전을 2로 업데이트하기 위해 "set image" 명령을 사용하자. 배포 이름과 새로운 이미지 버전 뒤에 버전 정보가 따른다.
1 2 | root@zerobig-vm:~# kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=jocatalin/kubernetes-bootcamp:v2 deployment.apps "kubernetes-bootcamp" image updated | cs |
이 명령은 디플로이먼트에게 애플리케이션에 다른 이미지를 사용하도록 통보해주고 롤링 업데이트가 개시되도록 해준다. "get pods" 명령으로 새로운 파드의 상태와 이전 파드가 소멸되는지 확인해보자.
1 2 3 4 5 6 7 8 9 10 | root@zerobig-vm:~# kubectl get pods NAME READY STATUS RESTARTS AGE kubernetes-bootcamp-5c69669756-d4kgp 1/1 Terminating 2 21h kubernetes-bootcamp-5c69669756-jlqrw 1/1 Terminating 0 48m kubernetes-bootcamp-7799cbcb86-6hfvm 1/1 Running 0 16s kubernetes-bootcamp-7799cbcb86-lxht5 1/1 Running 0 23s root@zerobig-vm:~# kubectl get pods NAME READY STATUS RESTARTS AGE kubernetes-bootcamp-7799cbcb86-6hfvm 1/1 Running 0 1m kubernetes-bootcamp-7799cbcb86-lxht5 1/1 Running 0 1m | cs |
Step 2: Verify an update
먼저 애플리케이션이 동작하는지 확인해 본다. 노출되어진 IP 와 Port 정보를 알아내기 위해 "describe service" 를 이용할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | root@zerobig-vm:~# kubectl describe services/kubernetes-bootcamp Name: kubernetes-bootcamp Namespace: default Labels: run=kubernetes-bootcamp Annotations: <none> Selector: run=kubernetes-bootcamp Type: NodePort IP: 10.102.90.35 Port: <unset> 8080/TCP TargetPort: 8080/TCP NodePort: <unset> 31988/TCP Endpoints: 172.17.0.7:8080,172.17.0.9:8080 Session Affinity: None External Traffic Policy: Cluster Events: <none> | cs |
지정된 노드 포트의 값을 갖는 NODE_PORT 라는 환경 변수를 생성한다.
1 2 3 | root@zerobig-vm:~# export NODE_PORT=$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}') root@zerobig-vm:~# echo NODE_PORT=$NODE_PORT NODE_PORT=31988 | cs |
다음, 노출된 IP와 Port 로 curl 을 수행할 것이다.
1 2 3 4 5 6 7 8 9 10 | root@zerobig-vm:~# curl $(minikube ip):$NODE_PORT Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-7799cbcb86-lxht5 | v=2 root@zerobig-vm:~# curl $(minikube ip):$NODE_PORT Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-7799cbcb86-lxht5 | v=2 root@zerobig-vm:~# curl $(minikube ip):$NODE_PORT Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-7799cbcb86-6hfvm | v=2 root@zerobig-vm:~# curl $(minikube ip):$NODE_PORT Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-7799cbcb86-lxht5 | v=2 root@zerobig-vm:~# curl $(minikube ip):$NODE_PORT Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-7799cbcb86-6hfvm | v=2 | cs |
매번 호출 할때 마다 다른 Pod 에게 요청이 전달되어 처리되고 모든 파드가 최신 버전(v2) 로 동작됨을 볼 수 있다.
업데이트 또한 rollout status 명령을 수행함으로서 확인 가능하다.
1 2 | root@zerobig-vm:~# kubectl rollout status deployments/kubernetes-bootcamp deployment "kubernetes-bootcamp" successfully rolled out | cs |
앱에 대한 현재의 이미지 버전을 보려면, 파드에 대한 describe 명령으로 확인 가능 하다.
1 2 3 4 5 | root@zerobig-vm:~# kubectl describe pods | grep Image Image: jocatalin/kubernetes-bootcamp:v2 Image ID: docker-pullable://jocatalin/kubernetes-bootcamp@sha256:fb1a3ced00cecfc1f83f18ab5cd14199e30adc1b49aa4244f5d65ad3f5feb2a5 Image: jocatalin/kubernetes-bootcamp:v2 Image ID: docker-pullable://jocatalin/kubernetes-bootcamp@sha256:fb1a3ced00cecfc1f83f18ab5cd14199e30adc1b49aa4244f5d65ad3f5feb2a5 | cs |
앱이 버전 2로 동작하고 있다.(이미지 필드를 확인한다.)
Step 3: Rollback an update
다른 업데이트를 수행하고 v10으로 이미지 태킹하여 배포를 한다.
1 2 | root@zerobig-vm:~# kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=gcr.io/google-samples/kubernetes-bootcamp:v10 deployment.extensions/kubernetes-bootcamp image updated | cs |
디플로이먼트 상태를 보기 위해 "get deployments" 를 이용한다.
1 2 3 4 5 6 7 8 9 | root@zerobig-vm:~# kubectl get deployments NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE kubernetes-bootcamp 2 3 1 2 21h root@zerobig-vm:~# root@zerobig-vm:~# kubectl get pods NAME READY STATUS RESTARTS AGE kubernetes-bootcamp-5f76cd7b94-9t56c 0/1 ImagePullBackOff 0 29s kubernetes-bootcamp-7799cbcb86-6hfvm 1/1 Running 0 11m kubernetes-bootcamp-7799cbcb86-lxht5 1/1 Running 0 11m | cs |
그런데 무언가 잘못 되었다. 우리가 원했던 수의 파드가 가용하지 않은 것이다. 다시 파드를 리스트 해보자.
1 2 3 4 5 | root@zerobig-vm:~# kubectl get pods NAME READY STATUS RESTARTS AGE kubernetes-bootcamp-5f76cd7b94-9t56c 0/1 ImagePullBackOff 0 29s kubernetes-bootcamp-7799cbcb86-6hfvm 1/1 Running 0 11m kubernetes-bootcamp-7799cbcb86-lxht5 1/1 Running 0 11m | cs |
파드에 대한 "describe" 명령은 더 자세한 정보를 제공해 준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | root@zerobig-vm:~# kubectl describe pods Name: kubernetes-bootcamp-5f76cd7b94-9t56c Namespace: default Node: minikube/10.0.2.15 Start Time: Thu, 09 Aug 2018 22:26:42 +0900 Labels: pod-template-hash=1932783650 run=kubernetes-bootcamp Annotations: <none> Status: Pending IP: 172.17.0.5 Controlled By: ReplicaSet/kubernetes-bootcamp-5f76cd7b94 Containers: kubernetes-bootcamp: Container ID: Image: gcr.io/google-samples/kubernetes-bootcamp:v10 Image ID: Port: 8080/TCP Host Port: 0/TCP State: Waiting Reason: ImagePullBackOff Ready: False Restart Count: 0 Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from default-token-7d4h2 (ro) Conditions: Type Status Initialized True Ready False PodScheduled True Volumes: default-token-7d4h2: Type: Secret (a volume populated by a Secret) SecretName: default-token-7d4h2 Optional: false QoS Class: BestEffort Node-Selectors: <none> Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s node.kubernetes.io/unreachable:NoExecute for 300s Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 1m default-scheduler Successfully assigned kubernetes-bootcamp-5f76cd7b94-9t56c to minikube Normal SuccessfulMountVolume 1m kubelet, minikube MountVolume.SetUp succeeded for volume "default-token-7d4h2" Normal BackOff 25s (x2 over 58s) kubelet, minikube Back-off pulling image "gcr.io/google-samples/kubernetes-bootcamp:v10" Warning Failed 25s (x2 over 58s) kubelet, minikube Error: ImagePullBackOff Normal Pulling 14s (x3 over 1m) kubelet, minikube pulling image "gcr.io/google-samples/kubernetes-bootcamp:v10" Warning Failed 12s (x3 over 59s) kubelet, minikube Failed to pull image "gcr.io/google-samples/kubernetes-bootcamp:v10": rpc error: code = Unknown desc = Error response from daemon: manifest for gcr.io/google-samples/kubernetes-bootcamp:v10 not found Warning Failed 12s (x3 over 59s) kubelet, minikube Error: ErrImagePull ... | cs |
<이하 생략>
레파지토리에 v10이란 이미지가 없다. 바로 이전 정상동작 버전으로 롤백을 시행하자. "rollout undo" 명령을 이용할 것이다.
1 2 | root@zerobig-vm:~# kubectl rollout undo deployments/kubernetes-bootcamp deployment.extensions/kubernetes-bootcamp | cs |
rollout 명령은 이전에 알고 있던(known) state (v2의 이미지) 로 디플로이먼트를 되돌렸다. 업데이트들은 버전으로 관리되어지고, 여러분은 디플로이먼트에 대해 이전의 알고 있던 어떠한 상태로도 되돌릴 수 있다. 다시 파드를 리스트 해본다.
1 2 3 4 | root@zerobig-vm:~# kubectl get pods NAME READY STATUS RESTARTS AGE kubernetes-bootcamp-7799cbcb86-6hfvm 1/1 Running 0 13m kubernetes-bootcamp-7799cbcb86-lxht5 1/1 Running 0 13m | cs |
4개의 파드가 동작 중이다.(참고로 이전 모듈에서 스케일 다운 하여 2개로 줄여 둔 상태이므로 파드는 2개가 맞다.) 다시 파드에 배포된 이미지를 확인해 본다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | root@zerobig-vm:~# kubectl describe pods Name: kubernetes-bootcamp-7799cbcb86-6hfvm Namespace: default Node: minikube/10.0.2.15 Start Time: Thu, 09 Aug 2018 22:15:31 +0900 Labels: pod-template-hash=3355767642 run=kubernetes-bootcamp Annotations: <none> Status: Running IP: 172.17.0.9 Controlled By: ReplicaSet/kubernetes-bootcamp-7799cbcb86 Containers: kubernetes-bootcamp: Container ID: docker://436e6598baf5ad41c4f71940692a847361d0ea90ee340bb0a39ef88727a298a1 Image: jocatalin/kubernetes-bootcamp:v2 Image ID: docker-pullable://jocatalin/kubernetes-bootcamp@sha256:fb1a3ced00cecfc1f83f18ab5cd14199e30adc1b49aa4244f5d65ad3f5feb2a5 Port: 8080/TCP Host Port: 0/TCP State: Running Started: Thu, 09 Aug 2018 22:15:33 +0900 Ready: True Restart Count: 0 Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from default-token-7d4h2 (ro) Conditions: Type Status Initialized True Ready True PodScheduled True Volumes: default-token-7d4h2: Type: Secret (a volume populated by a Secret) SecretName: default-token-7d4h2 Optional: false QoS Class: BestEffort Node-Selectors: <none> Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s node.kubernetes.io/unreachable:NoExecute for 300s Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 13m default-scheduler Successfully assigned kubernetes-bootcamp-7799cbcb86-6hfvm to minikube Normal SuccessfulMountVolume 13m kubelet, minikube MountVolume.SetUp succeeded for volume "default-token-7d4h2" Normal Pulled 13m kubelet, minikube Container image "jocatalin/kubernetes-bootcamp:v2" already present on machine Normal Created 13m kubelet, minikube Created container Normal Started 13m kubelet, minikube Started container Name: kubernetes-bootcamp-7799cbcb86-lxht5 Namespace: default Node: minikube/10.0.2.15 Start Time: Thu, 09 Aug 2018 22:15:24 +0900 Labels: pod-template-hash=3355767642 run=kubernetes-bootcamp Annotations: <none> Status: Running IP: 172.17.0.7 Controlled By: ReplicaSet/kubernetes-bootcamp-7799cbcb86 Containers: kubernetes-bootcamp: Container ID: docker://f592eb99ffcd20a0209df40847469c883eb79c2e13d0c772191922e81ec6c520 Image: jocatalin/kubernetes-bootcamp:v2 Image ID: docker-pullable://jocatalin/kubernetes-bootcamp@sha256:fb1a3ced00cecfc1f83f18ab5cd14199e30adc1b49aa4244f5d65ad3f5feb2a5 Port: 8080/TCP Host Port: 0/TCP State: Running Started: Thu, 09 Aug 2018 22:15:30 +0900 Ready: True Restart Count: 0 Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from default-token-7d4h2 (ro) Conditions: Type Status Initialized True Ready True PodScheduled True Volumes: default-token-7d4h2: Type: Secret (a volume populated by a Secret) SecretName: default-token-7d4h2 Optional: false QoS Class: BestEffort Node-Selectors: <none> Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s node.kubernetes.io/unreachable:NoExecute for 300s Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 13m default-scheduler Successfully assigned kubernetes-bootcamp-7799cbcb86-lxht5 to minikube Normal SuccessfulMountVolume 13m kubelet, minikube MountVolume.SetUp succeeded for volume "default-token-7d4h2" Normal Pulling 13m kubelet, minikube pulling image "jocatalin/kubernetes-bootcamp:v2" Normal Pulled 13m kubelet, minikube Successfully pulled image "jocatalin/kubernetes-bootcamp:v2" Normal Created 13m kubelet, minikube Created container Normal Started 13m kubelet, minikube Started container | cs |
디플로이먼트는 안정적인 앱 버전(v2) 을 이용하고 있다. 롤백이 성공적으로 이루어 진 것이다.