お久しぶりです。あけおめ投稿から大分期間がたってしまいましたね。今回は割と重たい記事になってしまいました。
何度か我が家では、kubernetes(k8s) を運用していることを紹介しています。このブログ(wordpress) なども含め色々なサービスをk8s上で動かしています。今回は、最近(4/15)、その k8s で障害が発生した時の対処と発生した事象などを紹介しようかと思います。
長いので概要とまとめ
- 障害の原因と対応
- k8s を構成している1つのノードのストレージに障害が発生し適切に動作しなくなっていた
- k8s の分散ストレージの longhorn にも影響があり一部のPVが読めなくなり、Podの起動ができない状態となった
- longhornのvolumeのreplica数の調整、atach/detach操作などを実施することでpodが起動するようになり復旧できた
- まとめ
- k8s の冗長性は偉大
- 基本的に何も失わなかった
- 縮退しているが今も元気に動作している
- k8s の障害発生時にノードの削除はもっと早めに実施しても良かった
- 家のクラスタ構成、監視の仕組みなどの見直しが必要
- k8s の冗長性は偉大
障害発生初期
発生日時は、2024/04/15 23:00頃。そろそろ寝ようかなぁと思っていた時間です。以下のような事象が発生しました。それぞれの詳細な時刻は不明です。
- wallabagにURLを追加したところエラーが発生する、webにアクセスしても timeout する
今迄こういったことは発生しなかったが、家の k8s クラスタでは分散ストレージの longhorn を利用しており、replica数は3を指定している。データロストの可能性は低い、podが何かおかしくなったと判断して wallabag の再デプロイを目指して削除を実施しようと判断しました。
しかし、以下のような状態となります。
- podの削除(kubectl delete -f マニフェストファイル) を実施
- podが終了されない
--grace-period=0 --force
などをつけて強制削除を実施
- PVCの削除が実行できない
- 確認するとlonghornのWeb UIにアクセスできない
- PVCを
kubectl delete
で削除してみているが、それでもPVCの削除ができない
- podが終了されない
このような状況であることから、wallabag だけでなく他のサービスも含めた k8s全体が怪しいのではという判断をしました。
障害発生中盤
wallabag 以外の他のサービスについても動作を確認開始しました。すると以下のような状況であることが分かりました。
- 他サービス(jellyfin、navidrome、minio) なども同様の状況
- Ingressが応答を返していない
- このブログ(light-of-moe.com) などの外部公開しているIngressは応答がある
Ingressが機能停止してしまってWebアクセスだけできなくなっているものがあるような状況と判断しました。なので一旦Ingressを再作成して様子を見てみます。以下のような変化がありました。
- Ingressを削除して再作成
- 作成後、jellyfin などのサービスはアクセス可能に
- longhornの Web UI にもアクセスが可能に
- wallabag は 500 のエラーが帰ってくる。
- wallabagで動作しているphpが出力
どうやらIngressの動作が適切でなく、再作成することで一部改善できたようです。しかし wallabag には上手く動作しない状態になっていました。wallabag のデータは失いたくはないのですが致命的ではないので多少壊れても構わないと判断して色々操作を実施してみます。
- 一旦 PVC の削除を完全に実行することを目指して色々操作を行なう
- どうやっても削除できない
- longhorn の動作が相当に怪しい
- podの起動時のエラーではMulti Attach云々(記録が無いので曖昧です)といったログが出ていたので、知らないリソースがVolumeのattachを行なっていないかと考え色々削除してみるが、それでも PVC の削除ができない
- 新しい PV を longhorn の UI上で作成するが固まってしまい作成できない
wallabag については状況が改善されません。また longhorn の動作はかなり奇妙です。ここで k8s のクラスタ自体に問題があり、特にlonghornがおかしくなっていると判断しました。
障害発生終盤
ここまでで、k8s 全体、特にlonghornの挙動がおかしいと判断しました。ここで一旦クラスタ全体を再起動してみようと判断しました。longhorn自体をまるごと再起動する手軽な手段が無いですし、家のクラスタは再起動などを実施されても無事起動できている実績もあったためです。
すると以下のような状況となりました
- クラスタ全体の再起動を実行(それぞれのホストでrebootコマンドを実行)
- k8s-worker-2というノードが終了処理途中で失敗した
- VMのhard rebootを実行するもファイルシステムのエラーで起動出来ない
- k8s-worker-1というノードも終了は怪しかったが、hard rebootで問題なく起動した
ノード(k8s-worker-2)のストレージに障害が発生して正常動作しなくなったという状況であるということが分かりました。
故障しているノードで動作しているPodやIngressが適切に動作できなくなったので一部のサービスが動かなくなったと判断するのが良さそうです。また壊れているのがストレージだったのでメモリで動作しているプロセスが中途半端に動作していたため k8s の自動復旧などでも上手く復活されなかったのでしょう。
復旧作業
ここからは復旧に向けての作業です。longhornで3台の冗長構成なので2台生存していれば簡単に復旧できるでしょうと考えていたのですが、そうでもありませんでした。なお、この時点で深夜の1時くらいだったと思います。
再起動直後の状況
再起動完了時点で以下のような状況でした
- サービスの自動復旧を待ってみるが、いくつかのpodが起動しない(Pendingのまま起動しない)
- Ingressは復活しておりlonghornのweb ui にはアクセスできた。Web UIから確認するとVolumeの状況として以下の2つに分類されていた
- DegratedになっているVolume
- AttachingになっているVolume
- DegratedになっているVolumeを使っているサービスは起動できている
- AttachingになっているVolumeを利用しているpodが起動できていない
とりあえず問題を簡単にするためにあまり使っていない不要なサービスを削除しました。とくに grafana/loki を監視の基盤として構築していたのですが起動できないようなので、これも削除しました。悲しい。
最終的に起動できていないサービスは以下のようなものとなりました。
- gitlab
- apache(wordpress)
- wallabag
- jellyfin
- minio
この中で特に重大なものは gitlab です。クラスタ内のサービスで gitlab が container registry になっているものがあります。このブログを投稿している wordpress がまさにそうです。
起動できていないサービスでデータロストなく復旧したいのは上の2つです。一応バックアップも取っているので最悪そちらからレストアすることも可能ですが、できれば復旧したいです。
このサービスのうち jellyfinはデータが削除されても一番重要な動画データ自体はNASに置いてあるのでk8s側のvolume は消えても問題ないです。失敗しても構わないサービスとして jellyfinの復旧を目指してみます。
PV/PVC、サービスの復旧作業
まず、動作しなくなった node については cordon、drain などを実施してスケジューリングされないように設定しました。
この状態で、k8s自体の操作はある程度正常で以下のような操作は可能となっていました
- 基本的なk8sの操作は可能
- 再起動前に挙動が怪しかったStatefulsetsの削除、podの強制削除などは適切に動作
- PVCの削除も可能
- PVのClaimRefを削除することでAvailableに変更可能
ただし、この状態でも pod から longhorn の volume のマウントが出来ないPodがいるという状況でした。前述のlonghorn の web ui 上で Attaching で止まっているVolumeがどうも良くないようです。
この Volume がそうなった原因は不明ですが、以下のような操作を実施して復旧できました。
- replicaの数を2つに変更
- これでDegrade状態のVolumeはHealthyになる
- Attaching状態のVolumeは利用しているPodとPVCを削除し、PVからClaimRefを削除することでDetach状態に遷移できた
- Detach状態ではreplicaの数が弄れないようだ
- longhorn の ui 上にあった Attach メニューで特定のホストへAttachを実行する
- これで replica 数が弄れるようになった
- 実行すると指定のノードのホスト上に
/dev/longhorn/xxxx
というデバイスが作成されて適切に処理されていそうな雰囲気 - このホストへのAttach/Detachの操作を実施することで volume の挙動が正常になった(気がする)
この状態で、longhornのUI上から PV/PVCの作成を実行する。これには前回と同じ名前空間、同じ名前でPVCを作ってくれる機能があります。これでPVCを再生成して、StatefulSet を再度生成したら、適切にマウントされて起動できました。
この操作を実施して、gitlab、apacheの復旧を確認。この時点で5:00くらいでした……。
なお、wallabagのVolumeだけ一部前半に操作した都合で完全に壊れてしまっていました。wallabagの利用しているDBのvolumeは無事なのでこれで起動することを期待して一旦作業は完了です(寝た)。
後始末(残ったちょっとした問題と対処方法)
時間が経過しても問題なく動作しているので一旦復旧はできたと判断しました。
wallabag の復旧についても DB のデータだけで復旧してくれました。何か問題があったりしそうですがURLなどの情報さえ取得できればどうにでもなると思うので問題ありません。
残った課題としては
- Terminatingで終了しないpodが大量にある。これは壊れたnodeで動作していたdaemonsetのpod。
というものがありました。強制削除などを色々実施したのですがどうやっても消えてくれません。これは kubectl delete node で動作していないノード自体を削除したら消えてくれました。
これで完全に問題がない状態になりました。長い戦いでした。
振り返りとまとめ
色々ありましたが、障害対応していくなかで色々知見が得られました
- grafana/lokiでk8sの監視をk8s上でやるのは無意味
- 完璧に役にたたなかった、ログを見るのすら出来なかったので
- 普段のリソースを眺めるための仕組みとしては意味があるかもしれないが、今回のような状況でこそ役にたって欲しかった
- 監視は他のシステムから実施するようなことを検討したい
- 途中 longhornのリソースを削除したのは失敗だった
- 次回障害対応するなら削除するならPV/PVCまでにしておくこと
- これを実施していなければおそらくwallabagのvolumeも失わなかったはず
- 挙動を見るに初手で kubectl delete node を実行しておけば復旧作業も簡単だったのでは?という気がする
- longhorn上からもnodeが削除されたら挙動が変わったのではないか
- そもそもVMの起動が怪しいのに復活の可能性を考えて delete を最後まで実行しなかったのは悪手だった
- k8sはnodeの追加、離脱が簡単に出来るのがメリットなので有効活用すべき
特にノードの削除は早期に実施しておけば良かったなぁと思います。ノードの追加、削除はインパクトのある変更ではあるので躊躇しがちですが、k8sの自動復旧の仕組みを期待するのであれば障害発生早期に実施する操作として認識しておくと良さそうですね。
あとは、なんだかんだ大変でしたが最終的には ノードを1つ失っただけで重大なデータは基本的に無事で元気に今もクラスタとしては動作しているので、k8sの仕組みとlonghornの冗長性には感謝しかありません。
というわけで、k8s の障害事例とその対応の紹介でした。