トラス構造が幾何学的に組まれた建築物のファサード
技術解説

Kubernetes本番運用で起きるノード入替失敗、原因と事前確認の手順

目次を見る

Kubernetesクラスタをマネージドサービス(EKS・GKE・AKSなど、制御プレーンの運用をクラウド事業者が担うサービス)で本番運用しているチームに向けた内容です。バージョンアップグレード作業中にPodが一斉に落ちる、あるいはローリングアップデート(ノードを1台ずつ入れ替える更新方式)が途中で止まる、という事象に心当たりがあれば参考にしてください。

本番クラスタの構築自体は、ドキュメント通りに進めればそこまで難しくありません。難しいのは「Day 2」と呼ばれる運用フェーズです。アップグレード・スケーリング・フェイルオーバー・セキュリティパッチの適用がこれにあたります。特にアップグレードは、設計の不備が一気に表面化するタイミングです。

何が起きるか

Kubernetesクラスタのバージョンアップグレード中、アプリケーションに短時間の停止が発生することがあります。症状としては、アップグレード対象のノードに乗っていたPodが突然Terminatingになり、後続のPodが起動し切る前にリクエストがエラーになるというパターンが典型です。

影響範囲は一部のユーザーだけのこともあれば、ロードバランサーのヘルスチェックが連鎖的に失敗してサービス全体が不安定になることもあります。特に、ステートフルなワークロード(PostgreSQLなどデータを保持するアプリ)が乗っているノードで起きると、フェイルオーバーが絡んで復旧までの時間が長引きがちです。

なぜ起きるか

原因は1つではなく、複数の設計漏れが重なって顕在化することがほとんどです。段階的に分解します。

1つ目は、PodDisruptionBudget(PDB、意図的なPod停止時に最低限維持する稼働数を宣言する仕組み)が未設定、またはmaxUnavailableの値が緩すぎるケースです。PDBがなければ、ノードドレイン(ノードからPodを退避させる操作)がPodの可用性を考慮せず一気に進んでしまいます。

2つ目は、Readiness Probe(Podがリクエストを受け付けられる状態かを判定するヘルスチェック)の設定不備です。新しいPodが起動してもアプリの初期化が終わる前にReadyと判定されると、ロードバランサーがまだ準備できていないPodにトラフィックを流してしまいます。

3つ目は、サージノード(アップグレード中に一時的に追加する予備ノード)を用意せずに既存ノード数のままアップグレードを進めてしまうケースです。この場合、Podの退避先が足りず、スケジューリング待ちの間にサービスのキャパシティが落ちます。

4つ目は、廃止予定のAPI(deprecated API)をリリースノートで確認せずにアップグレードしてしまうケースです。コントロールプレーンのバージョンが上がった瞬間に、既存のマニフェストが通らなくなり、デプロイそのものが失敗します。

自分のプロジェクトが該当するか確認する方法

まず、PDBが設定されているかを確認します。

kubectl get pdb -A

出力が空、もしくは対象Deploymentに対応するPDBが見当たらない場合は要注意です。ALLOWED DISRUPTIONS の列が0になっていないかも合わせて見てください。

次に、ノードプール(ワーカーノードの集合)がアベイラビリティゾーン(AZ、クラウド内の独立した障害単位)をまたいでいるか確認します。

kubectl get nodes -L topology.kubernetes.io/zone

すべてのノードが同じゾーンに偏っている場合、ゾーン障害やアップグレード時のノード入替で一時的に全滅するリスクがあります。

廃止予定APIの有無は、kubectl の deprecations チェックや、pluto のような専用ツールで洗い出せます。クラウド事業者の管理コンソール(EKSならAWSコンソールのアップグレードインサイト機能など)が事前警告を出してくれる場合もあるので、アップグレード前に必ず確認してください。

最後に、Readiness Probeの設定を確認します。

kubectl get deploy <deployment名> -o jsonpath='{.spec.template.spec.containers[*].readinessProbe}'

空で返ってくる場合、ヘルスチェックが未設定のまま本番稼働している可能性があります。

対策の手順

対策は、アップグレードの「前」「中」「後」に分けて考えると整理しやすいです。

アップグレード前にやることは次の通りです。

  • リリースノートを読み、廃止予定APIと非互換の変更点を洗い出す
  • ステージング環境で同じバージョンアップを先に実施し、挙動を確認する
  • PDBをすべての本番Deploymentに設定する(maxUnavailable: 1 などから始める)
  • Readiness Probeとresources.requests/limitsを全Podに設定する

アップグレード中にやることは次の通りです。

  • コントロールプレーンを先に上げ、ワーカーノードは後から1プール・1台ずつ進める
  • サージノードを一時的に追加し、退避先のキャパシティを確保する
  • kubectl rollout status でロールアウトの進行状況を監視し続ける

アップグレード後にやることは次の通りです。

  • 主要なSLI(レイテンシ・エラー率などサービスレベル指標)がSLO(目標値)内に収まっているかオブザーバビリティツール(Prometheus・Grafana・Datadogなど)で確認する
  • 廃止された機能に依存するマニフェストが残っていないか再スキャンする

これらの設定はTerraformなどのIaC(Infrastructure as Code、インフラ構成をコードで管理する手法)でノードプール定義ごとテンプレート化しておくと、次回以降のアップグレードで同じ確認漏れを防げます。たとえばノードプールのモジュールにPDBの最低値やサージ設定をデフォルトで組み込んでおけば、新しいサービスを追加するたびに設定を忘れるリスクを減らせます。

ゼロダウンタイムアップグレードはKubernetes側の機能だけでは成立せず、PDB・Readiness Probe・サージノードの3点セットをアプリ側で揃えて初めて実現します。

なお、ここまでの運用負荷を自前で抱えるのが難しいと感じる場合、Day 2運用(アップグレード・スケーリング・フェイルオーバーなど)を肩代わりするマネージドプラットフォームを検討する選択肢もあります。クラスタの運用に割く時間がプロダクト開発を上回っていると感じたら、判断材料の1つにしてください。

まとめ

Kubernetesのアップグレード障害は、PDB未設定・Readiness Probe不備・サージノード不足・廃止API未確認という4つの原因が重なって起きることが多いです。

まずは kubectl get pdb -A と kubectl get nodes -L topology.kubernetes.io/zone の2つのコマンドで現状を確認してください。

PDBとReadiness Probeが整っていれば、次はステージング環境でのリハーサルアップグレードを定例化し、IaCで設定をテンプレート化しておくと再発防止につながります。

参考

Kubernetes in Production: Reference Architecture, Zero-Downtime Upgrades, Autoscaling, and Running Postgres in Containers

この記事について: 本記事は AI を活用して作成し、forva AI 編集部が内容を確認・監修しています。

AI 駆動開発のご相談は forva AI へ。まずはお気軽にどうぞ。