서버를 패치하거나 재부팅해야 할 때의 표준 순서다.
kubectl cordon <노드> # 새 파드가 배치되지 않게 한다
kubectl drain <노드> --ignore-daemonsets --delete-emptydir-data
# 작업 수행
kubectl uncordon <노드>
kubectl get nodes
cordon 은 스케줄만 막고 이미 도는 파드는 그대로 둔다. drain 은 파드를 내보내 다른 노드에서 다시 뜨게 한다. DaemonSet 파드는 노드마다 하나씩이라 옮길 수 없으므로 --ignore-daemonsets 가 필요하고, emptyDir 을 쓰는 파드는 그 데이터가 사라지는 것을 명시적으로 승인해야 하므로 --delete-emptydir-data 가 필요하다.
컨트롤러 없이 만든 파드가 있으면 drain 이 거부한다. 다시 만들어 줄 주체가 없어 사라지기 때문이다. 정말 지워도 되면 --force 를 준다.
PodDisruptionBudget 이 걸려 있으면 drain 은 그 조건을 지키며 기다린다. 레플리카가 하나인 워크로드에 PDB 로 minAvailable: 1 을 걸어 두면 영원히 멈추므로, 진행되지 않을 때는 PDB 를 확인한다.
kubectl get pdb -A
kubectl get pod -o wide --field-selector spec.nodeName=<노드>
노드 전체가 아니라 특정 워크로드만 멈출 때다.
kubectl scale deployment <이름> --replicas=0 -n <네임스페이스>
kubectl scale statefulset <이름> --replicas=0 -n <네임스페이스>
원래 레플리카 수를 기록해 두고 작업 뒤 되돌린다. Service 오브젝트는 남아 있지만 엔드포인트가 비어 사실상 중단 상태가 된다.
파드는 살려 둔 채 트래픽만 끊고 싶다면 셀렉터를 지워 엔드포인트를 비운다. 파드 안에서 계속 확인 작업을 해야 할 때 쓴다.
kubectl get svc <이름> -o yaml > svc-backup.yaml # 반드시 먼저 받아 둔다
kubectl patch svc <이름> -p '{"spec":{"selector":null}}'
복구는 받아 둔 정의를 다시 적용한다. kubectl delete endpoints 로 지우는 방법은 컨트롤러가 곧바로 다시 만들기 때문에 효과가 없다.
점검한다고 systemctl stop kubelet 부터 하지 않는다. 파드가 정상 종료 절차를 밟지 못하고, 컨트롤 플레인은 그 노드를 장애로 인식해 몇 분 뒤 파드를 다른 곳에 띄운다. 그 사이 노드에서는 컨테이너가 계속 돌고 있으므로 같은 서비스가 두 벌 도는 상태가 만들어진다.
순서는 언제나 cordon → drain → 작업 → uncordon 이다.