1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

概要

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 のシナリオは次の通り:

  1. Ceph RBD の PV + Pod からなる StatefulSet を作成する
  2. Node に障害を発生させる
  3. Medik8s による Tainting を確認する
  4. Kubernetes および Rook の作用で、Pod および RBD が終了することを確認する
  5. 別ノードで Pod および RBD が再作成されることを確認する

rook-failover.drawio.png

前提条件

  • 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 を見つける:

medik8s_operator.png

Node Health Check Operator をインストールする。

完了すると、"Node Health Check Operator"、および "Self Node Remediation Operator" がインストールされる。

Screenshot from 2024-06-27 12-12-47.png

SelfNodeRemediationTemplate カスタムリソースの作成

Self Node Remediation Operator の Web コンソールを開き、SelfNodeRemediatorTemplate リソースの設定画面を開く。
画面右側の "SelfNodeRemediatorTemplate の作成" と書かれた青いボタンを押す。

Screenshot from 2024-06-27 01-32-30.png

各項目を次のように入力し、「作成」をクリックする:

  • Name は selfnoderemediationtemplate-outofservicetaint
  • Template -> spec -> remediationStrategy: "OutOfServiceTaint"

Screenshot from 2024-06-26 22-50-56.png

NodeHealthCheck カスタムリソースの作成

Node Health Check Operator の Web コンソールを開き、NodeHealthCheck リソースの設定画面を開く。
画面右側の "NodeHealthCheck の作成" と書かれた青いボタンを押す。

Screenshot from 2024-06-27 01-33-08.png

各項目を次のように入力し、「作成」をクリックする:

  • 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 程度の適当な短い値を設定する

nhc.png

StatefulSet の作成

まずは次の manifest より StatefulSet を作成する:

test-sts.yaml
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 名を表示するものである:

find_rbd.sh
#!/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 参考資料

  1. Kubernetes フェイルオーバーの改善: Non-Graceful Node Shutdown (Open Source Summit Japan 2023) : OSS 貢献活動 | NEC

  2. Rook-Ceph による out-of-service Taint の付与・除去を行うコード箇所のリンク:self-node-remediation/controllers/selfnoderemediation_controller.go at 4b91e5c9f79bc4dfc0c47c5027164e877709617e · medik8s/self-node-remediation

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?