ビビリフクロウの足跡

いつもお世話になっているインターネットへの恩返し

Kubernetesのノード障害時の挙動とノード復帰後の再スケジューリング

Kubernetes上のアプリケーションの可用性について調べていたところ、ノード障害時に気をつけたほうが良い点を見つけたので記事にしてみます。

Kubernetesのノード障害時にはノードのステータスがNotReadyになります。Kubernetesクラスタが以下の通り構成されていたとしましょう。

# kubectl get node
NAME                                 STATUS   ROLES    AGE    VERSION
test-cluster-7ajeznlcoium-master-0   Ready    master   147m   v1.14.0
test-cluster-7ajeznlcoium-minion-0   Ready    <none>   147m   v1.14.0
test-cluster-7ajeznlcoium-minion-1   Ready    <none>   147m   v1.14.0
test-cluster-7ajeznlcoium-minion-2   Ready    <none>   147m   v1.14.0

さらに、Podは次のように起動していたとしましょう。

NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE                                 NOMINATED NODE   READINESS GATES
httpd-6b77d6648-4jl4z    1/1     Running   0          20s   10.100.0.13   test-cluster-7ajeznlcoium-minion-0   <none>           <none>
httpd-6b77d6648-h7kjb    1/1     Running   0          20s   10.100.1.18   test-cluster-7ajeznlcoium-minion-1   <none>           <none>
httpd-6b77d6648-vd9xs    1/1     Running   0          20s   10.100.2.14   test-cluster-7ajeznlcoium-minion-2   <none>           <none>
nginx-7db9fccd9b-g8fjl   1/1     Running   0          26s   10.100.1.17   test-cluster-7ajeznlcoium-minion-1   <none>           <none>
nginx-7db9fccd9b-lp4fv   1/1     Running   0          26s   10.100.0.12   test-cluster-7ajeznlcoium-minion-0   <none>           <none>
nginx-7db9fccd9b-xdkfn   1/1     Running   0          27s   10.100.2.13   test-cluster-7ajeznlcoium-minion-2   <none>           <none>

ここで、ワーカーノードtest-cluster-7ajeznlcoium-minion-2のkubeletサービスを停止します。

(test-cluster-7ajeznlcoium-minion-2)
# sudo systemctl stop kubelet

40秒ほど経つと、STATUSNotReadyになるはずです。

# kubectl get node
NAME                                 STATUS     ROLES    AGE    VERSION
test-cluster-7ajeznlcoium-master-0   Ready      master   148m   v1.14.0
test-cluster-7ajeznlcoium-minion-0   Ready      <none>   149m   v1.14.0
test-cluster-7ajeznlcoium-minion-1   Ready      <none>   149m   v1.14.0
test-cluster-7ajeznlcoium-minion-2   NotReady   <none>   148m   v1.14.0

ノードがNotReadyだから、Podも移動しているだろう。。。と思いませんか? ですが。。。

NAME                     READY   STATUS    RESTARTS   AGE     IP            NODE                                 NOMINATED NODE   READINESS GATES
httpd-6b77d6648-4jl4z    1/1     Running   0          3m50s   10.100.0.13   test-cluster-7ajeznlcoium-minion-0   <none>           <none>
httpd-6b77d6648-h7kjb    1/1     Running   0          3m50s   10.100.1.18   test-cluster-7ajeznlcoium-minion-1   <none>           <none>
httpd-6b77d6648-vd9xs    1/1     Running   0          3m50s   10.100.2.14   test-cluster-7ajeznlcoium-minion-2   <none>           <none>
nginx-7db9fccd9b-g8fjl   1/1     Running   0          3m56s   10.100.1.17   test-cluster-7ajeznlcoium-minion-1   <none>           <none>
nginx-7db9fccd9b-lp4fv   1/1     Running   0          3m56s   10.100.0.12   test-cluster-7ajeznlcoium-minion-0   <none>           <none>
nginx-7db9fccd9b-xdkfn   1/1     Running   0          3m57s   10.100.2.13   test-cluster-7ajeznlcoium-minion-2   <none>           <none>

2分経っても移動しません。。。

実は、v1.13.0からTaintBasedEvictionというPod退避機能がデフォルトで有効になったようです。この機能ではTaintとeffectNoExecuteなTolerationを使って、Podを退避させます。具体的にはノードがNotReadyになると自動的にノードにtaint node.kubernetes.io/not-readyが付くようになり、加えて全てのPodに対してデフォルトのtolerationとしてこのtaintに対するNoExecute tolerationが付与されます。これにより当該機能を実現しています。

デフォルトではPod退避の猶予期間であるtolerationSecondsパラメータは300秒(5分)です。このため、デフォルトでは5分経たないとPodは再スケジューリングされなかったのです。この猶予期間を短くするには、kube-apiserverの起動オプションに以下を指定します。この例では30秒にしています。

  --default-not-ready-toleration-seconds=30
  --default-unreachable-toleration-seconds=30

では再チャレンジ。。。

