DevOps/Kubernetes

[Kubernetes] Cordon & Drain

TTOII 2022. 6. 6. 13:20
728x90

🚀 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 해줘야한다.

728x90