왜 노드 유지보수가 까다로운가

커널 패치, 인스턴스 타입 교체, 하드웨어 점검처럼 노드를 잠시 내려야 하는 순간은 반드시 온다. 문제는 노드 위에서 Pod가 돌고 있다는 점이다. 아무 조치 없이 노드를 재부팅하거나 삭제하면 그 위의 Pod는 SIGKILL로 강제 종료된다. 처리 중이던 요청은 끊기고, PodDisruptionBudget이나 롤링 전략도 무시된다. 그래서 스케줄링을 막고(cordon), 기존 Pod를 안전하게 다른 노드로 내보내는(drain) 두 단계가 표준 절차가 된다.

cordon과 drain의 역할 구분

둘은 자주 붙어 다니지만 하는 일이 다르다. cordon은 노드에 SchedulingDisabled 표시를 달아 새 Pod가 배치되지 않게만 한다. 이미 떠 있는 Pod는 건드리지 않는다. drain은 cordon을 포함하면서, 그 위의 Pod를 축출(evict)해 실제로 비운다.

명령새 Pod 스케줄링기존 Pod용도
cordon차단유지배치만 막고 관찰
drain차단축출노드 비우기
uncordon허용유지작업 후 복귀

기본 절차

가장 단순한 흐름은 cordon 없이 drain 한 번으로 끝난다. drain이 내부에서 cordon을 먼저 수행하기 때문이다.

# 1) 새 스케줄링 차단 (선택: 미리 격리해두고 싶을 때)
kubectl cordon node-01

# 2) DaemonSet은 무시하고, emptyDir 볼륨 Pod까지 허용, 최대 5분 대기
kubectl drain node-01 \
  --ignore-daemonsets \
  --delete-emptydir-data \
  --grace-period=120 \
  --timeout=300s

# 3) 커널 패치·재부팅 등 실제 유지보수 수행
# ...

# 4) 작업 후 다시 스케줄링 허용
kubectl uncordon node-01

--ignore-daemonsets는 사실상 필수다. DaemonSet Pod는 노드에 고정되므로 이 플래그가 없으면 drain이 곧바로 멈춘다.

PodDisruptionBudget으로 가용성 지키기

drain은 축출 API를 쓰기 때문에 PodDisruptionBudget(PDB)을 존중한다. PDB가 없으면 replica가 한꺼번에 사라져 순간적으로 서비스가 비는 사고가 난다. 최소 가용 Pod 수를 명시해두면 축출이 그 선을 넘지 않도록 대기한다.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-pdb
spec:
  minAvailable: 2          # 항상 2개는 살아있어야 함
  selector:
    matchLabels:
      app: web

이 상태에서 replica가 3개고 2개가 다른 노드에 없다면, drain은 새 Pod가 떠서 조건을 만족할 때까지 블로킹된다. 이때 kubectl get pdbALLOWED DISRUPTIONS가 0인지 확인하면 왜 멈췄는지 바로 보인다.

drain이 막힐 때 진단

실무에서 drain은 "안 끝나는" 경우가 많다. 원인은 대체로 정해져 있다.

  • PDB 위반: 대체 Pod가 뜰 여유 노드가 없어 대기.
  • ReplicaSet에 속하지 않은 단독 Pod: 기본적으로 축출을 거부한다. 정말 버려도 되면 --force 필요.
  • terminationGracePeriodSeconds: 종료 훅이 오래 걸리는 Pod가 시간을 잡아먹는다.
  • local storage 사용: --delete-emptydir-data 없으면 거부된다.
# 어떤 Pod가 축출 대기 중인지 이벤트로 확인
kubectl get events --field-selector reason=EvictionBlocked -A

# 특정 노드에 남아있는 비-DaemonSet Pod 조회
kubectl get pods -A --field-selector spec.nodeName=node-01 \
  -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,OWNER:.metadata.ownerReferences[0].kind

여러 노드를 다룰 때 주의점

클러스터 업그레이드처럼 여러 노드를 순회할 때는 반드시 한 번에 한 대씩 처리한다. 병렬로 drain하면 PDB가 각각은 만족해도 전체 replica가 동시에 이동하며 용량이 부족해질 수 있다. 또한 drain된 노드가 오토스케일러에 의해 축소 대상이 되지 않도록, 짧은 유지보수라면 노드에 cluster-autoscaler.kubernetes.io/scale-down-disabled=true 어노테이션을 붙여두는 편이 안전하다.

정리

핵심은 세 가지다. drain 전에 워크로드마다 PDB를 걸어 최소 가용성을 보장하고, --ignore-daemonsets와 필요한 데이터 플래그를 빠뜨리지 말며, 작업이 끝나면 uncordon으로 노드를 반드시 되돌린다. cordon/drain은 명령 자체는 단순하지만, 막히는 순간의 원인을 읽어내는 것이 실무 역량이다. 먼저 스테이징에서 PDB와 종료 훅 동작을 확인한 뒤 운영에 적용하는 것을 권한다.