Kubernetesのワーカーノードを

自動修復するために必要だったこと

CyberAgent - Hiroki Kawahara

CloudNative Days Tokyo 2021

自己紹介

● 川原 大輝(かわはら ひろき)

● 2021年4月入社

● CyberAgent group Infrastructure Unit所属

● 自宅ラック勢

○ Kubernetes

○ ZFS

○ Minio

○ MySQL

○ Elastic Cloud on Kubernetes

○ Harbor

○ 僕の考える最強のクラウドストレージ


2
AKE

● 社内向けKubernetes as a Service

● CIUのチームでGKE互換を目指して開発中

● 2017年4月にリリースしたv1をへて、v2を新規開発中

3
v1 v2
構成管理 OpenStack Heat ClusterAPI Provider OpenStack
マルチDC 非対応 対応
参考資料:
https://speakerdeck.com/masayaaoyama/cabasenext2021-k8s-amsy810
Kubernetesの全体像

4
https://kubernetes.io/ja/docs/concepts/overview/components/
ClusterAPI

5
Management Cluster Workload Cluster
Core Cluster API
Cluster API Provider
OpenStack
OpenStack
VM
Node
Machine
Deployment
Machine
Deployment
Machine
Deployment
Machine
Workload Cluster : ユーザーが利用するクラスタ

Management Cluster : Workload Clusterを管理するためのクラスタ

ClusterAPI

6
Management Cluster Workload Cluster
Core Cluster API
Cluster API Provider
OpenStack
OpenStack
VM
Node
Machine
Deployment
Machine
Deployment
Machine
Deployment
Machine
MachineDeploymentのreplicasをもとに

Machineリソースを作成

Workload Cluster : ユーザーが利用するクラスタ

Management Cluster : Workload Clusterを管理するためのクラスタ

ClusterAPI

7
Management Cluster Workload Cluster
Core Cluster API
Cluster API Provider
OpenStack
OpenStack
VM
Node
Machine
Deployment
Machine
Deployment
Machine
Deployment
Machine
Workload Cluster : ユーザーが利用するクラスタ

Management Cluster : Workload Clusterを管理するためのクラスタ

MachineリソースをもとにVMを立ち上げ、

kubeadmを使ってWorkload Clusterに

ジョインさせる

そもそもNodeの障害とは?

8
KubernetesにおけるNodeのNotReady

9
主にこれ

※ NodeがReadyでも障害状態であることはありますが、今回は対象外です 

  弊社でも、つい最近もありました :innocent: 

NodeのCondition

● Nodeの状態を示す

○ Type ReadyはPodが配置可能であること


○ Type DiskPressureはディスクの残容量が低下


● Type ReadyがFalse / Unknown

⇒ kubectlにおいてNotReadyと表示される

10
https://kubernetes.io/ja/docs/concepts/architecture/nodes/#condition
NodeのCondition

● Nodeの状態を示す

○ Type ReadyはPodが配置可能であること


○ Type DiskPressureはディスクの残容量が低下


● Type ReadyがFalse / Unknown

⇒ kubectlにおいてNotReadyと表示される

11
https://kubernetes.io/ja/docs/concepts/architecture/nodes/#condition
状態を確認した最後の時間

NodeのCondition

12
https://kubernetes.io/ja/docs/concepts/architecture/nodes/#condition
● Nodeの状態を示す

○ Type ReadyはPodが配置可能であること


○ Type DiskPressureはディスクの残容量が低下


● Type ReadyがFalse / Unknown

⇒ kubectlにおいてNotReadyと表示される

最後に状態遷移した時間

NodeのCondition

13
https://kubernetes.io/ja/docs/concepts/architecture/nodes/#condition
Ready な Node は True

kubeletを人為的に落としたNodeは

Unknownとなっている

なぜNodeがNotReadyになるの?

14
ノード上のコンポーネント

15
Node
kubelet
container runtime
kube-apiserver
1. kubeletがkube-apiserverに対して

自分のあるべき姿をWatchする

2. kubeletがcontainer runtimeに指示を出す

3. container runtimeがcontainerを立ち上げる

container
container
container
Linux Kernel
Type ReadyをFalseにしている実装

16
https://github.com/kubernetes/kubernetes/blob/0cd75e8fec62a2531637e80bb950ac9983cac1b0/pkg/kubelet/nodestatus/setters.go#L510-L539
Type ReadyをFalseに設定する実装

17
https://github.com/kubernetes/kubernetes/blob/0cd75e8fec62a2531637e80bb950ac9983cac1b0/pkg/kubelet/nodestatus/setters.go#L510-L539
container runtimeのエラーの確認

Type ReadyをFalseに設定する実装

18
https://github.com/kubernetes/kubernetes/blob/0cd75e8fec62a2531637e80bb950ac9983cac1b0/pkg/kubelet/nodestatus/setters.go#L510-L539
ノードがシャットダウン中かどうか

