Kubernetesのノード障害

Kubernetesのノード障害は、機器故障のほかリソース超過によるものが多く発生します。
ワークロードマネージャにはプリエンプティブにコンテナ用リソースを管理してハード資源を使い切って欲しいのですが、Kubernetesではコンテナに押し切られてノードごとダウンする挙動が生じます。

ノード障害時の挙動

通常稼働時のノードはReadyステータスですが、障害が起きるとNotReadyに移行します。
NotReadyノード上で動作しているPodはシャットダウンし、コントローラの定義に応じて再作成が走ります。

Google Kubernetes Engine(GKE)の例では、不定の期間NotReadyの状態が継続し、少なくとも数分間は該当ノードを利用できない挙動を観察しています。

なお、NotReadyの要因は基本的にログに残りません。

Podのリカバリ

NotReady後のリカバリは、Deploymentなどのコントローラによる自動プロセスに任せることになります。
この際、代替ノードが存在する必要があります。

複数ノード構成をとったうえで、nodeSelectorの対象を確認します。
nodeSelectorの制約でPodの代替配備先がない場合、ノードが復旧するまでPodのダウン状態は継続します。

また、障害ノード回復後に同一ノードで再配備するケースで二次的な障害の挙動を観察しています。
StatefulsetpersistentDiskをロストして動作不能になります。これはPodを削除して作成すると直ります。
また、flannelなどのkube-systemコンテナにエラーが起き、一部のPodが起動不能になります。これはmasterを再起動しないと直らず、かなり深刻です。GKEの場合masterがマネージドサービスであるため、再起動の手段がありません。バージョンアップの副作用で再起動するより他なしとなります。

二次的な障害波及

実行可能ノードが減ったときPodが再構成されます。再配備時のノード選択ロジックには制御しきれない面があり、Podの偏りによる二次的な障害波及の可能性があります。

とくにkube-systemのPodが特定ノードに偏ることがあり、リソースに空きがなくなるとサービスのPodがPendingで起動不能になります。

この状態になると回復のきっかけを失い、自動的なサービスレベル維持は全く機能しません。
ノード障害のほか、ノード数縮退の際にもこのシナリオは起こりえるため、自動スケール構成にも注意が必要です。

kube-dnsダウン

kube-dnsがダウンするとクラスタはその機能をほぼ失います。
kube-dnsは一般的なDeploymentコンテナに過ぎないため、異常動作の際に自動回復できないケースはあります。

たとえばノード縮退時に異常コンテナを終了しきれなかったケースでは、以下のような状態になることがあります。

$ kubectl get pod -nkube-system | grep dns
kube-dns-7b474755d7-tkhwd                                  4/4     Running      0             40m
kube-dns-7b474755d7-vjrsc                                  4/4     Terminated   0             80m

このようにTerminatedステータスにも関わらずコンテナ起動数の面で生きているようなPodが混ざっている場合、TerminatedのPodにもリクエストは流れ、タイムアウトで失敗します。

この場合にもkubectlは動作するケースは多々あり、kubectl delete pod -nkube-system kube-dns-7b474755d7-vjrscで異常コンテナを削除すると復旧します。

リソース超過によるノード障害の予防

ノード障害時にはログが残らないため、直接の原因切り分けはできません。

コンテナスペックのresources.limits.memoryを未設定のケースでメモリを使い過ぎた場合については、ノード障害が起きることを確認しています。
また、ノード障害後の自動再配備でコンテナ配置が偏ることにより、残存ノードも連鎖的にダウンする挙動も観察しています。

resources.limits.memoryを設定した場合にRAM要求が超過すると、OOM KillerがそのPodをkillする挙動になりノード障害の予防につながります。

resources.limits.cpuについては特定できていませんが、RAM設定を解決しても過負荷によるノードダウンは起きることから、CPUの過剰消費が引き金になる可能性もあり得ます。
すべてのコンテナにlimits.cpulimits.memoryを指定したクラスタでは、ノードクラッシュの頻度は減ることを観測しています。cpuを特定コンテナが使い切った場合、ヘルスチェックの応答不良をきっかけに障害ノードと判定されるのではないかと見ています。

ただしCPUのlimits指定は、マルチコアノードでコア数に応じてキャパシティが変わるため、適切な値の設計は困難かもしれません。

ノード資源の余裕が安定稼働に関わる

リソースの逼迫はノード障害の要因になる一方、設定によりノード障害を完全に防ぐには至っていません。
コンテナは便利ですが、Kubernetesの稼働品質はLinuxを直接利用する構成に比べると明確に落ちます。

GCPの仮想マシンはStandardとPreemptible(SpotVM)の2グレードから選べます。
VMじたいはStandardの方がサービスレベルが高いものの、GKEの負荷耐性の限界上、StandardVMのGKEノードなら安定稼働する関係とは言い切れません。

また、RAMフットプリントの大きい言語や処理の遅い言語は相対的にコンテナの安定性を下げる面があります。

根本的な解決は、Kubernetesそのものの進化によるリソース管理の品質UPを待つよりほかないでしょう。
当面は、ハード資源のサイジングによるNotReady発生の低減と、複数コンテナ構成による緩和で乗り切る展開が続くと考えられます。

中馬崇尋
Chuma Takahiro