Kubernetesを止めてもワークロードは止まらない?

2023年05月31日 水曜日


【この記事を書いた人】
田口 景介

社会人生活の半分をフリーランス、半分をIIJで過ごすエンジニア。元々はアプリケーション屋だったはずが、クラウドと出会ったばかりに半身をインフラ屋に売り渡す羽目に。現在はコンテナ技術に傾倒中だが語りだすと長いので割愛。タグをつけるならコンテナ、クラウド、ロードバイク、うどん。

「Kubernetesを止めてもワークロードは止まらない?」のイメージ

もう2年前のエントリになりますが、IIJがKubernetesの活用を始めたきっかけを紹介したのがこの記事でした。

ようこそ、Kubernetes沼へ。商用サービスSREの現場から

このとき、「不安を感じることなく、確実にシステムをアップデートする手段をKubernetesがもたらしくてくれるものと期待して使い始めた」と書きました。今振り返ってみると、その効果は期待を上回るものでしたが、いい意味で予想と大きく異なっていたことが一つありました。それは、言い方が難しいのですが、Kubernetesが思った以上にコンフィグレーションツールであったということです。仮想マシンに対するハイパーバイザや、プロセスに対するOSであれば、停止するとその上のシステムをも巻き込んで止まってしまうクリティカルな存在で、なによりも可用性が求められる存在です。当初はKubernetesもそれと同じく、Kubernetesの可用性に不安があればシステム全体の安定性に悪影響を及ぼし、停止すればコンテナをも巻き込んで止まってしまうのだと思っていました。ところが実際にはそんなことはなく、仮にKubernetesのコントロールプレーンが停止したところで、デプロイ済のワークロードは何事もなく動き続けます。

つまり、Kubenernetesは新規にワークロードをデプロイしたり、ネットワークなりストレージなりのコンフィグレーションを変更したりするときにのみ介入する、ある種のコンフィグレーションツールだということです。あとは、稼働中のワークロードやインフラに対するヘルスチェックとセルフヒーリングを主な役割としています。

意外でしょうか?もしくは、信じられませんか?わかります。私たちもそのことに気が付いた時は何かの勘違いではないかとさえ思ったものです。ですが事実ですので、その証拠にアプリケーションを動かしたまま、Kubernetesを停止して見せましょう。

Kubernetesを止めても、ワークロードは止まらない

具体的には、Kubernetesのコントロールプレーン群とノードエージェントであるkubelet、コンテナランタイムであるcontainerdなどを順番に止めていくことにします。そして、事前にデプロイしておいたWebサーバであるnginxコンテナへインターネットからアクセスが続くことをもって影響がないと判断することにします。

止めるシステムと動き続けるコンテナ

止める順番にはちょっと気を使います。別に丁寧に作業しないとPodまで止まってしまうとかそういうことではなく、むしろ逆で、いくら止めようとしてもKubernetesが復旧させてしまうので、セルフヒーリングが働かないように順番を考えないと止められないのです。すばらしいレジリエンシーと言えましょう。おかげで今回はCNIプラグイン(calico)を止めていません。CNIプラグインを停止するとノードステータスがNotReadyと判断され、nginxのPodにEvictionが発生してしまいます。これを避けるためにkube-apiserverを先に止めてしまうと、今度は当然ながらCNIプラグインを止めることができなくなります。CNIプラグインを止めてもワークロードに影響はないのですが、目をつぶることにします。

nginxをデプロイする

まず、動作確認に使うnginxをデプロイし、アクセスできることを確認しましょう。一通りKubernetesを停止したあとでもこのPodが動き続け、200 OKが返ることを最後に確認することにします。

$ kubectl -n default create deploy nginx --image=nginx
$ kubectl -n default expose deploy nginx --type=LoadBalancer --port=80 --target-port=80
$ kubectl -n default get svc nginx
NAME    TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)        AGE
nginx   LoadBalancer   10.97.199.197   133.xxx.yy.zzz   80:31376/TCP   1m
$ curl -v http://133.xxx.yy.zzz/
> GET / HTTP/1.1
< HTTP/1.1 200 OK
< Server: nginx/1.21.6

