概要
Rook-Ceph は、Kubernetes の Non-Graceful Node Shutdown 機能を前提とした failover の仕組みを備えている。前回の記事では、手動で障害の発生したノードに out-of-service
Taints を付与し、StatefulSet (Pod + Ceph RBD) の failover が起こる様子を確認できた:
また、Medik8s と呼ばれる障害検知と Tainting を自動で行う OSS を併用することで 1、先述のような障害発生時の Tainting も自動化する運用が考えられる。
本記事では、Medik8s と Rook-Ceph の組み合わせによる failover を検証し、ノードの電源を切るだけで StatefulSet (Pod + RBD PV) が自動的に別の正常な Node に移動する様子を確かめる。
Failover のシナリオ
想定する Failover のシナリオは次の通り:
- Ceph RBD の PV + Pod からなる StatefulSet を作成する
- Node に障害を発生させる
- Medik8s による Tainting を確認する
- Kubernetes および Rook の作用で、Pod および RBD が終了することを確認する
- 別ノードで Pod および RBD が再作成されることを確認する
前提条件
- Kubernetes Cluster として 3-node 構成の OKD4 cluster を用いる。各 Node は master と worker 両方の role を持つ。
- Rook-Ceph 環境として OpenShift Data Foundation を用いる。OpenShift Data Foundation 導入手順の例については次の記事も参照:
事前準備
Node Health Check Operator のインストール
Operator Hub を開き、"Medik8s" などで検索し、Node Health Check Operator を見つける:
Node Health Check Operator をインストールする。
完了すると、"Node Health Check Operator"、および "Self Node Remediation Operator" がインストールされる。
SelfNodeRemediationTemplate カスタムリソースの作成
Self Node Remediation Operator の Web コンソールを開き、SelfNodeRemediatorTemplate リソースの設定画面を開く。
画面右側の "SelfNodeRemediatorTemplate の作成" と書かれた青いボタンを押す。
各項目を次のように入力し、「作成」をクリックする:
- Name は
selfnoderemediationtemplate-outofservicetaint
- Template -> spec -> remediationStrategy: "OutOfServiceTaint"
NodeHealthCheck カスタムリソースの作成
Node Health Check Operator の Web コンソールを開き、NodeHealthCheck リソースの設定画面を開く。
画面右側の "NodeHealthCheck の作成" と書かれた青いボタンを押す。
各項目を次のように入力し、「作成」をクリックする:
- Name は
nodehealthcheck-outofservice
とする - Remediation template の欄を設定する
- "Other" ラジオボックスを選択する
- Api version, Kind, Name, Namespace は、さきほど作成した SelfNodeRemediationTemplate を指定する。具体的な値は次の表の通り:
Key | Value |
---|---|
Api version | self-node-remediation.medik8s.io/v1alpha1 |
Kind | SelfNodeRemediationTemplate |
Name | selfnoderemediationtemplate-outofservicetaint |
Namespace | openshift-workload-availability |
-
Node selection は Template を適用したいノードのラベルを指定する。本記事では ODF node を指定したいので、
cluster.ocs.openshift.io/openshift-storage
を指定する -
Unhealthy Condition はデフォルトのままでよい。Duration は異常を検知してから障害発生と認定するまでの時間で、デフォルトでは
300s
になっている。今回は実験目的なので、20s
程度の適当な短い値を設定する
StatefulSet の作成
まずは次の manifest より StatefulSet を作成する:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: test-sts
spec:
serviceName: "nginx"
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
storageClassName: "ocs-storagecluster-ceph-rbd"
リソースを作成する:
$ oc apply -f test-sts.yaml
statefulset.apps/test-sts created
StatefulSet が作成されたことを確認する:
$ oc get sts
NAME READY AGE
test-sts 1/1 2m16s
Pod が稼働していることを確認する:
$ oc get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-sts-0 1/1 Running 0 36s 10.130.0.28 master0 <none> <none>
PVC がBound されていることを確認する:
$ oc get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-test-sts-0 Bound pvc-1e714430-b9ce-435e-93e4-f4338d1eeaed 1Gi RWO ocs-storagecluster-ceph-rbd 2m27s
作成された PV を確認する:
$ oc get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-1e714430-b9ce-435e-93e4-f4338d1eeaed 1Gi RWO Delete Bound default/www-test-sts-0 ocs-storagecluster-ceph-rbd 2m28s
RBD の確認
次に、PV に対応する RBD がどのNode 上に存在するか確認する。
これには、次のスクリプト:find_rbd.sh
を作成すると便利である。
これは、PV 名を与えると RBD デバイスが存在する Node 名を表示するものである:
#!/bin/bash
usage()
{
echo "Usage:"
echo " ./find_rbd.sh <pvname>"
}
# Parse arguments
if [ "$#" -ne 1 ]; then
usage
exit 1
fi
pvname=$1
nodes=$(oc get nodes --no-headers)
while read -r line; do
node=$(echo "$line" | awk '{print $1}')
status=$(echo "$line" | awk '{print $2}')
if [ "$status" != "Ready" ]; then
continue
fi
lsblk_result=$(oc debug node/${node} -- chroot /host lsblk -d 2> /dev/null)
echo "${lsblk_result}" | grep "/${pvname}/" > /dev/null
if [[ $? -eq 0 ]]; then
echo "An RBD found on $node:"
# Output until the indentation ends
echo "${lsblk_result}" | awk -v pvname="${pvname}" '
{
if (found) {
if ($0 ~ /^ {32}/) {
print $0
} else {
found = 0
}
}
if ($0 ~ pvname) {
print $0
found = 1
}
}'
exit 0
fi
done <<< "$nodes"
echo RBD not found.
exit 1
これを利用して、PV に対応する RBD が存在する Node を特定する:
$ ./find_rbd.sh pvc-1e714430-b9ce-435e-93e4-f4338d1eeaed
An RBD found on master2:
rbd0 251:0 0 1G 0 disk /var/lib/kubelet/pods/f0cb6e7d-78b2-4bfd-8ffd-55627a990c36/volumes/kubernetes.io~csi/pvc-1e714430-b9ce-435e-93e4-f4338d1eeaed/mount
/var/lib/kubelet/plugins/kubernetes.io/csi/openshift-storage.rbd.csi.ceph.com/4005d25cbb5fbd3d05a867378d413b2564df4455349e0458fe26e66ece694fa8/globalmount/0001-0011-open
shift-storage-0000000000000002-5f231f68-9dd5-457a-a5f4-61f1bae8fd9b
Node master2
上に /dev/rbd0
として存在していることが確認できた。
Node に taints がないことの確認
すべての Nodes において、 taints : null
になっていることを確認する。
$ oc get nodes -o json | jq '.items[] | {name: .metadata.name, taints: .spec.taints}'
{
"name": "master0",
"taints": null
}
{
"name": "master1",
"taints": null
}
{
"name": "master2",
"taints": null
}
Failover の動作確認
準備段階で対象の RBD が master2
上にあることがわかったので、この Node を強制的に電源OFFにする。
しばらくすると OFF にした Node にTaints が付与される。
待ち時間は、NodeHealthCheck 作成時に設定した Duration によるが、20s
に設定した場合は1分くらいで Tainting されると思われる。
Medik8s によって Taints が付与されているようだが、out-of-service
taints ではない。
$ oc get nodes -o json | jq '.items[] | {name: .metadata.name, taints: .spec.taints}'
{
"name": "master0",
"taints": null
}
{
"name": "master1",
"taints": null
}
{
"name": "master2",
"taints": [
{
"effect": "NoSchedule",
"key": "node.kubernetes.io/unreachable",
"timeAdded": "2024-06-28T01:21:27Z"
},
{
"effect": "NoExecute",
"key": "node.kubernetes.io/unreachable",
"timeAdded": "2024-06-28T01:21:33Z"
},
{
"effect": "NoExecute",
"key": "medik8s.io/remediation",
"timeAdded": "2024-06-28T01:21:48Z",
"value": "self-node-remediation"
},
{
"effect": "NoSchedule",
"key": "node.kubernetes.io/unschedulable",
"timeAdded": "2024-06-28T01:21:48Z"
}
]
}
少し待つと Pod が Terminating に変化する。
$ date
Fri Jun 28 10:20:46 AM JST 2024
$ oc get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-sts-0 1/1 Running 0 2m43s 10.129.0.243 master2 <none> <none>
$ date
Fri Jun 28 10:21:57 AM JST 2024
$ oc get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-sts-0 1/1 Terminating 0 3m54s 10.129.0.243 master2 <none> <none>
...
Terminating 状態が3分ほど続いたあと、一瞬だけ out-of-service
Taints が master2 に付与される。
$ oc get nodes -o json | jq '.items[] | {name: .metadata.name, taints: .spec.taints}'
{
"name": "master0",
"taints": null
}
{
"name": "master1",
"taints": null
}
{
"name": "master2",
"taints": [
{
"effect": "NoSchedule",
"key": "node.kubernetes.io/unreachable",
"timeAdded": "2024-06-28T01:21:27Z"
},
{
"effect": "NoExecute",
"key": "node.kubernetes.io/unreachable",
"timeAdded": "2024-06-28T01:21:33Z"
},
{
"effect": "NoExecute",
"key": "medik8s.io/remediation",
"timeAdded": "2024-06-28T01:21:48Z",
"value": "self-node-remediation"
},
{
"effect": "NoSchedule",
"key": "node.kubernetes.io/unschedulable",
"timeAdded": "2024-06-28T01:21:48Z"
},
{
"effect": "NoExecute",
"key": "node.kubernetes.io/out-of-service",
"timeAdded": "2024-06-28T01:24:50Z",
"value": "nodeshutdown"
}
]
}
Medik8s において、out-of-service
taint は付与しっぱなしではなく、リソースが削除されるまでの間だけ存在し、Pod や RBD の detach 処理が完了したら速やかに taint が削除される 2。なのでtaint を1秒間隔で出力とかしてないと out-of-service
tains が付与される瞬間は見れないかもしれない。
out-of-service
Taints が付与されたことを後から確認するには、self-node-remediation-controller-manager
Pod の logs を見ればよい:
$ oc logs -n openshift-workload-availability $(oc get pod -n openshift-workload-availability -o custom-columns=:metadata.name | grep self-node-remediation-controller-manager)
...
2024-06-27T02:04:47.033077449Z INFO controllers.SelfNodeRemediation out-of-service taint added {"selfnoderemediation": "openshift-workload-availability/master2", "new taints": [{"key":"node.kubernetes.io/unreachable","effect":"NoSchedule","timeAdded":"2024-06-27T02:01:14Z"},{"key":"node.kubernetes.io/unreachable","effect":"NoExecute","timeAdded":"2024-06-27T02:01:20Z"},{"key":"medik8s.io/remediation","value":"self-node-remediation","effect":"NoExecute","timeAdded":"2024-06-27T02:01:45Z"},{"key":"node.kubernetes.io/unschedulable","effect":"NoSchedule","timeAdded":"2024-06-27T02:01:45Z"},{"key":"node.kubernetes.io/out-of-service","value":"nodeshutdown","effect":"NoExecute","timeAdded":"2024-06-27T02:04:47Z"}]}
2024-06-27T02:04:47.03358787Z DEBUG events [remediation] Remediation process - add out-of-service taint to unhealthy node {"type": "Normal", "object": {"kind":"Node","name":"master2","uid":"7c4e06b6-dd7b-484d-9554-369515eb9b2d","apiVersion":"v1","resourceVersion":"454433"}, "reason": "AddOutOfService"}
2024-06-27T02:04:47.037005111Z INFO controllers.SelfNodeRemediation waiting for terminating pod {"selfnoderemediation": "openshift-workload-availability/master2", "pod name": "route-controller-manager-7c8f598cc4-fcfdq", "phase": "Running"}
2024-06-27T02:04:52.049149508Z INFO controllers.SelfNodeRemediation waiting for terminating pod {"selfnoderemediation": "openshift-workload-availability/master2", "pod name": "controller-manager-646bc75c48-grgct", "phase": "Running"}
2024-06-27T02:04:54.332206174Z INFO controllers.SelfNodeRemediationConfig Syncing certs
2024-06-27T02:04:54.332443056Z INFO controllers.SelfNodeRemediationConfig Cert secret already exists
2024-06-27T02:04:54.332453562Z INFO controllers.SelfNodeRemediationConfig.syncConfigDaemonset Start to sync config daemonset
2024-06-27T02:04:54.339036147Z INFO controllers.SelfNodeRemediationConfig Updating DS tolerations
2024/06/27 02:04:54 reconciling (apps/v1, Kind=DaemonSet) openshift-workload-availability/self-node-remediation-ds
2024/06/27 02:04:54 update was successful
2024-06-27T02:04:57.192184753Z INFO controllers.SelfNodeRemediation out-of-service taint removed {"selfnoderemediation": "openshift-workload-availability/master2", "new taints": [{"key":"node.kubernetes.io/unreachable","effect":"NoSchedule","timeAdded":"2024-06-27T02:01:14Z"},{"key":"node.kubernetes.io/unreachable","effect":"NoExecute","timeAdded":"2024-06-27T02:01:20Z"},{"key":"medik8s.io/remediation","value":"self-node-remediation","effect":"NoExecute","timeAdded":"2024-06-27T02:01:45Z"},{"key":"node.kubernetes.io/unschedulable","effect":"NoSchedule","timeAdded":"2024-06-27T02:01:45Z"}]}
2024-06-27T02:04:57.192542562Z DEBUG events [remediation] Remediation process - remove out-of-service taint from node {"type": "Normal", "object": {"kind":"Node","name":"master2","uid":"7c4e06b6-dd7b-484d-9554-369515eb9b2d","apiVersion":"v1","resourceVersion":"454661"}, "reason": "RemoveOutOfService"}
2024-06-27T02:04:57.192888654Z DEBUG events [remediation] Remediation process - finished deleting unhealthy node resources {"type": "Normal", "object": {"kind":"Node","name":"master2","uid":"7c4e06b6-dd7b-484d-9554-369515eb9b2d","apiVersion":"v1","resourceVersion":"454661"}, "reason": "DeleteResources"}
out-of-service
Taints の付与とほぼ同時に、別ノード master1
で ContainerCreating が開始される。
...
$ date
Fri Jun 28 10:24:48 AM JST 2024
$ oc get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-sts-0 1/1 Terminating 0 6m45s 10.129.0.243 master2 <none> <none>
$ date
Fri Jun 28 10:24:58 AM JST 2024
$ oc get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-sts-0 0/1 ContainerCreating 0 1s <none> master0 <none> <none>
$ date
Fri Jun 28 10:25:08 AM JST 2024
$ oc get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-sts-0 1/1 Running 0 11s 10.130.0.125 master0 <none> <none>
failover に要した時間(Pod が Terminating に変化してから ContainerCreating になるまで)は3分程度のようだ。(ただし今回は RBD に何も入れていないので、そこは考慮する必要がある)。
RBD も master2
から master0
に移っていることが確認できる:
$ ./find_rbd.sh pvc-1e714430-b9ce-435e-93e4-f4338d1eeaed
An RBD found on master0:
rbd0 251:0 0 1G 0 disk /var/lib/kubelet/pods/9676b428-cb30-45bd-a24a-788420133951/volumes/kubernetes.io~csi/pvc-1e714430-b9ce-435e-93e4-f4338d1eeaed/mount
/var/lib/kubelet/plugins/kubernetes.io/csi/openshift-storage.rbd.csi.ceph.com/4005d25cbb5fbd3d05a867378d413b2564df4455349e0458fe26e66ece694fa8/globalmount/0001-0011-openshift-storage-0000000000000002-5f231f68-9dd5-457a-a5f4-61f1bae8fd9b
Medik8s 参考資料
- OpenShift.Run-2024-Medik8s - ノード障害時にStatefulSet も自動回復したい!そんなときはmediks.io - Manabu Ori - RedHat - Speaker Deck
- 第6章 ノードヘルスチェックを使用したノードの修復 | Red Hat Product Documentation
- Node Health Check Operator - Red Hat Blog
- Getting Started | Medik8s
-
Kubernetes フェイルオーバーの改善: Non-Graceful Node Shutdown (Open Source Summit Japan 2023) : OSS 貢献活動 | NEC ↩
-
Rook-Ceph による
out-of-service
Taint の付与・除去を行うコード箇所のリンク:self-node-remediation/controllers/selfnoderemediation_controller.go at 4b91e5c9f79bc4dfc0c47c5027164e877709617e · medik8s/self-node-remediation ↩