kubernetes: Deployment
여기서는 Kubernetes의 컨트롤러 중 하나인 Deployment에 대해서 이런저런 간단한 이야기를 해 보자. 자세한 내용을 쓰려면 아예 따로 써야 할 것 같다.
Deployment: 배포를 잘 할수 있도록 하는 컨트롤러
A Deployment provides declarative updates for Pods and ReplicaSets.
공식 페이지에서는 위와 같은 말로 Deployment를 설명하고 있는데, 이게 무슨 의미인지 조금 더 알아보는 것이 좋겠다.
우선 답부터 말하면 Deployment
는 replicaset에 비해 배포와 관련된 편의성을 제공했다고 보면 된다. 그래서 이런 배포에 관련한 기능을 어떻게 제공하는지 한번 확인해 보자.
Deployment의 관리 대상: ReplicaSet
먼저 간단한 Deployment
를 다시 한 번 생성해 보자. 내용은 아래와 같다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 6
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
이를 생성하면,
$ kubectl create -f deployment.yaml
deployment.apps/nginx-deployment created
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-f89759699-2cbl4 0/1 ContainerCreating 0 2s
nginx-deployment-f89759699-hsk6w 0/1 ContainerCreating 0 2s
nginx-deployment-f89759699-7vr75 0/1 ContainerCreating 0 2s
nginx-deployment-f89759699-smcgr 0/1 ContainerCreating 0 2s
nginx-deployment-f89759699-f2r6j 0/1 ContainerCreating 0 2s
nginx-deployment-f89759699-p98dh 0/1 ContainerCreating 0 2s
6개의 replicas
를 가지는 nginx-deployment
라는 deployment를 생성하였다. 이름을 보면 nginx-deployment
이외에 f89759699
라는 이름이 추가로 붙었다는 것을 확인할 수 있다. 뒤의 5자리 문자열이야 generateName
으로 붙었을 테니. 그럼 f89759699
는 어디서 왔는가?
$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
nginx-deployment-f89759699 6 6 6 87s
replicaset의 이름에 해당 스트링이 붙어 있다. 이게 그런데 왜 붙어있을까? 왜냐 하면 replicaset에서 팟을 라벨과 셀렉터를 통해 관리하고 있으므로, 마찬가지로 Deployment 또한 라벨과 셀렉터로 관리하면 되는 것이 아닐까? 왜 굳이 이름을 새로 추가했을까?
첫 번째로 관리 대상을 명확히 파악해 보자. deployment의 spec.selector.matchLabels
는 app: nginx
로 되어 있는데, replicaset의 metadata.labels
에서 app: nginx2
로 변경하면 다음과 같은 광경을 확인할 수 있다.
# replicaset의 라벨을 "app: nginx2"로 변경하였다.
$ kubectl edit rs nginx-deployment-f89759699
replicaset.apps/nginx-deployment-f89759699 edited
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-f89759699-7vr75 1/1 Running 0 4m4s
nginx-deployment-f89759699-hsk6w 1/1 Running 0 4m4s
nginx-deployment-f89759699-f2r6j 1/1 Running 0 4m4s
nginx-deployment-f89759699-2cbl4 1/1 Running 0 4m4s
nginx-deployment-f89759699-smcgr 1/1 Running 0 4m4s
nginx-deployment-f89759699-p98dh 1/1 Running 0 4m4s
nginx-deployment-6f657c7746-574c8 0/1 ContainerCreating 0 2s
nginx-deployment-6f657c7746-5kk5n 0/1 ContainerCreating 0 2s
nginx-deployment-6f657c7746-sgm9t 0/1 ContainerCreating 0 2s
nginx-deployment-6f657c7746-krfkj 0/1 ContainerCreating 0 2s
nginx-deployment-6f657c7746-z8bpc 0/1 ContainerCreating 0 2s
nginx-deployment-6f657c7746-xh54c 0/1 ContainerCreating 0 2s
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-f89759699 6 6 6 4m49s
nginx-deployment-6f657c7746 6 6 6 47s
앗, 라벨을 변경했더니 새로운 팟이 등장하였다. 거기다 replicaset을 조회해 보니 하나의 replicaset이 더 뜬 것을 확인할 수 있다. 그러니까, deployment는 replicaset을 관리하는 것이고, 라벨을 변경했더니 변경된 라벨의 replicaset이 없으므로 이를 생성한 것이다. 거기다가 새로 생성된 replicaset은 자신이 관리하는 팟이 없었으므로, replicas만큼 다시 팟을 생성했다고 봐도 되겠다.
즉 deployment는 단순히 replicaset을 관리하는 주체라고 봐도 되겠다.
Deployment의 업데이트 방식: ReplicaSet의 교체
그러면 이번에는 라벨 이외에 deployment의 정보를 변경해 보자. nginx의 버전을 변경해 보려고 한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 6
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14
# image를 nginx에서 nginx:1.14로 변경하였다.
그리고 이를 실행하면,
$ kubectl apply -f deployment.yaml
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
deployment.apps/nginx-deployment configured
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-6f657c7746-sgm9t 1/1 Running 0 17m
nginx-deployment-6f657c7746-xh54c 1/1 Running 0 17m
nginx-deployment-6f657c7746-5kk5n 1/1 Running 0 17m
nginx-deployment-6f657c7746-krfkj 1/1 Running 0 17m
nginx-deployment-6f657c7746-z8bpc 1/1 Running 0 17m
nginx-deployment-7bcc8c844-x757g 0/1 ContainerCreating 0 3s
nginx-deployment-7bcc8c844-8lgzb 0/1 ContainerCreating 0 3s
nginx-deployment-7bcc8c844-dpj6j 0/1 ContainerCreating 0 3s
nginx-deployment-6f657c7746-574c8 0/1 Terminating 0 17m
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-6f657c7746 5 5 5 17m
nginx-deployment-7bcc8c844 3 3 0 5s
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-6f657c7746-sgm9t 1/1 Running 0 17m
nginx-deployment-6f657c7746-z8bpc 1/1 Running 0 17m
nginx-deployment-7bcc8c844-x757g 1/1 Running 0 10s
nginx-deployment-7bcc8c844-8lgzb 1/1 Running 0 10s
nginx-deployment-7bcc8c844-dpj6j 1/1 Running 0 10s
nginx-deployment-7bcc8c844-9cttr 0/1 ContainerCreating 0 3s
nginx-deployment-7bcc8c844-qllp8 0/1 ContainerCreating 0 3s
nginx-deployment-7bcc8c844-28sxd 0/1 ContainerCreating 0 2s
nginx-deployment-6f657c7746-krfkj 0/1 Terminating 0 17m
nginx-deployment-6f657c7746-5kk5n 0/1 Terminating 0 17m
nginx-deployment-6f657c7746-xh54c 0/1 Terminating 0 17m
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-6f657c7746 2 2 2 17m
nginx-deployment-7bcc8c844 6 6 3 12s
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-7bcc8c844-x757g 1/1 Running 0 21s
nginx-deployment-7bcc8c844-8lgzb 1/1 Running 0 21s
nginx-deployment-7bcc8c844-dpj6j 1/1 Running 0 21s
nginx-deployment-7bcc8c844-9cttr 1/1 Running 0 14s
nginx-deployment-7bcc8c844-qllp8 1/1 Running 0 14s
nginx-deployment-7bcc8c844-28sxd 1/1 Running 0 13s
nginx-deployment-6f657c7746-z8bpc 0/1 Terminating 0 18m
nginx-deployment-6f657c7746-sgm9t 0/1 Terminating 0 18m
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-7bcc8c844 6 6 6 23s
nginx-deployment-6f657c7746 0 0 0 18m
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-7bcc8c844-x757g 1/1 Running 0 28s
nginx-deployment-7bcc8c844-8lgzb 1/1 Running 0 28s
nginx-deployment-7bcc8c844-dpj6j 1/1 Running 0 28s
nginx-deployment-7bcc8c844-9cttr 1/1 Running 0 21s
nginx-deployment-7bcc8c844-qllp8 1/1 Running 0 21s
nginx-deployment-7bcc8c844-28sxd 1/1 Running 0 20s
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-7bcc8c844 6 6 6 29s
nginx-deployment-6f657c7746 0 0 0 18m
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-7bcc8c844-x757g 1/1 Running 0 35s
nginx-deployment-7bcc8c844-8lgzb 1/1 Running 0 35s
nginx-deployment-7bcc8c844-dpj6j 1/1 Running 0 35s
nginx-deployment-7bcc8c844-9cttr 1/1 Running 0 28s
nginx-deployment-7bcc8c844-qllp8 1/1 Running 0 28s
nginx-deployment-7bcc8c844-28sxd 1/1 Running 0 27s
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-7bcc8c844 6 6 6 37s
nginx-deployment-6f657c7746 0 0 0 18m
앗, 갑자기 수많은 팟이 생성되었다! 그런데 잘 보면 replicaset이 추가되었으므로, replicaset이 관리할 새로운 팟이 생성되었다고 볼 수 있다. 거기다 지속적으로 get을 찍어 보니 기존 nginx-deployment-6f657c7746
에서 생성한 팟은 숫자가 줄어들고 있고, 새로운 nginx-deployment-7bcc8c844
에서 관리하는 팟의 숫자가 늘어나는 것을 볼 수 있다. 거기에 추가적으로, 기존 replicaset은 지워지지 않고 단지 DESIRED
가 0으로 바뀐 채로 남아 있는 것을 확인할 수 있다.
이런 시나리오를 생각해 보면 다음과 같이 사용할 수 있음을 알 수 있다. 만약 어떤 어플리케이션의 버전이 변경된 경우, Deployment
에 변경된 내용을 기록하기만 해도 업데이트가 된다는 말이다. 특히 업데이트는 위에서 봤듯 한 번에 진행되는 것이 아니라, 새로운 요구사항에 맞는 replicaset을 생성한 후 기존 팟을 지워나감과 동시에 새로운 팟을 생성해 나간다. 따라서 모든 팟이 꺼지는 상황이 생기지는 않게 되는 것이다. 즉, Deployment는 replicaset을 우아하게 교체하여 서비스에 지장이 없는 업데이트를 진행한다.
한 가지 더 이야기할 것이 있다면, 이런 업데이트는 spec.template
이 변경될 때만 가능하다. 즉 metadata
같은 것들을 변경해 봤자 전혀 소용이 없다는 것. 생각해 보면 당연한 것이, Deployment의 관리 대상이 업데이트되는 것이므로 spec을 고쳐야 하는 것이 당연하기도 하다. 그래서 보통 강제 업데이트를 진행할 때는 보통 template에 이런저런 값들을 넣어 업데이트하는 식으로 진행한다. 예를 들면 이런 식이다.
$ kubectl patch deployment nginx-deployment -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"date\":\"`date +'%s'`\"}}}}}"
deployment.apps/nginx-deployment patched
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-6f657c7746 0 0 0 36m
nginx-deployment-7bcc8c844 0 0 0 18m
nginx-deployment-949db48d4 6 6 6 14s
왜 이전 replicaset은 지워지지 않는가
kubectl rollout
이라는 명령어가 있다.
$ kubectl rollout
Manage the rollout of a resource.
Valid resource types include:
* deployments
* daemonsets
* statefulsets
Examples:
# Rollback to the previous deployment
kubectl rollout undo deployment/abc
...
...
rollout
명령어를 통해 이전 버전의 deployment로 되돌아갈 수 있다. 그래서 이걸 한번 간단히 실행해 보면,
$ kubectl rollout history deployment/nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
1 <none>
2 <none>
3 <none>
우선 위의 강제 업데이트까지 해서 3번의 업데이트가 있었다는 것을 알 수 있다. 그럼 실제로 이전 버전으로 돌리려면,
$ kubectl rollout undo deployment/nginx-deployment
deployment.apps/nginx-deployment rolled back
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-949db48d4-v8bdd 1/1 Running 0 6m36s
nginx-deployment-949db48d4-5qtzn 1/1 Running 0 6m36s
nginx-deployment-949db48d4-78fxf 1/1 Running 0 6m36s
nginx-deployment-949db48d4-kwvb6 1/1 Running 0 6m29s
nginx-deployment-949db48d4-mvjwm 1/1 Running 0 6m30s
nginx-deployment-949db48d4-v2hl9 1/1 Terminating 0 6m29s
nginx-deployment-7bcc8c844-btwt9 0/1 ContainerCreating 0 2s
nginx-deployment-7bcc8c844-phpjv 0/1 ContainerCreating 0 2s
nginx-deployment-7bcc8c844-jn55n 0/1 ContainerCreating 0 1s
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-6f657c7746 0 0 0 42m
nginx-deployment-949db48d4 5 5 5 6m38s
nginx-deployment-7bcc8c844 3 3 0 25m
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-6f657c7746 0 0 0 42m
nginx-deployment-949db48d4 2 2 2 6m45s
nginx-deployment-7bcc8c844 6 6 3 25m
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-6f657c7746 0 0 0 42m
nginx-deployment-7bcc8c844 6 6 4 25m
nginx-deployment-949db48d4 1 1 1 6m52s
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-6f657c7746 0 0 0 43m
nginx-deployment-7bcc8c844 6 6 5 25m
nginx-deployment-949db48d4 0 0 0 7m
kubectl rollout undo
를 통해 되돌아갈 수 있다. 되돌아가는 방법은 별게 아니고, 현재의 replicaset의 수를 줄이면서, DESIRED 0인 상태로 유지되던 replicaset의 수를 그냥 늘리는 것 뿐이다. 마찬가지로 우아하게 전환할 뿐. --revision
을 통해 특정 버전을 선택하여 업데이트할 수 있으므로 이것도 알아두면 좋겠다.
undo 후에는 새로운 리비전 번호를 받는다. 3번 리비전에서 undo를 통해 2번 리비전으로 이동했다고 해서 현재 리비전이 2로 바뀌는 것은 아니고, 새로운 리비전 4를 받는다는 것이다.
$ kubectl rollout history deployment/nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
1 <none>
3 <none>
4 <none>
$ kubectl describe deployment nginx-deployment
Name: nginx-deployment
Namespace: playground
CreationTimestamp: Sun, 21 Jun 2020 13:37:12 +0900
Labels: app=nginx
Annotations: deployment.kubernetes.io/revision: 4
kubectl.kubernetes.io/last-applied-configuration:
...
...
...
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-6f657c7746 0 0 0 46m
nginx-deployment-949db48d4 0 0 0 10m
nginx-deployment-7bcc8c844 6 6 6 29m
2번 리비전이 없어지고 새로운 4번 리비전이 생긴 것을 볼 수 있다. 따라서 redo
는 없다.