[Kubernetes] Cordon & Drain
🚀 Cordon & Drain
🚀 Cordon
스케줄링 금지
kubectl cordon <NODENAME>
스케줄링 허용
kubectl uncordon <NODENAME>
vagrant@k8s-node1 ~/schedule/affinity kubectl get nodes
NAME STATUS ROLES AGE VERSION
node1 Ready control-plane,master 10d v1.22.8
node2 Ready <none> 10d v1.22.8
node3 Ready <none> 7d23h v1.22.8
node의 STATUS
를 주의깊게 보자
vagrant@k8s-node1 ~/schedule/affinity kubectl cordon node2
node/node2 cordoned
node2에 cordon
을 걸었다.
vagrant@k8s-node1 ~/schedule/affinity kubectl get nodes
NAME STATUS ROLES AGE VERSION
node1 Ready control-plane,master 10d v1.22.8
node2 Ready,SchedulingDisabled <none> 10d v1.22.8
node3 Ready <none> 7d23h v1.22.8
상태에 SchedulingDisabled
가 추가됐다. 이제부터 스케줄러가 해당 노드에 배치시키지 않는다.
해당 노드에 이미 존재하는 파드들은 무관하며 새롭게 생성되는 파드부터 적용된다.
vagrant@k8s-node1 ~/schedule/affinity kubectl create -f myweb-a.yaml
replicaset.apps/myweb-a created
vagrant@k8s-node1 ~/schedule/affinity kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myweb-a-5dqcz 1/1 Running 0 7s 10.233.90.103 node1 <none> <none>
myweb-a-6j4xt 1/1 Running 0 7s 10.233.92.162 node3 <none> <none>
nfs-client-provisioner-758f8cd4d6-wpjbt 1/1 Running 0 2d22h 10.233.92.110 node3 <none> <none>
node에 cordon
이 걸린 상태에서 새로운 파드를 생성해보았다.
복제본이 2개이며 antiAffinity
정책과 SchedulingDisabled
에 의해 node1, 3에 배치될 수 밖에 없다.
vagrant@k8s-node1 ~/schedule/affinity kubectl create -f myweb-b.yaml
replicaset.apps/myweb-b created
vagrant@k8s-node1 ~/schedule/affinity kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myweb-a-5dqcz 1/1 Running 0 94s 10.233.90.103 node1 <none> <none>
myweb-a-6j4xt 1/1 Running 0 94s 10.233.92.162 node3 <none> <none>
myweb-b-9rz7w 0/1 ContainerCreating 0 3s <none> node3 <none> <none>
myweb-b-wg8vv 1/1 Running 0 3s 10.233.90.104 node1 <none> <none>
nfs-client-provisioner-758f8cd4d6-wpjbt 1/1 Running 0 2d22h 10.233.92.110 node3 <none> <none>
myweb-b
또한 affinity
정책에 의해 myweb-a
와 같은 노드에 배치될 수 밖에 없다.
vagrant@k8s-node1 ~/schedule/affinity kubectl scale rs myweb-a --replicas=3
replicaset.apps/myweb-a scaled
vagrant@k8s-node1 ~/schedule/affinity kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myweb-a-5dqcz 1/1 Running 0 4m23s 10.233.90.103 node1 <none> <none>
myweb-a-6j4xt 1/1 Running 0 4m23s 10.233.92.162 node3 <none> <none>
myweb-a-rzxsf 0/1 Pending 0 4s <none> <none> <none> <none>
myweb-b-9rz7w 1/1 Running 0 2m52s 10.233.92.163 node3 <none> <none>
myweb-b-wg8vv 1/1 Running 0 2m52s 10.233.90.104 node1 <none> <none>
nfs-client-provisioner-758f8cd4d6-wpjbt 1/1 Running 0 2d22h 10.233.92.110 node3 <none> <none>
이때 복제본 개수를 3으로 늘리면 myweb-a
파드끼리는 antiAffinity
이기 때문에 마지막에 생성된 myweb-a-rzxsf
는 반드시 node2에 배치될 수 밖에 없다.
하지만 현재 node2는 스케줄링이 금지되어 있어 Pending
상태가 된다.
vagrant@k8s-node1 ~/schedule/affinity kubectl describe po myweb-a-rzxsf
Warning FailedScheduling 3m24s default-scheduler 0/3 nodes are available: 1 node(s) were unschedulable, 2 node(s) didn't match pod anti-affinity rules.
describe
명령을 통해 확인하면 1개의 노드는 스케줄링이 안되고 2개는 anti-affinity
때문에 안된다는 메세지가 출력된다.
즉, node1, 3은 anti-affinity
때문에 안되고 node2는 스케줄링이 금지된 것이다.
vagrant@k8s-node1 ~/schedule/affinity kubectl uncordon node2
node/node2 uncordoned
vagrant@k8s-node1 ~/schedule/affinity kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myweb-a-5dqcz 1/1 Running 0 9m52s 10.233.90.103 node1 <none> <none>
myweb-a-6j4xt 1/1 Running 0 9m52s 10.233.92.162 node3 <none> <none>
myweb-a-rzxsf 1/1 Running 0 5m33s 10.233.96.143 node2 <none> <none>
myweb-b-9rz7w 1/1 Running 0 8m21s 10.233.92.163 node3 <none> <none>
myweb-b-wg8vv 1/1 Running 0 8m21s 10.233.90.104 node1 <none> <none>
nfs-client-provisioner-758f8cd4d6-wpjbt 1/1 Running 0 2d22h 10.233.92.110 node3 <none> <none>
이제 uncordon
하면 스케줄링이 정상적으로 완료되어 node2에 배치되는 것을 볼 수 있다.
🚀 Drain
Cordon → 기존 파드를 제거
kubectl drain <NODENAME> --ignore-daemonsets
vagrant@k8s-node1 ~/schedule/affinity kubectl drain node2
node/node2 cordoned
DEPRECATED WARNING: Aborting the drain command in a list of nodes will be deprecated in v1.23.
The new behavior will make the drain command go through all nodes even if one or more nodes failed during the drain.
For now, users can try such experience via: --ignore-errors
error: unable to drain node "node2", aborting command...
There are pending nodes to be drained:
node2
error: cannot delete DaemonSet-managed Pods (use --ignore-daemonsets to ignore): ingress-nginx/ingress-nginx-controller-2jrhn, kube-system/calico-node-ft9lf, kube-system/kube-proxy-zmm5n, kube-system/nodelocaldns-mq9nm, metallb-system/speaker-fnpnb
There are pending nodes to be drained:
만약 노드가 3개 있는데 node2에 drain
을 걸면
복제본을 다루는 컨트롤러에 의해 만들어진 파드는 삭제되었다가 affinity
정책에 위반되지 않는다면 다른 노드에 생성된다.
이렇게 복제본을 제공하는 워크로드들은 아무 상관이 없으나,
데몬셋이나 파드들은 노드에서 쫓아내면 다른 노드로 갈 수가 없다.
따라서 출력된 에러를 해석해보면
데몬셋에 의해 관리되는 ingress-nginx/ingress-nginx-controller-2jrhn
,
kube-system/calico-node-ft9lf
, kube-system/kube-proxy-zmm5n
, kube-system/nodelocaldns-mq9nm
, metallb-system/speaker-fnpnb
와 같은 파드들은 해당 노드에서 drain
되면 다른 노드에서 생성되지 않아 삭제할 수 없다는 것이다.
이 문제를 무시하기 위해서는 --ignore-daemonsets
옵션을 사용한다.
kubectl drain node2 --ignore-daemonsets
해당 명령을 실행하면 node2의 모든 파드들이 evicting
하며 node2에 자동으로 cordon
이 걸린다.
따라서 drain
은 기존 파드들을 삭제하고 동시에 cordon
시켜버린다.
더이상 새로운 파드가 배치되면 안되기 때문이다.
node2에 패치를 하거나 커널 업데이트를 해야 할 때 안전하게 작업하기 위해서 drain
을 시켜주는 것이 좋다.
즉, RS, RC, Deploy, STS에 의해 생성되는 파드는 다른 노드에 재배치하고 그다음 패치하고 재부팅하는 것이 안전하다.
한가지 꼭 기억해야할 것은 drain
하게되면 자동으로 cordon
이 걸리는데 시스템을 재부팅해도 여전히 cordon
상태이다.
따라서 배치를 시키기 위해서는 반드시 uncordon
해줘야한다.