TT Lab
はじめる
学ぶ 学習パス コース

Kubernetesディストリビューション — 自分で立てる

入る場所のない OS — Talos がシェルをなくした理由

TT Labで続きを見る

一言でいうと

Talos Linuxは、シェル・SSH・パッケージなしでKubernetesだけを動かすOSで、ノードに入る代わりに、ノードごとに開いているgRPC APIですべてを読み取り、変更します。

なぜ必要なのか

ノードが30台ほどになると、必ずこういうことが起きます。障害のとき、誰かが1台のノードにSSHで入ってsysctlを変更し、kubeletの引数を直して急場をしのぎます。記録は残らず、3か月後にそのノードだけがおかしな動きをします。構成管理ツールを導入しても、「管理ツールの外で手を加えたもの」は依然として可能です。

Talosはこの問題を、ルールではなく構造で防ぎます。思想のドキュメントは、シェルも、SSHも、GNUユーティリティも、busyboxさえないと書いています。カーネルが起動する最初のプロセスはsystemdではなくGoで書かれたmachinedで、ルートファイルシステムはSquashFSなので書き込めません。入る場所がないので、手を加えることもできません。その代わり、ノードのすべての状態は1つの宣言的なマシン設定で決まり、変更する道はAPI 1つです。

どう動くのか

構成要素は少ないです。apidがポート50000でgRPCリクエストを受け取ってmachinedに渡し、コントロールプレーンなら他のノードのapidへの中継も行います。machinedは決められたサービス(containerd・etcd・kubelet・trustdなど)だけを動かし、任意のユーザーサービスは受け付けません。talosctlはこのAPIのクライアントで、-eはどのノードのapidに入るか、-nはどのノードが答えるかを決めます。

内部状態は、Kubernetesのようにリソースとコントローラーで表されます。サービス・クラスターのメンバー・マシン設定がすべてリソースなので、同じコマンドで読み取れます。

talosctl -n 10.5.0.2 services                    # machined 가 돌리는 서비스
talosctl -n 10.5.0.2 get members                 # 클러스터 멤버(discovery)
talosctl -n 10.5.0.3 get mc v1alpha1 -o yaml     # 머신 설정도 버전이 붙은 리소스
talosctl -n 10.5.0.3 logs kubelet                # 셸 대신 로그도 API 로

設定を変更するコマンドはapply-config・edit machineconfig・patch machineconfigの3つで、適用方式は--modeで選びます。設定編集のドキュメント(v1.13)によると、autoは必要なら再起動し、no-rebootは即座に適用しますが再起動が必要なフィールドなら拒否し、stagedは次の再起動で適用し、tryは即座に適用したあと、決められた時間内に他の変更がなければ元に戻します。nodeLabels・kubelet・networkなどの項目は、再起動なしで反映される一覧に入っています。パッチは設定の一部だけを書いたstrategic merge YAMLで、リストは通常は追加されますが、cluster.network.podSubnetsとserviceSubnetsは上書きされます。特定のキーは$patch: deleteで削除します。

ローカルでは、Dockerの案内のとおりtalosctl cluster create dockerでノードをコンテナとして起動できます。コンテナモードでは、upgradeやresetのようなAPIは適用されません。

現場での姿

このコースのVMで実測したものです。ノードのコンテナにdocker exec ... shを実行すると、exit 127でexecutable file not foundが出て、ノードIPのポート22は拒否され、ポート50000だけが開いています。習慣どおりの「とりあえず入ってみよう」が、最初の一手で止まります。

CIDR範囲の落とし穴もそのまま現れました。talosctlが作るデフォルトのCIDR範囲はPodが10.244.0.0/16、Serviceが10.96.0.0/12ですが、このVM自身のアドレスがホストクラスターの10.244の範囲にあります。そのため--config-patchで10.200と10.201を指定して起動しました。

バージョンの選択も実測で決めました。talosctl v1.14.0のデフォルトイメージで起動すると、ノードのログに/etc overlayを作る途中でFSCONFIG_SET_FD failed ... lowerdir+が出て、APIが最後まで準備できませんでした(ホストカーネル6.8)。同じVMでv1.13.10は、約2分で2台のノードがReadyになりました。コンテナの中のTalosはホストカーネルをそのまま使うので、バージョンを上げるときは、ホストカーネルがまず条件になります。

最も重要な実測は、検証の限界です。ラベル名に空白を入れたパッチはInvalidArgumentで拒否され、設定のバージョンはそのままです。一方、kubeletのextraArgsに存在しないフラグを入れると「Applied configuration without a reboot」として受け入れられ、kubeletがunknown flagで5秒ごとに再起動します。検証はドキュメントの形式を見るだけで、kubeletがそのフラグを知っているかどうかまでは知りません。シェルがないので、原因はtalosctl servicesのLAST EVENTとtalosctl logs kubeletで探し、$patch: deleteでそのキーだけを削除すると、3秒以内にkubeletが正常になりました。

実務で本当に大切なこと

マシン設定ファイルが、そのままノードです。ノードで直したものはなく、設定リソースのversionが変更履歴の骨格です。パッチファイルをリポジトリに置き、同じファイルで複数のノードに適用する流れが自然になります。

tryモードを、リモート変更の命綱として使います。ネットワークのように、間違って変更するとAPIに再び届かなくなるおそれのある設定はtryで入れ、確認できたら同じ変更をもう一度適用して確定させます。確認している間に別の変更を入れると、元に戻す処理が取り消される点も覚えておく必要があります。

検証を通過したことを、成功と読み取りません。適用後は、サービスの状態とノードのReadyを必ず見直します。そして--dry-runは、合成した結果を見せるだけで検証はしません(実測)。

応急処置の手段がAPIだけであることを、受け入れます。apidに届くネットワークとtalosconfigの証明書を失うと、ノードを直す方法が事実上なくなります。証明書の保管とアクセス経路が、SSHキーの管理よりも重要になります。

次のラボですること

VM内にDockerで起動したTalosノード2台で、シェルがないことを証拠として残し、サービス・メンバー・マシン設定を読み取り、kubeconfigをAPIで取得してCIDR範囲を確認します。そのあと、ワーカーにno-rebootでラベルを付け、tryで付けたラベルが30秒後に元に戻るのを見ます。最後に、検証が拒否するパッチと、受け入れられたもののkubeletを止めてしまうパッチを順に入れ、ログで原因を探して元に戻し、レポートにまとめます。