Type ReadyをFalseに設定する実装

19
https://github.com/kubernetes/kubernetes/blob/0cd75e8fec62a2531637e80bb950ac9983cac1b0/pkg/kubelet/nodestatus/setters.go#L510-L539
キャパシティの確認

NodeがNotReadyになる要因

● StatusがUnknown

○ kubeletがkube-apiserverに到達できていない


● StatusがFalse

○ container runtimeが異常

○ ノードがシャットダウン中

○ キャパシティの設定が間違っている


20
kubeletがkube-apiserverに到達できていない

21
Node
kubelet
container runtime
kube-apiserver ● kubeletが起動できていない

○ kubeletの設定ミス

● kubeletがハングアップ

○ kubeletの一時的な不具合

● ネットワーク的に到達できない

○ OS側の設定ミス

■ ストレージで永続化されている設定

■ ipコマンドなどの一時的な設定

○ kubeletの設定ミス

○ 外部要因

● 想定外

container
container
container
Linux Kernel
container runtimeが異常

22
Node
kubelet
container runtime
kube-apiserver
● container runtimeが起動できない

○ container runtime設定ミス

● container runtimeがハングアップ

○ container runtimeの一時的な不具合

● kubeletがcontainer runtimeを見つけられない

○ kubeletの設定ミス

● 想定外

container
container
container
Linux Kernel
障害の要因まとめ

23
Node
kubelet
container runtime
kube-apiserver
container
container
container
Linux Kernel
アプリケーションのハングアップ
永続化されていない設定のミス
永続化された設定のミス
外部要因
想定外
原因がだいたいわかったところで、

自己修復する手法を考える

24
自動修復の方法

25
Node
kubelet
container runtime
kube-apiserver
container
container
container
Linux Kernel
アプリケーションのハングアップ
永続化されていない設定のミス
永続化された設定のミス
外部要因
想定外
アプリケーションの再起動
OSの再起動
filesystemのロールバック 

必要なノード数を確保
自動修復機能の対応表

26

 AKEv2
 AKEv1
 GKE

アプリケーションの再起動
 ○
 ○
 ○

OSの再起動
 ○
 ○
 ○

filesystemのロールバック
 ○
 ×
 ○

必要なノード数を確保

(Cluster Autoscaler)

○
 ○
 ○

https://cloud.google.com/kubernetes-engine/docs/how-to/node-auto-repair
https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-autoscaler
アプリケーションの再起動

● Liveness Probeのような形で、ヘルスチェックした上で再起動

● GKEの手法がkubernetesリポジトリに含まれているので採用

27
https://github.com/kubernetes/kubernetes/blob/master/cluster/gce/gci/health-monitor.sh 

アプリケーションの再起動

● Liveness Probeのような形で、ヘルスチェックした上で再起動

● GKEの手法がkubernetesリポジトリに含まれているので採用

● 公式はCRIとDockerに対応

○ CRIに対応しているruntimeに使えるため、ContainerdとCRI-Oに対応


28
https://github.com/kubernetes/kubernetes/blob/master/cluster/gce/gci/health-monitor.sh 

OSの再起動

● 基本的にはGKEの振る舞いをまねた動きを目指す

29
Management Cluster Workload Cluster
Core Cluster API
Cluster API Provider
OpenStack
OpenStack
VM
Node
Controller
Machine
Deployment
Machine
Deployment
Machine
Deployment
Machine
https://cloud.google.com/kubernetes-engine/docs/how-to/node-auto-repair
OSの再起動

● 基本的にはGKEの振る舞いをまねた動きを目指す

30
Management Cluster Workload Cluster
Core Cluster API
Cluster API Provider
OpenStack
OpenStack
VM
Node
Controller
Machine
Deployment
Machine
Deployment
Machine
Deployment
Machine
https://cloud.google.com/kubernetes-engine/docs/how-to/node-auto-repair
① 10分NotReadyが継続したNodeをドレイン
② NotReadyなNodeに紐付いているVMを再起動
OSの再起動

● 基本的にはGKEの振る舞いをまねた動きを目指す

● 復旧できないノード・フラッピングするノードはどうする?

⇒ 現在進行形で試行錯誤中

31
Management Cluster Workload Cluster
Core Cluster API
Cluster API Provider
OpenStack
OpenStack
VM
Node
Controller
Machine
Deployment
Machine
Deployment
Machine
Deployment
Machine
https://cloud.google.com/kubernetes-engine/docs/how-to/node-auto-repair
① 10分NotReadyが継続したNodeをドレイン
② NotReadyなNodeに紐付いているVMを再起動
filesystemをロールバックするにあたって

● 再起動によって間違った設定が消える必要がある