ネットワーク制御系ワークロードを止める

それでは、順番にKubernetesを構成するコンポーネントを止めていきましょう。最初に、ネットワークまわりのヘルスチェックを無効化するために、kube-proxyとcloud-controller-managerを止めます。これらを残しておくとロードバランサからのヘルスチェックが働いてしまい、実際にはnginxが動いているにもかかわらずプールから除外されてしまうからです。

$ kubectl -n kube-system delete ds kube-proxy
$ kubectl -n kube-system delete ds cloud-controller-manager

kube-proxyはクラスタ内部のロードバランサ(iptablesやipvsで実装)を、cloud-controller-managerはクラスタ外部のロードバランサのコンフィグレーションを担当していますが、そこの設定を変更する必要がなければ停止しても特に支障はありません。

マスターノードのkubeletを止める

次に、マスターノードのkubeletを止めます。kube-apiserverを止めようにもkubeletが動いていると復旧させてしまうため、先に止める必要があります。

$ sudo systemctl stop kubelet
$ sudo systemctl status kubelet
* kubelet.service - kubelet: The Kubernetes Node Agent
     Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor preset: enabled)
     Active: inactive (dead) since Fri 2023-04-14 19:46:27 JST; 19min ago
$ kubectl get node
NAME      STATUS     ROLES           AGE   VERSION
master1   NotReady   control-plane   32d   v1.25.4
worker1   Ready      worker          32d   v1.25.4
worker2   Ready      worker          32d   v1.25.4

kubeletはコンテナの起動・停止、PersistentVolumeのマウント・アンマウントなど、kube-apiserverからのリクエストを受けてノードで行われる一切合切を処理するノードエージェントですが、デプロイ済のコンテナが動き続けるだけならば必須ではありません。ただし、readiness probe、liveness probeといったヘルスチェックが働かなくなったり、ConfigMap/Secretの更新が反映されなくなったり、実際にはいろいろな影響が発生します。

kube-apiserver, kube-controller-manager, kube-schedulerを止める

続いて、いよいよKubernetesの中心と言える3つのコンテナを停止します。これら3つのコンテナはkubeletがstatic podとして起動するものですが、kubeletを停止しても動き続けているので、containerdへ命令してコンテナを停止します。

$ sudo nerdctl ps --namespace k8s.io | grep kube-system/kube-
CONTAINER ID    IMAGE                                              COMMAND                   CREATED        STATUS    PORTS    NAMES
10304e7b4a62    registry.k8s.io/kube-apiserver:v1.25.4             "kube-apiserver --ad…"    4 days ago     Up                 k8s://kube-system/kube-apiserver-ivm27656206/kube-apiserver
5d86bf7de91c    registry.k8s.io/kube-scheduler:v1.25.4             "kube-scheduler --au…"    4 days ago     Up                 k8s://kube-system/kube-scheduler-ivm27656206/kube-scheduler
6e43cbdb1e9c    registry.k8s.io/kube-controller-manager:v1.25.4    "kube-controller-man…"    4 days ago     Up                 k8s://kube-system/kube-controller-manager-ivm27656206/kube-controller-manager
$ sudo nerdctl stop <コンテナID> --namespace k8s.io
$ sudo nerdctl ps --namespace k8s.io | grep 'kube-system/kube-'
<なにもない>
$

kube-apiserverを止めてしまったのでもはやkubectlコマンドを利用できず、nginxのPodが存在しているかもわかりません。

ワーカーノードのkubeletを止める

kube-apiserverを止めてノードのヘルスチェックが止まったので、続いてワーカーノードのkubeletを停止します。ちなみに、先にkubeletを停止するとkube-apiserverがノードの停止を検知し、しばらくするとそのノードで動いているPodを別ノードで起動しなおそうとします。通常これは期待した挙動ですが、今回は面倒なのでこの手順で止めていきます。

