https://kubernetes.io/ko/docs/concepts/workloads/pods/disruptions/
파드는 다음과 같은 경우가 아니면 사라지지 않는다.
그렇담 "불가피한" 오류란 무엇을 의미하는 걸까? 🤔
아래에 그 예시가 몇 가지 있고, 이런 경우 비자발적 중단이 일어날 것이다.
자발적 중단은 다음과 같다.
비자발적 중단으로 인한 영향을 감소시키기 위한 몇 가지 방법을 먼저 살펴보자
쿠버네티스는 자발적 중단이 자주 발생하는 경우에도 고가용성 어플리케이션을 실행할 수 있도록 도움을 주고 있다.
어플리케이션 소유자는 각 어플리케이션에 대해 PodDisruptionBudget(PDB)를 만들 수 있다. 이는 자발적 중단으로인해 일시에 중지되는 (복제된) 어플리케이션 파드의 수를 제한한다. quorum 기반의 어플리케이션은 실행 중인 레플리카의 수가 quorum 이하로 떨어지지 않도록 한다. 웹 프런트엔드는 부하를 처리하는 레플리카의 수가 일정 비율 이하로 떨어지지 않도록 보장할 수 있다.
따라서 클러스터 관리자와 호스팅 공급자는 직접적으로 파드나 Deployment를 제거하는 대신 PDB를 준수하는 도구(Eviction API)를 사용해야한다.
kubectl drain
명령을 사용하면 노드를 서비스 중단으로 표시 할 수 있는데, 이 때 Eviction API는 사용자가 서비스를 중단시키려는 노드의 모든 파드를 제거하려고 한다. kubectl
은 사용자를 대신해 제출되는 제거 요청을 거부 할 수 있으므로 Eviction API는 대상 노드의 모든 파드가 종료되거나 타임아웃 될 때까지 실패한 요청에 대해 주기적으로 재시도한다.
PDB는 어플리케이션이 필요로하는 레플리카 수에 상대적으로, 용인 가능한 레플리카 수를 지정한다. 예를 들어,.spec.replicas: 5
를 갖는 Deployment는 어느 시점에든 5개의 파드를 유지해야한다. 해당 Deployment의 PDB가 특정 시점에 파드 4개를 허용한다면, Eviction API는 한 번에 1(spec-PDB)개의 파드에 대한 자발적 중단을 허용한다.
파드가 의도한 수량은 워크로드 리소스의 .spec.replicas
를 기반으로 계산되며, 컨트롤 플레인은 파드의 .metadata.ownerReferences
를 기반으로 소유하는 워크로드 리소스를 찾아낸다.
PDB는 비자발적 중단이 발생하는 것을 막을 수는 없지만, Budget은 차감된다.
어플리케이션의 롤링 업그레이드로 파드가 삭제되거나 사용 불가 상태가 되는 경우 중단 Budget에 영향을 주나, 워크로드 리소스(Deployment, StatefulSet 등)는 롤링 업데이트 시 PDB의 제한을 받지않는다. 단, 어플리케이션 업데이트 중 실패 처리는 특정 워크로드 리소스에 대한 명세가 정의한다.
Eviction API를 사용해 파드를 제거하는 경우, PodSpec의 termicationGracePeriodSeconds
설정을 준수하여 정상적으로 종료된 상태가 된다.
현재 노드는 3개가 존재한다.
클러스터에서는 여러 어플리케이션을 실행하고 있는데, 이 중 하나는 3개의 레플리카를 가진다.
pod-x
라는 PDB와 무관한 파드도 존재한다.
초기에 파드는 다음처럼 배치된다.
node-1 | node-2 | node-3 |
---|---|---|
pod-a available | pod-b available | pod-c available |
pod-x available |
PDB는 다음처럼 정의되어 있다.
클러스터 관리자가 어떤 작업을 위해 node-1
을 kubectl drain
명령을 사용해 비우려고 한다. 이에 따라 kubectl
은 pod-a
와 pod-x
를 제거하려 할 것이다.
이는 즉시 성공할 것이고, 두 파드는 동시에 terminating
상태로 진입한다.
node-1 draining | node-2 | node-3 |
---|---|---|
pod-a terminating | pod-b available | pod-c available |
pod-x terminating |
Deployment는 (a, b, c) 세 개의 파드 중 하나가 중지(pod-a
)됨을 알게되고 pod-d
라는 대체 파드를 생성하나 node-1
이 차단되어 있기에 다른 노드에 위치시킨다.
pod-x
의 대체 파드인 pod-y
또한 생성된다.
node-1 draining | node-2 | node-3 |
---|---|---|
pod-a terminating | pod-b available | pod-c available |
pod-x terminating | pod-d starting | pod-y |
pod-a
와 pod-x
가 완전히 종료되는 순간 클러스터는 다음 상태가 된다.
node-1 drained | node-2 | node-3 |
---|---|---|
pod-b available | pod-c available | |
pod-d starting | pod-y |
pod-d
가 아직 available
한 상태가 아닌 경우 node-2
혹은 node-3
를 비우려고하면 Deployment에 available
한 파드가 2개이기 때문에 PDB에 의해 drain 명령은 차단된다. (PDB는 최소 2개의 pod를 유지하기로 했기 때문이다.)
이어서 pod-d
가 available 상태가 되었다고 치자.
node-1 drained | node-2 | node-3 |
---|---|---|
pod-b available | pod-c available | |
pod-d available | pod-y |
클러스터 관리자는 node-1
에 이어 node-2
도 비우고자 한다.
kubectl drain
명령에 의해 pod-b
와 pod-d
를 제거하려하나 (둘 중 어떤 순서에 의해서든) 하나가 제거(pod-b
가 제거되었다고 치자.)되면 pod는 2개가 남고, 남은 하나(pod-d
)를 더 제거하려하는 순간 이는 거부된다.
따라서 Deployment는 pod-d
를 대체한 pod-e
를 생성한다.
클러스터에 pod-e
를 스케줄하기 위한 충분한 리소스가 없으므로 braining 명령은 차단된다. 따라서 최종적으로 클러스터는 다음과 같은 상태로 끝나게 된다.
node-1 drained | node-2 | node-3 | no node |
---|---|---|---|
pod-b terminating | pod-c available | pod-e pending | |
pod-d available | pod-y |
이 시점에서 클러스터 관리자는 노드를 추가해야(pod-e를 available 상태로 만들어야한다.) 원하는 작업을 이어갈 수 있을 것이다.
쿠버네티스에 중단이 발생할 수 있는 비율을 변화시키는 요소는 다음과 같은 것들이 있다.
둘은 서로에 대한 지식이 부족한 별도 역할로 생각하는 것이 유용하다. 그 이유는 다음과 같은 경우에 타당하다.
PDB는 역할 분리에 따라 알맞는 인터페이스를 제공한다.
만약 조직에 역할 분리에 따른 책임의 분리가 없다면 PDB를 사용할 필요가 없다.
클러스터 전체 노드에 대해 노드 혹은 시스템 소프트웨어 업그레이드로 인한 중단이 발생할 수 있는 작업을 수행하는 경우 다음과 같은 옵션을 선택한다.
이 컨테이너 유형은 트러블 슈팅 등과 같은 작업을 위해 기존 파드에서 임시적으로 실행된다.
사용자는 어플리케이션 빌드보다는 서비스 점검 시에 임시 컨테이너를 사용한다.❗️ 주의
임시 컨테이너는 초기의 alpha 상태이며, Production 컨테이너에는 적합하지 않다. 쿠버네티스 사용 중단 정책에 따라 이 alpha 기능은 향후 크게 변경되거나 완전히 제거될 가능성이 있다.
파드는 일회용이고, 교체 가능한 것으로 의도되었으므로 사용자는 파드가 한 번 생성된 이후에는 컨테이너를 추가할 수 없다. 대신, 사용자는 보통 Deployment를 사용해 제어하는 방식으로 파드를 삭제하고 교체한다.
그러나 때때로 재현하기 어려운 버그의 문제 해결을 위해 기존 파드의 상태를 검사해야 할 수 있다. 이 경우 사용자는 기존 파드에서 임시컨테이너를 실행해 상태를 검사하고, 임의의 명령을 실행할 수 있다.
✔️ 임시 컨테이너는 리소스/실행에 대한 보증이 없다는 점에서 실 컨테이너와 다르며, 결코 자동으로 재시작되지 않는다. 그래서 (당연히) 어플리케이션을 만드는데 적합하지 않다.
임시 컨테이너 또한 일반 컨테이너와 동일하게 ContainerSpec
으로 구성하지만, 많은 필드가 호환되지 않으며 허용되지 않는다.
ports
, livenessProbe
, readinessProbe
와 같은 필드가 허용되지 않음resources
설정이 허용되지 않음또한, pod.spec
에 직접 추가하는 대신 특별한 API인 ephemeralcontainers
핸들러를 사용해 만들어지므로 kubectl edit
을 사용해 추가할 수 없다.
일반 컨테이너와 동일하게 사용자는 임시 컨테이너를 파드에 추가한 이후에 변경 혹은 제거 할 수 없다.
컨테이너가 충돌되거나 이미 배포된 컨테이너에 디버깅 도구가 포함되지 않은 이유로 kubectl exec
이 불충분할 때 문제 해결에 유용하다.
예를들어, distroless 이미지를 사용해 attack surface, 버그 및 취약점 노출을 줄이는 최소한의 컨테이너 이미지 배포가 가능하나 해당 이미지는 셸이나 다른 디버깅 도구를 포함하지 않으므로 kubectl exec
만으로 문제 해결이 어려울 때도 사용 가능하다.
임시 컨테이너를 사용 시 프로세스 네임스페이스 공유를 활성화하여 다른 컨테이너 내부의 프로세스를 확인할 수도 있다.
일반적으로 API를 직접 호출하지 않고 kubectl debug
이나 다른 kubectl
플러그인을 사용해 이런 단계들을 자동화한다.
임시 컨테이너는 파드의 ephemeralcontainers
하위 리소스를 사용해 생성되며, kubectl --raw
를 사용해서 볼 수 있다.
{
"apiVersion": "v1",
"kind": "EphemeralContainers",
"metadata": {
"name": "example-pod"
},
"ephemeralContainers": [{
"command": [
"sh"
],
"image": "busybox",
"imagePullPolicy": "IfNotPresent",
"name": "debugger",
"stdin": true,
"tty": true,
"terminationMessagePolicy": "File"
}]
}
이미 실행중인 example-pod
에 임시 컨테이너를 업데이트 한다.
> kubectl replace --raw /api/v1/namespaces/default/pods/example-pod/ephemeralcontainers -f ec.json
# 반환된 새로운 임시컨테이너 목록
{
"kind":"EphemeralContainers",
"apiVersion":"v1",
"metadata":{
"name":"example-pod",
"namespace":"default",
"selfLink":"/api/v1/namespaces/default/pods/example-pod/ephemeralcontainers",
"uid":"a14a6d9b-62f2-4119-9d8e-e2ed6bc3a47c",
"resourceVersion":"15886",
"creationTimestamp":"2019-08-29T06:41:42Z"
},
"ephemeralContainers":[
{
"name":"debugger",
"image":"busybox",
"command":[
"sh"
],
"resources":{
},
"terminationMessagePolicy":"File",
"imagePullPolicy":"IfNotPresent",
"stdin":true,
"tty":true
}
]
}
kubectl describe
를 사용해 임시 컨테이너의 상태를 볼 수 있다.
> kubectl describe pod example-pod
...
Ephemeral Containers:
debugger:
Container ID: docker://cf81908f149e7e9213d3c3644eda55c72efaff67652a2685c1146f0ce151e80f
Image: busybox
Image ID: docker-pullable://busybox@sha256:9f1003c480699be56815db0f8146ad2e22efea85129b5b5983d0e0fb52d9ab70
Port: <none>
Host Port: <none>
Command:
sh
State: Running
Started: Thu, 29 Aug 2019 06:42:21 +0000
Ready: False
Restart Count: 0
Environment: <none>
Mounts: <none>
...
kubectl attach
, kubectl exec
, kubectl logs
를 사용해서 다른 컨테이너와 같은 방식으로 임시 컨테이너와 상호작용할 수 있다.
> kubectl attach -it example-pod -c debugger