● 一部のディレクトリはロールバックしない

○ /var/log : ログは調査に利用するため、残しておく


○ /var/lib/containerd : emptyDirなどが格納されるため、可能な限り残したい


32
GKEの場合

33
https://cloud.google.com/container-optimized-os/docs/concepts/disks-and-filesystem?hl=ja (一部抜粋)
/home

/var

/etc

/tmp

/usr

その他

GKEの場合

34
https://cloud.google.com/container-optimized-os/docs/concepts/disks-and-filesystem?hl=ja (一部抜粋)
/dev/dm-0 (ro)

/dev/sda1 (rw)
 tmpfs (rw)

再起動後に消失

/home

/var

/etc

/tmp

/usr

その他

GKEの場合

35
https://cloud.google.com/container-optimized-os/docs/concepts/disks-and-filesystem?hl=ja (一部抜粋)
/dev/dm-0 (ro)

/dev/sda1 (rw)
 tmpfs (rw)

再起動後に消失

● OverlayFSはファイルシステム(FS)をかぶせることができる 

● 参照は、上のFSから行い、なければ下のFSを参照 

● 変更は上のFSに適用される 

● コンテナの基礎技術 

ルートファイルシステムを

リードオンリーにするのは大変

36
逆に消えてもいいところだけ、

tmpfsでオーバーレイしよう!!

37
/etc/cni.net.d

/home

/var

/etc

/usr

その他

AKEv2の場合

38
/dev/vda1 (rw)

tmpfs (rw)

再起動後に消失

/dev/vda1 (bind)

起動時の動き

39
オーバーレイするtmpfs

(upper dir)を用意

起動時の動き

40
各ディレクトリを

オーバーレイしていく

起動時の動き

41
upper dirを削除

これで動くのは謎だけど、ディレクトリがきれいになる

起動時の動き

42
rootfsをリマウント

起動時の動き

43
書き込み可能にしたいディレクトリにバインド

起動時の動き

44
掃除

起動時の動き

45
このスクリプトを

いい感じのタイミングで

実行する

いいタイミングってなんだよ

46
systemdとの戦い

● 結論は以下の通り

○ local-fs.targetが完了してから各種アプリケーションが実行される


⇒ local-fs.targetの一環として先ほどのスクリプトを実行


● systemd-analyze plotで起動時の動きを確認できる

47
初回起動時

● Kubernetesの設定(/etc/kubernetes)は...

○ 永続化する必要がある

○ 書き換えられると障害につながりやすい


● kubeadmの実行後にオーバーレイする必要がある

48
⇒ cloudinitのkubeadm実行後に、

スクリプトとsystemd serviceを差し込む

オーバーレイできない一部ディレクトリについて

CNIのディレクトリ(/opt/cni/bin, /etc/cni/net.d, /usr/libexec/kubernetes) 

● kubeadmによって構成が終了したあとに、オーバーレイとCNIのインストー
ルのどちらがさきに実行されるかコントロールできない

⇒ 永続化で統一

sssd(/usr/lib/x86_64-linux-gnu/ldb/modules/ldb) 

● カーネルのバグのため、カーネルアップグレードまでの暫定措置

● SSSD on docker container (Ubuntu) - Stack Overflow 

49
Cluster Autoscaler(ClusterAPI)

● Clusterに必要なNodeを確保するようにClusterAPIを操作

● リソースの抽象度がうまいので、ちゃんと動く

50
Management Cluster Workload Cluster
Core Cluster API
Cluster API Provider
OpenStack
OpenStack
VM
Node
Cluster Autoscaler
Controller
Machine
Deployment
Machine
Deployment
Machine
Deployment
Machine
MachineDeploymentをWatch
● annotationsからノード数の
上限・下限を取得
PodとNodeをWatch
● Podのrequestsの総数に対してNodeが
足りているか確認
● 足りていなければMachineDeploymentの
replicasを増やす
障害時のCluster Autoscalerの挙動

● NotReadyなNodeはCluster Autoscalerにおいて

有効なキャパシティとして計算されない

⇒ replicasを増やして新しいNodeをデプロイする

51
Management Cluster Workload Cluster
Core Cluster API
Cluster API Provider
OpenStack
OpenStack
VM
Node
Cluster Autoscaler
Controller
Machine
Deployment
Machine
Deployment
Machine
Deployment
Machine
まとめ

52
● GKEの手法はkubernetesのリポジトリに結構眠っている

○ Autopilotは全然わかりませんでした


● Nodeの再起動をするコントローラは奥が深い

● cloud-init + OverlayFSで手軽にfilesystemのロールバックを実現

● Cluster Autoscalerがあればクラスタのキャパシティは確保可能

ぜひ皆さんもNodeの自動修復を!!!!


Kubernetesのワーカーノードを自動修復するために必要だったこと