注意
本記事の記載内容は独自調査に基づいたものであり、Kubernetesの公式な見解ではありませんので、本記事に基づく各方面への問い合わせはお控えください。また、記事中の対応により問題が解消することを保証するものではありません。
経緯
安定して稼働していたKubernetesクラスターを1.17にバージョンした頃から、度々ノードが突然死ぬ事象が発生するようになりました。
調べたところ、該当ノードのOSは生きており、PINGにも応答しますが、Kubernetesレベルではマスターとの通信が失われており、次のようなログが出力されます。
kubelet.log E1013 20:38:49.531746 2094 controller.go:177] failed to update node lease, error: Put https://172.20.0.1:2040/apis/coordination.k8s.io/v1/namespaces/kube-node-lease/leases/10.129.108.224?timeout=10s: context deadline exceeded (Client.Timeout exceeded while awaiting headers)
kubelet.log E1013 20:38:50.140112 2094 kubelet_node_status.go:402] Error updating node status, will retry: error getting node "**.**.**.**": Get https://172.20.0.1:2040/api/v1/nodes/10.129.108.224?resourceVersion=0&timeout=10s: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
kubelet.log E1013 20:38:53.975263 2094 controller.go:177] failed to update node lease, error: Put https://172.20.0.1:2040/apis/coordination.k8s.io/v1/namespaces/kube-node-lease/leases/10.129.108.224?timeout=10s: read tcp 172.20.0.1:6999->172.20.0.1:2040: use of closed network connection
原因と推定されるもの
何かしらの原因でマスターとノードで通信が切断した場合、通常はリトライが試みられますが、Kubernetesを記述しているGoの不具合で、通信のリトライに失敗し続ける可能性があるようです。
影響範囲
Kubernetes 1.16,1.17,1.18で発生しやすいようです、特に1.17では頻発するようです。
困ること
Kubernetesなのでノードが1台死んだくらいで気にすることはないという考え方もありますが、この事象には1つ困ったことがあります。それは、該当ノードはマスターからは死んだように見えますが、実は生きているというところです。例えば、レプリカ数1で動かす必要のあるPodが該当ノードにいた場合、ノードの切断を検知したマスターは別のノードでPodの起動を試みます。しかし該当ノードのPodは稼働中であるため、二重起動状態になります。このとき、もしPodがPVのファイルをロックするような挙動をしている場合(DBなど)、後から起動したPodはロックの獲得に失敗し正しく起動できません。さらに、元のPodへのService経由での通信はマスターによって除外されるため、最終的にPodへのアクセスができない状態に陥ります。
ちなみに該当ノードのPodはマスターと切り離されても勝手には自爆してくれません。過去のKubernetesではそのような動作をしていたのですが、1.15から自爆はしなくなりました。
https://kubernetes.io/ja/docs/concepts/architecture/nodes/
その場合、APIサーバーがkubeletと再び通信を確立するまでの間、Podの削除を行うことはできません。削除がスケジュールされるまでの間、削除対象のPodたちは切り離されたノードの上で稼働を続けることになります。
バージョン1.5よりも前のKubernetesでは、ノードコントローラーはAPIサーバーから到達不能なそれらのPodを強制削除していました。しかしながら、1.5以降では、ノードコントローラーはクラスター内でPodが停止するのを確認するまでは強制的に削除しないようになりました。
対応
Kubernetesのバージョンを1.19に上げることでGoのバージョンが上がり、この問題は解消する可能性があります。
IBM Cloud Kubernetes Serviceでは最近1.19が利用可能になりましたので、バージョンアップすることを強くお勧めします。
ただしRed Hat OpenShift Cluster on IBM Cloudのベースで動いているKubernetesはまだ1.19に対応していません。
バージョンアップが出来ない場合は、ノードの再起動を行うことで状態が回復します。