NAME                     READY   STATUS        RESTARTS   AGE    IP            NODE                                 NOMINATED NODE   READINESS GATES
httpd-6b77d6648-7hf2x    1/1     Running       0          4m7s   10.100.0.14   test-cluster-7ajeznlcoium-minion-0   <none>           <none>
httpd-6b77d6648-dshdc    1/1     Running       0          4m8s   10.100.1.21   test-cluster-7ajeznlcoium-minion-1   <none>           <none>
httpd-6b77d6648-l9tkd    1/1     Terminating   0          4m7s   10.100.2.15   test-cluster-7ajeznlcoium-minion-2   <none>           <none>
httpd-6b77d6648-qjmfm    1/1     Running       0          12s    10.100.1.24   test-cluster-7ajeznlcoium-minion-1   <none>           <none>
nginx-7db9fccd9b-hh5gk   1/1     Terminating   0          4m1s   10.100.2.16   test-cluster-7ajeznlcoium-minion-2   <none>           <none>
nginx-7db9fccd9b-q9psw   1/1     Running       0          12s    10.100.1.23   test-cluster-7ajeznlcoium-minion-1   <none>           <none>
nginx-7db9fccd9b-r2td2   1/1     Running       0          4m1s   10.100.0.15   test-cluster-7ajeznlcoium-minion-0   <none>           <none>
nginx-7db9fccd9b-wq2hg   1/1     Running       0          4m1s   10.100.1.22   test-cluster-7ajeznlcoium-minion-1   <none>           <none>

見事、30秒程度で退避できました! でもちょっとまってください。もしこの状態でノードが復活したらどうなるでしょう。

NAME                                 STATUS   ROLES    AGE    VERSION
test-cluster-7ajeznlcoium-master-0   Ready    master   173m   v1.14.0
test-cluster-7ajeznlcoium-minion-0   Ready    <none>   174m   v1.14.0
test-cluster-7ajeznlcoium-minion-1   Ready    <none>   174m   v1.14.0
test-cluster-7ajeznlcoium-minion-2   Ready    <none>   173m   v1.14.0

Podの稼動状態はこのようになりました。

NAME                     READY   STATUS    RESTARTS   AGE     IP            NODE                                 NOMINATED NODE   READINESS GATES
httpd-6b77d6648-7hf2x    1/1     Running   0          5m51s   10.100.0.14   test-cluster-7ajeznlcoium-minion-0   <none>           <none>
httpd-6b77d6648-dshdc    1/1     Running   0          5m52s   10.100.1.21   test-cluster-7ajeznlcoium-minion-1   <none>           <none>
httpd-6b77d6648-qjmfm    1/1     Running   0          116s    10.100.1.24   test-cluster-7ajeznlcoium-minion-1   <none>           <none>
nginx-7db9fccd9b-q9psw   1/1     Running   0          116s    10.100.1.23   test-cluster-7ajeznlcoium-minion-1   <none>           <none>
nginx-7db9fccd9b-r2td2   1/1     Running   0          5m45s   10.100.0.15   test-cluster-7ajeznlcoium-minion-0   <none>           <none>
nginx-7db9fccd9b-wq2hg   1/1     Running   0          5m45s   10.100.1.22   test-cluster-7ajeznlcoium-minion-1   <none>           <none>

test-cluster-7ajeznlcoium-minion-2には再スケジューリングされません。Podが片寄状態になってしまっています。もしこの状態でたくさんPodがのったノードが停止してしまうと。。。

という事態を防ぐためにdeschedulerというプロダクトがあります!

これはKubernetesのJobとして機能させることで、Podのスケジューリングの偏りを検知して、再スケジューリングをかけてくれます。早速導入してみます。

git clone https://github.com/kubernetes-incubator/descheduler
cd descheduler/kubernetes
kubectl apply -f rbac.yaml
kubectl apply -f configmap.yaml
cat <<EOF | kubectl apply -f -
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  labels:
    run: descheduler-cronjob
  name: descheduler-cronjob
  namespace: kube-system
spec:
  concurrencyPolicy: Allow
  jobTemplate:
    spec:
      template:
        metadata:
          name: descheduler-pod
          annotations:
            scheduler.alpha.kubernetes.io/critical-pod: ""
        spec:
            containers:
            - name: descheduler
              image: bbrfkr0129/descheduler:latest
              volumeMounts:
              - mountPath: /policy-dir
                name: policy-volume
              command:
                - "/bin/descheduler"
              args:
                - "--policy-config-file"
                - "/policy-dir/policy.yaml"
                - "--v"
                - "3"
            restartPolicy: "Never"
            serviceAccountName: descheduler-sa
            volumes:
            - name: policy-volume
              configMap:
                name: descheduler-policy-configmap

  schedule: '*/3 * * * *'
EOF

この例ではCronJobを用いて3分ごとに再スケジューリングするようにしています。

3分後、見事偏りがなくなりました。。。!!

NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE                                 NOMINATED NODE   READINESS GATES
httpd-6b77d6648-7hf2x    1/1     Running   0          14m   10.100.0.14   test-cluster-7ajeznlcoium-minion-0   <none>           <none>
httpd-6b77d6648-dshdc    1/1     Running   0          14m   10.100.1.21   test-cluster-7ajeznlcoium-minion-1   <none>           <none>
httpd-6b77d6648-zvznn    1/1     Running   0          44s   10.100.2.18   test-cluster-7ajeznlcoium-minion-2   <none>           <none>
nginx-7db9fccd9b-hqj72   1/1     Running   0          44s   10.100.2.20   test-cluster-7ajeznlcoium-minion-2   <none>           <none>
nginx-7db9fccd9b-q9psw   1/1     Running   0          10m   10.100.1.23   test-cluster-7ajeznlcoium-minion-1   <none>           <none>
nginx-7db9fccd9b-r2td2   1/1     Running   0          14m   10.100.0.15   test-cluster-7ajeznlcoium-minion-0   <none>           <none>