エラーの内容
kubectl rollout status deployment/my-app を実行すると、ターミナルがそのまま固まります:
Waiting for deployment "my-app" rollout to finish: 1 old replicas are pending termination...
新しいPodは起動しています。ヘルスチェックも通過しています。しかし古いPodが終了しません。何分経っても、デプロイメントは successfully rolled out に到達しません。
根本原因
- PodDisruptionBudget(PDB)が終了をブロックしている — バジェットがゼロの中断しか許可していないため、KubernetesはPodを終了できません。
- 古いPodが
Terminating状態のまま固まっている — ファイナライザー、SIGTERMを無視するプロセス、または遅いグレースフルシャットダウンが原因で止まっています。 - ノードのキャパシティ不足 — 新しいPodをスケジュールできる場所がなく、古いPodがプレースホルダーとして残り続けます。
- ローリングアップデート設定のデッドロック —
maxUnavailable: 0とmaxSurge: 0を同時に設定すると、Kubernetesは一切の操作ができなくなります。 - 新しいPodがReadyにならない — Kubernetesは交代のPodが正常になるまで古いレプリカを削除しません。壊れたReadinessプローブが静かにロールアウト全体を停止させます。
まず診断する
推測せず、以下のコマンドを実行して何が詰まっているかを正確に特定してください:
# ロールアウトの完全な状態を確認
kubectl rollout status deployment/my-app
# このデプロイメントの全Podをノード配置とともに一覧表示
kubectl get pods -l app=my-app -o wide
# 詰まっているPodのイベントを調査
kubectl describe pod <terminating-pod-name>
# PodDisruptionBudgetがブロックしていないか確認
kubectl get pdb
kubectl describe pdb <pdb-name>
Podが5分以上 Terminating 状態の場合、ほぼ確実にファイナライザーが原因です。確認してください:
kubectl get pod <terminating-pod-name> -o json | jq '.metadata.finalizers'
ここで空でない配列が返ってきたら、それが原因です。
修正1: Terminating状態で詰まっているPodを強制削除する
Podが10分以上Terminating状態のままですか?通常の削除では解消しません。強制削除してください:
kubectl delete pod <terminating-pod-name> --grace-period=0 --force
これはグレースフルシャットダウンのウィンドウを完全にスキップします。Podが本当に固まっていることを確認してから使用してください — コネクションのドレインに90秒かかる遅いJavaサービスとは区別が必要です。
ファイナライザーが削除をブロックしている場合、手動で取り除いてください:
kubectl patch pod <terminating-pod-name> -p '{"metadata":{"finalizers":[]}}' --type=merge
修正2: PodDisruptionBudgetを調整または一時的に削除する
kubectl describe pdb で Allowed disruptions: 0 と表示されていますか?それがブロックの原因です。PDBの minAvailable が現在のレプリカ数と同じになっており、どのPodも触れない状態です。解決策は3つあります:
# オプションA: スケールアップして余裕を作る(最も安全)
kubectl scale deployment/my-app --replicas=<current+1>
# オプションB: PDBの最小値を一時的に緩和する
kubectl patch pdb <pdb-name> -p '{"spec":{"minAvailable":1}}'
# オプションC: ロールアウトが完了するまでPDBを削除する
kubectl delete pdb <pdb-name>
本番環境ではオプションAが最も安全です — 保護を減らすのではなく、キャパシティを追加します。ロールアウトが完了したら元のPDB設定を復元してください。
修正3: 新しいPodのReadinessプローブの失敗を修正する
明らかなエラーがなく静かに止まるロールアウトは、ほぼ常にReadinessプローブの問題です。新しいPodが起動してもReadyにならず、Kubernetesが古いPodを無限に保持し続けます。
kubectl describe pod <new-pod-name> | grep -A 10 Readiness
kubectl logs <new-pod-name>
よくある2つのシナリオ:アプリの初期化に45秒かかるのに initialDelaySeconds が10に設定されている、または新しいイメージで /health エンドポイントが /healthz にリネームされた。プローブの設定を修正してください:
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 3
修正4: ローリングアップデート戦略を調整する
maxUnavailable: 0 と maxSurge: 0 を両方設定するとデッドロックが発生します。Kubernetesは新しいPodを起動できず(サージが0)、古いPodも削除できません(利用不可が0)。何も動きません。
kubectl get deployment my-app -o yaml | grep -A 5 strategy
サージPodを少なくとも1つ許可すれば、ロールアウトはすぐに解除されます:
kubectl patch deployment my-app -p '{
"spec": {
"strategy": {
"rollingUpdate": {
"maxUnavailable": 0,
"maxSurge": 1
}
}
}
}'
maxUnavailable: 0, maxSurge: 1 はゼロダウンタイムの標準デフォルト設定です。移行中に1つ余分なPodを実行し、交代のPodが正常になったら古いPodを削除します。
修正5: 必要に応じてロールバックする
新しいイメージが壊れていて今すぐ本番を復旧する必要がありますか?まずロールバックして、後でデバッグしてください:
# 直前のリビジョンにロールバック
kubectl rollout undo deployment/my-app
# または特定のリビジョンを指定
kubectl rollout history deployment/my-app
kubectl rollout undo deployment/my-app --to-revision=2
修正を確認する
# ロールアウトが完了したことを確認
kubectl rollout status deployment/my-app
# 期待値: deployment "my-app" successfully rolled out
# Terminating状態のPodがないことを確認
kubectl get pods -l app=my-app
# レプリカ数が望ましい状態と一致しているか確認
kubectl get deployment my-app
予防策
- アプリの実際のシャットダウン時間に合わせて
terminationGracePeriodSecondsを調整してください。 コネクションプールをドレインするSpring Bootアプリは60〜90秒必要な場合があります。デフォルトの30秒は万能ではありません。 - ロールアウトのたびにステージング環境でReadinessプローブをテストしてください。 一度もパスしないプローブは、静かなロールアウト停止の最も一般的な原因であり、早期に検出するのが最も簡単です。
- 厳しいリソース制約がない限り、
maxUnavailable: 0, maxSurge: 1をデフォルトにしてください。 ほとんどのワークロードで最も安全なゼロダウンタイム設定です。 - PDBの
minAvailableはレプリカ数より低く設定してください。 3つのレプリカでminAvailable: 3を設定すると、自分のロールアウトを含む全ての中断が永久にブロックされます。 - アプリケーションでSIGTERMを適切に処理してください。 プロセスはシグナルをキャッチし、新しいリクエストの受付を止め、処理中のリクエストをドレインしてから正常に終了すべきです。SIGTERMを無視することが、30秒のシャットダウンを10分間フリーズするPodに変える原因です。