$ sudo systemctl stop kubelet
$ sudo systemctl status kubelet
* kubelet.service - kubelet: The Kubernetes Node Agent
     Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor preset: enabled)
     Active: inactive (dead) since Fri 2023-04-14 19:58:31 JST; 5min ago

containerdを止める

最後に、コンテナランタイムであるcontainerdも止めてしまいます。containerdを止めるとさすがにコンテナも止まるだろうと思った方がいるかもしれませんが、止まりません。かつてDockerを利用していた時は、dockerを停止するとコンテナも停止してしまったのですが、containerdに切り替えてからはそのような影響も排除されました。

$ sudo systemctl stop containerd
$ sudo systemctl status containerd
* containerd.service - containerd container runtime
     Loaded: loaded (/usr/local/lib/systemd/system/containerd.service; enabled; vendor preset: enabled)
     Active: inactive (dead) since Fri 2023-04-14 19:59:51 JST; 3min 55s ago

アクセスを確認する

これでKubernetesが停止したと言える状態になりましたので、あらためてnginxへアクセスしてみましょう。無事動き続けていることが確認できます。御覧の通り、ワークロードは止まらないのです。

$ curl -v http://133.xxx.yy.zzz/
< HTTP/1.1 200 OK
< Server: nginx/1.21.6
< Date: Fri, 14 Apr 2023 11:00:26 GMT

止めると見えてくるKubernetesの正体

それでは、今度は止めたプロセスやコンテナを起動して復旧させます。一般的には、動き続けていたシステムを再起動すると、思わぬ影響があり正常に起動しないなどということがありますが、Kubernetesでそのようなことはまず起こりません。また、ワークロードに影響を与えずに止められる、そして起動するだけで復旧するということは、ワークロードへの影響を最小限にKubernetesのメンテナンスが可能であることを意味します。これは長期的に運用していくにあたり、大変重要なことです。

$ sudo systemctl start containerd
$ sudo systemctl start kubelet
$ sudo nerdctl ps --namespace k8s.io | grep 'kube-system/kube-'
3a5095e5f3c6    registry.k8s.io/kube-apiserver:v1.25.4             "kube-apiserver --ad…"    14 seconds ago    Up                 k8s://kube-system/kube-apiserver-ivm27656206/kube-apiserver
4fbdc497c27f    registry.k8s.io/kube-controller-manager:v1.25.4    "kube-controller-man…"    14 seconds ago    Up                 k8s://kube-system/kube-controller-manager-ivm27656206/kube-controller-manager
e0d02fac8417    registry.k8s.io/kube-scheduler:v1.25.4             "kube-scheduler --au…"    14 seconds ago    Up                 k8s://kube-system/kube-scheduler-ivm27656206/kube-scheduler

このようにKubernetesを止めても稼働中のワークロードにクリティカルな影響がないことを確認できましたが、以下の機能は失われています。

  • 新規にワークロードをデプロイできなくなる
  • コンフィグレーションの更新ができなくなる
  • セルフヒーリングが機能しなくなる

状態を更新できず、ヘルスチェックが働かなくなるということです。そう考えると、これこそがKubernetesが何者かを端的に表していると思いませんか? KubernetesはハイパーバイザやOSとは違って何かをホストするわけではなく、かつて人力で行っていたシステムの構築やコンフィグレーションを極めてインテリジェントかつオートマチックに実行してくれる、どちらかと言えばツールと表現する方がしっくりくるような存在なのです。

こんな実験をすると、Kubernetesがよくわからないブラックボックスではなく、身近な存在に感じられるようになったのではないでしょうか。

田口 景介

2023年05月31日 水曜日

社会人生活の半分をフリーランス、半分をIIJで過ごすエンジニア。元々はアプリケーション屋だったはずが、クラウドと出会ったばかりに半身をインフラ屋に売り渡す羽目に。現在はコンテナ技術に傾倒中だが語りだすと長いので割愛。タグをつけるならコンテナ、クラウド、ロードバイク、うどん。

Related
関連記事