Kubernetesを止めてもワークロードは止まらない?
2023年05月31日 水曜日
CONTENTS
もう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がよくわからないブラックボックスではなく、身近な存在に感じられるようになったのではないでしょうか。