kubernetesの物理障害の復旧事例

kubernetes上に構築したシステム障害時の復旧フローの例です。

障害状況の分析

まず、サービス停止に気づいた際、kubectl get podsでPod(コンテナ)の起動状態を確認します。

NAME                 READY     STATUS    RESTARTS   AGE
app1-sjh0p         1/1       Running   0          86d
platform1-vkrh8        1/1       Running   3          26d
ethnicdb-4vtsm   1/1       Running   0          153d
app3-7jvsw        1/1       Running   0          6d
app6-l9xsj      1/1       Running   0          6d
app2-bxtp1         1/1       Running   0          6d
tool1-fpdkl         0/1       Pending   0          3h
tool1-r9q7c         1/1       Unknown   0          6d
tool2-957xq        1/1       Unknown   0          6d
mysql-lqw4l       0/1       Pending   0          3h
mysql-z9swv       1/1       Unknown   0          6d
nginx-dbfww          0/1       Pending   0          3h
nginx-wlsrg          1/1       Unknown   0          6d
postgresql-1p9bx             1/1       Unknown   0          6d
postgresql-s31vh             0/1       Pending   0          3h
redis-hzfn2            1/1       Unknown   0          6d
redis-w3rwq            0/1       Pending   0          3h
app4-n0355         0/1       Pending   0          3h
app4-skzd7         1/1       Unknown   0          6d
app5-c0p84       1/1       Unknown   0          6d
app5-k2p1n       0/1       Pending   0          3h
wp1-051wg         1/1       Unknown   0          6d
wp1-nstvs         0/1       Pending   0          3h
wp2-5llc4        0/1       Pending   0          3h
wp2-c5v0r        1/1       Unknown   1          6d
wp3-bjmhv    0/1       Pending   0          3h
wp3-bxdbs    1/1       Unknown   0          6d

正常稼働している場合には、この出力はすべてRunningステータスになっています。
今回の出力例では、以下のような状況であることが分かります。

  • 一部のPodは生きている
  • 多くのPodがPendingとUnknownの重複した2Podを表示している。

この時点で物理障害のような印象が強いことから、kubectl get nodesで実行ノードのステータスを確認します。

NAME                                     STATUS     AGE       VERSION
gke-clst1-some-pool-86f0b1c7-l65v   Ready      153d      v1.5.2
gke-clst1-some-pool-86f0b1c7-opbl   NotReady   153d      v1.5.2

予想どおり、1ノードがNotReadyステータスになっています。
おそらく、以下のような流れでサービス停止したのだろう、ということが分かります。

  1. ノードがNotReadyになったことがきっかけでPodもUnknownステータスになる
  2. サービス維持のために追加でPodを自動起動する挙動になったものの、リソースがなくPendingで止まった

復旧作業

kubernetesはReadyなnodeが追加されしだい、システム設定したサービスレベルのとおりにPodを自動起動する挙動をとります。

また、UnknownのPodが残っていても動作に支障があるため、NotReadyのノードを削除します。

今回の環境は、Google Container Engine上で動作しているため、Google Cloud Platformの管理画面から操作できます。

kubernetesノードが動作しているVM(GKEのノードではなくGCPの仮想マシン)を削除すると、新しいVM上にgke-clst1-some-pool-86f0b1c7-opblのノードが自動再構成されました。

そして2ノードがReadyに復旧したところでPodのステータスもContainerCreating→Runningに自動遷移します。

なお、今回、単にVMを削除できたのは、RDBMSのDB領域などデータをVM外のPersistentDiskに保持する構成をとっていたためです。

dockerと同様、データの置き場所にとくに追加設定していない場合、データはコンテナとともに消えるため、VMを削除する前にデータサルベージが必要になります(したがってすぐに復旧はできない)。

PersistentDiskにデータをread/writeする構成の場合、新規PodがPersistentDiskをマウントして動作継続できます。(または、マウント時のトラブルで起動途中で異常終了することもあります)

GKEストレージ障害のケース

Disk failed to relink with udevadm #608の障害に遭遇しました(2022春)。
v1.22で各クラウドのストレージドライバを導入できるCSI driverが有効になり、ストレージの挙動が各社ドライバ依存になりました。

このケースではSpotVMノード上のPodがPersistentDiskをマウントできず、起動しなくなりました。 VMの品質が悪い場合には時間が経つと回復することもありますが、この場合はどのような操作をしても回復しませんでした。

最終的にCSI driverを無効化するダウングレードにより回復しました。
そのためには、CSI driverの設定をOFFにしたうえで、v1.21の新規ノードプールを作成し、既存ノードプールを削除する、という手順が必要でした。

また、基本的にGKEはダウングレードできません。リリースチャンネルをNoneにするとノードプールのバージョン選択肢が増えるため、マスターとバージョンの異なる古いノードを構成できます。

issueのとおりこの時点で解決のヒントもつかめていない事象であり、バージョンアップを自動適用した結果、ある日クラッシュするという展開でした。

まとめ

kubernetes上のクラスタは物理障害から比較的手軽に復旧できることを確認できました。

異常ノードの物理サーバーを除去し自動ノード復旧から再構成する手順は、物理障害時の基本的な復旧シナリオになりそうです。
本来的には、余裕ノードを多めに構成したり、Podを多重起動したり、何らかのリカバリ方式を持っておくことでアップタイムを向上できます。

ただし、ディスク周りでは復旧しにくいトラブルが起きやすく、とくにミドルウェアじたいの品質が原因の場合は対処の難易度は上がります。

中馬崇尋
Chuma Takahiro