2026年9月25日
2026年9月25日
Kubernetesのノードメンテナンス手順:cordon・drain・uncordonと止めないための準備
Kubernetes のノードを OS 更新や再起動のために止めるときは、kubectl cordon で新しい Pod が載らないようにし、kubectl drain で既存の Pod を他のノードへ退避させ、作業後に kubectl uncordon で戻すのが基本の流れです。この記事では、各コマンドの意味と主要なオプション、drain が止まる典型的な原因(PodDisruptionBudget など)、サービスを止めずにメンテナンスするための事前準備までを説明します。
なお、クラスタ全体のバージョンアップの手順は別記事で扱っており、この記事は「1台のノードを安全に外して戻す」ことに絞っています。
cordon・drain・uncordonの役割
3つのコマンドは役割が分かれています。
- cordon: ノードを「スケジュール不可」にする。すでに動いている Pod はそのまま。
kubectl get nodesで STATUS にSchedulingDisabledが付く - drain: cordon したうえで、ノード上の Pod を追い出す(evict)。追い出された Pod は、Deployment などのコントローラーが他のノードに作り直す
- uncordon: スケジュール不可を解除し、新しい Pod が再び載るようにする
drain は単に Pod を削除するのではなく、Eviction API を通して退避させます。Eviction API は PodDisruptionBudget(PDB)を尊重するため、「同時に落としてよい Pod の数」を超える退避は拒否されます。この仕組みが、drain 中にサービスが全部止まるのを防ぐ安全装置になっています。
作業前に確認すること
drain を実行する前に、そのノードで何が動いているかを把握します。
kubectl get pods -A -o wide --field-selector spec.nodeName=worker-1
一覧を見て、次の点を確認します。
- レプリカが1つしかない Deployment はないか。1つだけなら、退避の間はそのアプリが止まります。メンテナンス前に replicas を2以上にするか、停止を許容するかを決めます。
- 同じアプリの Pod が同じノードに偏っていないか。偏っていると、そのノードを外した瞬間に処理能力が大きく落ちます。topologySpreadConstraints や Pod anti-affinity で分散させておくのが理想です。
- コントローラーに管理されていない Pod(素の Pod)はないか。drain で消すと、誰も作り直しません。
- emptyDir やローカルの永続ボリュームを使う Pod はないか。emptyDir の中身は退避で失われます。ローカル PV に縛られた Pod は他のノードへ移れません。
- 他のノードに空きリソースがあるか。
kubectl top nodesなどで確認し、退避先が足りないと Pod が Pending のまま残ります。
手順1:cordonで新規スケジュールを止める
kubectl cordon worker-1
kubectl get nodes
cordon すると、ノードに node.kubernetes.io/unschedulable:NoSchedule の taint が付きます。この時点ではまだ Pod は動いているので、影響はありません。drain は内部で cordon も行うため省略もできますが、先に cordon しておくと、確認作業中に新しい Pod が載ってしまうのを防げます。
手順2:drainでPodを退避する
よく使う形は次のとおりです。
kubectl drain worker-1 \
--ignore-daemonsets \
--delete-emptydir-data \
--grace-period=60 \
--timeout=10m
各オプションの意味です。
--ignore-daemonsets: DaemonSet の Pod は各ノードに1つずつ置く前提なので退避先がなく、そのままでは drain が止まります。このオプションで DaemonSet の Pod を対象外にします。ログ収集や監視エージェントは多くの場合 DaemonSet です。--delete-emptydir-data: emptyDir を使う Pod も退避対象にします(中身は消えます)。古い記事では--delete-local-dataと書かれていますが、これは旧名で、現在は--delete-emptydir-dataを使います。--grace-period: Pod に与える終了猶予秒数。指定しなければ各 Pod のterminationGracePeriodSecondsが使われます。--timeout: drain 全体の待ち時間の上限。指定しないと条件がそろうまで待ち続けることがあります。
コントローラーに管理されていない Pod がある場合、drain はエラーで止まります。--force を付けると削除して先に進めますが、その Pod は作り直されません。中身を確認してから使ってください。
退避の進み具合は、別の端末で次のように見ると分かりやすいです。
kubectl get pods -A -o wide --field-selector spec.nodeName=worker-1 -w
DaemonSet 以外の Pod がいなくなれば drain は完了です。ここでノードを再起動したり、OS のパッケージを更新したりします。
drainが止まるときの原因と対処
drain が終わらないときは、出力メッセージを読めば原因の見当がつきます。可能性が高い順に挙げます。
PodDisruptionBudgetに阻まれている
「Cannot evict pod as it would violate the pod's disruption budget」という内容のメッセージが繰り返し出る場合です。PDB の設定を確認します。
kubectl get pdb -A
ALLOWED DISRUPTIONS が 0 の PDB があると、そのアプリの Pod は退避できません。典型的なのは「replicas が 2 で minAvailable: 2」のように、1つも落とせない設定です。対処は、replicas を増やして余裕を作るか、PDB の値を見直すことです。PDB を一時的に削除する方法もありますが、サービス停止を許容する判断になるため、影響を確認してから行います。
もう一つの原因は、Pod がすでに不健全(Ready でない)なせいで budget を食いつぶしているケースです。PDB には不健全な Pod の扱いを決める unhealthyPodEvictionPolicy という項目があり、AlwaysAllow にすると不健全な Pod は PDB に関係なく退避できます。対応状況は Kubernetes のバージョンによるため、利用中の版のドキュメントで確認してください(2026年10月現在)。
PDB の例です。
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
maxUnavailable: 1
selector:
matchLabels:
app: web
maxUnavailable: 1 なら、レプリカ数が変わっても「1つずつなら落としてよい」という意味になり、メンテナンスと相性がよい設定です。
Podの終了に時間がかかっている
退避は始まっているのに Terminating のまま残る場合は、アプリが SIGTERM を受けても終了しないか、preStop フックが長いことが考えられます。kubectl describe pod でイベントを確認し、アプリ側の終了処理を見直します。
退避先に空きがない
drain 自体は終わったのに、作り直された Pod が Pending のままの場合です。kubectl describe pod に「Insufficient cpu」などが出ていれば、他のノードのリソース不足です。ノードを追加するか、クラスタオートスケーラーの設定を確認します。
手順3:uncordonで戻し、偏りを確認する
作業後、ノードが Ready に戻ったことを確認してから uncordon します。
kubectl get nodes
kubectl uncordon worker-1
注意したいのは、uncordon しても退避済みの Pod は自動では戻ってこない点です。新しく作られる Pod から順に載るため、しばらくはノード間の偏りが残ります。偏りを直したい場合は、kubectl rollout restart deployment/<名前> で Pod を作り直す方法があります(再作成に伴う影響を確認してから実行します)。
複数台を順番にメンテナンスするときは、1台を戻し、Pod が Ready になって落ち着いたのを確認してから次のノードに進みます。急いで続けると、PDB の余裕がない状態で次の drain が始まり、止まりやすくなります。
マネージドKubernetesの場合
Amazon EKS のマネージドノードグループや GKE などでは、ノードの更新時にサービス側が自動で drain を行う仕組みがあります。その場合も PDB は尊重されるため、PDB が厳しすぎると更新が進まなかったり、一定時間後に強制的に進められたりします。挙動はサービスや設定によって異なるので、各サービスのドキュメントで確認してください。
まとめ
- cordon で新規スケジュールを止め、drain で退避し、作業後に uncordon で戻す
- drain は Eviction API を使い PDB を尊重する。
--ignore-daemonsetsと--delete-emptydir-dataをよく使う - 止まる原因で多いのは PDB の余裕不足。replicas を2以上にし、
maxUnavailable: 1などで退避の余地を作る - uncordon しても Pod は自動で戻らない。複数台は1台ずつ進める
- 手順は本番の前にステージング用クラスタで一度通しておく