この記事では、自宅PC2台を使ってKubernetesクラスタを構築したときに実行したコマンドやトラブル、その対処法を書き連ねていく。
前回の記事はこちら。
なお、今回はワーカノードにはすでにkubeadm, kubectl, kubeletなどのコマンドはインストールしてあり、ポート類の開放もしている。そのあたりの手順は公式ドキュメントを参照されたい。
ネットワークアドオンの追加
Pod同士が通信できるようにするためには、CNI(Container Network Interface) を含めたネットワークアドオンをデプロイする必要がある。これはKubernetes本体には含まれておらず、外部からインストールする必要がある。
今回は、とりあえずFlannelを使用する。
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
ワーカノードの追加
その1までのステップでは、コントロールプレーンノードに対して設定を行ってきたが、ここからは2代目のパソコンで操作を行う。
公式ドキュメントによると、下記のコマンドをワーカノードとするPC上で実行することでKubernetesクラスタにワーカノードとして追加できるというのだが、kubeadm init 時に見たトークンを忘れてしまった。
sudo kubeadm join --token <token> <control-plane-host>:<control-plane-port> --discovery-token-ca-cert-hash sha256:<hash>
というわけなので、今回はコントロールプレーンノードでトークンを再作成する。 kubeadm token create --print-join-command というコマンドを実行したところ、ワーカノードで実行するコマンドは何を打てばいいかというレベルで教えてくれた。 このコマンドの先頭に sudo を付けて実行する。
yuritani@yuritani-PC-VN370FS6R:~$ kubeadm token create --print-join-command kubeadm join 192.168.11.101:6443 --token 9zmnmv.000u4e6sbj1qm04f --discovery-token-ca-cert-hash sha256:61abf5dd057f0ad50ee9153d116d09366f430e53daeb4e63040e810d09b5111f
このような実行結果が得られる。
yuritani@yuritani-server:~$ sudo kubeadm join 192.168.11.101:6443 --token 9zmnmv.000u4e6sbj1qm04f --disc overy-token-ca-cert-hash sha256:61abf5dd057f0ad50ee9153d116d09366f430e53daeb4e63040e810d09b5111f [sudo] yuritani のパスワード: [preflight] Running pre-flight checks W0427 13:45:54.720190 7490 checks.go:1065] [preflight] WARNING: Couldn't create the interface used for talking to the container runtime: failed to create new CRI runtime service: validate service connection: validate CRI v1 runtime API for endpoint "unix:///var/run/containerd/containerd.sock": rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService [preflight] Reading configuration from the "kubeadm-config" ConfigMap in namespace "kube-system"... [preflight] Use 'kubeadm init phase upload-config --config your-config-file' to re-upload it. [kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml" [kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env" [kubelet-start] Starting the kubelet [kubelet-check] Waiting for a healthy kubelet at http://127.0.0.1:10248/healthz. This can take up to 4m0s
エラー原因の調査
しかし、エラーが出てうまくいかないので
ところが、以下のようなエラーが出てきた。
ワーカノード側のエラーの内容
Unfortunately, an error has occurred, likely caused by:
- The kubelet is not running
- The kubelet is unhealthy due to a misconfiguration of the node in some way (required cgroups disabled)
If you are on a systemd-powered system, you can try to troubleshoot the error with the following commands:
- 'systemctl status kubelet'
- 'journalctl -xeu kubelet'
error execution phase kubelet-wait-bootstrap: failed while waiting for the kubelet to start: The HTTP call equal to 'curl -sSL http://127.0.0.1:10248/healthz' returned error: Get "http://127.0.0.1:10248/healthz": context deadline exceeded
どうやらコントロールプレーンノード側との接続を試したがタイムアウトしてしまったようだ。
末尾のこのエラーメッセージについてGPT-4.1に聞いたところ下記のような返答が返ってきた。
error execution phase kubelet-wait-bootstrap: failed while waiting for the kubelet to start: The HTTP call equal to 'curl -sSL http://127.0.0.1:10248/healthz' returned error: Get "http://127.0.0.1:10248/healthz": context deadline exceeded
AIの返答
このエラーは、kubeadm joinやinit時に「kubeletの起動待ち」でタイムアウトし、kubeletのヘルスチェック(http://127.0.0.1:10248/healthz)にアクセスできなかったことを示します。 主な原因と対策は以下の通りです。 kubeletが起動していない、または異常終了している systemctl status kubelet で状態を確認し、必要なら systemctl restart kubelet で再起動してください。 journalctl -u kubelet でエラーログを確認してください。 swapが有効になっている Kubernetesはswapが有効だと起動しません。swapoff -a で無効化し、/etc/fstabも修正してください。 必要なカーネルモジュールやsysctl設定が不足 br_netfilterなどのモジュールがロードされているか確認 sysctl net.bridge.bridge-nf-call-iptables=1 などの設定を確認 ポート競合やファイアウォール 10248ポートが他のプロセスで使われていないか、またはファイアウォールでブロックされていないか確認 kubeletの設定ファイルや引数のミス /var/lib/kubelet/config.yaml などの設定ファイルに誤りがないか確認 まずはkubeletの状態とログを確認し、swapやカーネル設定も見直してください。 追加のエラーログがあれば、さらに詳しくアドバイスできます。
kubeletは動いている
127.0.0.1 ということは、ローカルホストの/healthz10248ポートを使ってアクセスしようとして失敗している。ということはワーカノード側の問題。先ほどのコントロールプレーンノード側の問題は関係がない。
kubeletが動作していない可能性を考え、 systemctl status kubelet を実行してみると、案の定ステータスは activating であり、動作していない。
なぜkubeletがactivating状態のままなのか不明だが、ひとまずkubeletを再起動してみる。
sudo systemctl restart kubelet
ダメだ。それでもactiveにならない。
swapもoffになっている
AIのアドバイス通りに、free -h と swapon --show でswapがオンになっていないかどうかを確認してみるが、swapはオンになっていない。
コンテナランタイムはインストールされている
AIの2つ目以降のアドバイスが曖昧なので、もう一度公式ドキュメントのパッケージインストール手順に立ち返ってみる。
必要とされているコンテナランタイムである containerd はちゃんとactiveになっている。
containerdの状態確認結果
yuritani@yuritani-server:~$ systemctl status containerd ● containerd.service - containerd container runtime Loaded: loaded (/usr/lib/systemd/system/containerd.service; enabled; pres> Active: active (running) since Mon 2026-04-27 12:42:25 JST; 7h ago Docs: https://containerd.io Main PID: 1227 (containerd) Tasks: 9 Memory: 53.2M (peak: 54.3M) CPU: 36.202s CGroup: /system.slice/containerd.service └─1227 /usr/bin/containerd
コンテナランタイムの動作に必要な設定ができていない
では、コンテナランタイムインストールの必須条件は満たしているかを確認してみる。
満たしていない。 lsmod | grep br_netfilter を実行しても何も返ってこない。これではインストール要件である「IPv4フォワーディングを有効化し、iptablesからブリッジされたトラフィックを見えるようにする」を満たしていないことになる。
また、コンテナランタイムの動作に必要なcgroupドライバーの設定もできていない。
エラー原因への対処: コンテナランタイムが動くための設定
IPv4フォワーディングを有効化する
公式ドキュメントにあるコマンドをもう一度実行する。
実行したコマンド
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter # この構成に必要なカーネルパラメーター、再起動しても値は永続します cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF # 再起動せずにカーネルパラメーターを適用 sudo sysctl --system
有効化後の確認がこちら。
yuritani@yuritani-server:~$ lsmod | grep br_netfilter br_netfilter 32768 0 bridge 425984 1 br_netfilter yuritani@yuritani-server:~$ lsmod | grep overlay overlay 212992 0 yuritani@yuritani-server:~$ sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1
cgroupドライバーを設定
Linuxでは、コンテナランタイムはコンテナのリソースを管理するために、cgroupと連携する。そのために、コンテナランタイムは「cgroupドライバー」というものを経由してcgroup連携するための設定をしている必要がある。詳しくは公式ドキュメントを参照。
cgroupドライバーは大きく2種類ある。
- cgroupfs
- systemd
ここで、Linux上のinitシステムとしてsystemdを使用しているときは、2つのドライバーがあるとリソース管理に矛盾を生むため、systemdをcgroupドライバーとして使わないといけない。
今回の環境であるUbuntu 24.04 LTSでは、initシステムとしてsystemdを使用していた。
yuritani@yuritani-server:~$ ps -p 1 -o comm= systemd
この場合は、コンテナランタイムがcgroupドライバーとしてsystemdを使用できるようにするため、下記の2つの設定をする必要がある。
- kubelet側の設定:
/var/lib/kubelet/config.yaml - containerd側の設定
/etc/containerd/config.toml
の2つを設定する必要がある。
今回の環境では、すでにKubeletConfigurationの /var/lib/kubelet/config.yaml では cgroupDriver: systemd が設定されていた。
続いて、/etc/containerd/config.toml の設定を公式ドキュメントに沿って下記のように行う。
なお、containerdのバージョンは2.x系である。
[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc]
[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc.options]
SystemdCgroup = true
このうえで、sudo systemctl restart containerd containerdを再起動する。
コマンドの再実行
これで動くはず。コマンドを再実行してみる。
sudo kubeadm join 192.168.11.101:6443 --token 9zmnmv.000u4e6sbj1qm04f --discovery-token-ca-cert-hash sha256:61abf5dd057f0ad50ee9153d116d09366f430e53daeb4e63040e810d09b5111f
しかし、このような結果が返ってくる。
エラー全文
[preflight] Running pre-flight checks W0502 20:33:44.467517 54760 checks.go:1065] [preflight] WARNING: Couldn't create the interface used for talking to the container runtime: failed to create new CRI runtime service: validate service connection: validate CRI v1 runtime API for endpoint "unix:///var/run/containerd/containerd.sock": rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService error execution phase preflight: [preflight] Some fatal errors occurred: [ERROR FileAvailable--etc-kubernetes-kubelet.conf]: /etc/kubernetes/kubelet.conf already exists [ERROR FileAvailable--etc-kubernetes-pki-ca.crt]: /etc/kubernetes/pki/ca.crt already exists [preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...` To see the stack trace of this error execute with --v=5 or higher
ここで指摘されているのは下記の通り、1つの警告と2つのエラー。
- 警告: コンテナランタイムと通信するのに使われるインターフェースを作ることができなかった。なぜなら、
runtime.v1.RuntimeServiceという不明なランタイムサービスが原因で、とあるエンドポイントに対してCRI v1 ランタイムAPIのバリデーションが通らなかったから。 - エラー:
/etc/kubernetes/kubelet.conf already existsが既に存在する。 - エラー:
/etc/kubernetes/pki/ca.crtが既に存在する。
kubelet.confやca.crtが既に存在してはまずいのはなぜだろうか。
CRIランタイムの設定の修正
原因は、下記のCRIプラグインがデフォルトの config.toml で無効化されていたからだった。
disabled_plugins = ["cri"]
上記の行を削除した。
すでに作られたconfファイルとcrtファイルの削除
前回のjoinコマンド実行時に作成されてしまったであろうファイルは、一度削除しなければならない。これは単純にrmするだけでよい。
sudo rm /etc/kubernetes/kubelet.conf sudo rm /etc/kubernetes/pki/ca.crt
※ここは sudo kubeadm reset でもいいらしい。
再試行
上記の対処を行ったのち、コマンドを再試行したところ、きちんとワーカノードをクラスタに追加することができた。
yuritani@yuritani-server:~$ sudo kubeadm join 192.168.11.101:6443 --token <トークン> --discovery-token-ca-cert-ha sh sha256:61abf5dd057f0ad50ee9153d116d09366f430e53daeb4e63040e810d09b5111f [sudo] yuritani のパスワード: [preflight] Running pre-flight checks [preflight] Reading configuration from the "kubeadm-config" ConfigMap in namespace "kube-system"... [preflight] Use 'kubeadm init phase upload-config --config your-config-file' to re-upload it. [kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml" [kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env" [kubelet-start] Starting the kubelet [kubelet-check] Waiting for a healthy kubelet at http://127.0.0.1:10248/healthz. This can take up to 4m0s [kubelet-check] The kubelet is healthy after 503.065006ms [kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap This node has joined the cluster: * Certificate signing request was sent to apiserver and a response was received. * The Kubelet was informed of the new secure connection details. Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
クラスタにワーカノードが追加されたことの確認
下記のコマンドをコントロールプレーンノードで実行し、ワーカノードがクラスタに追加されたことを確認した。
yuritani@yuritani-PC-VN370FS6R:~$ kubectl get nodes NAME STATUS ROLES AGE VERSION yuritani-pc-vn370fs6r Ready control-plane 63d v1.33.0 yuritani-server Ready <none> 23m v1.33.0
感想
今回の記事の内容はGitHub CopilotやClaude Codeに相談しながら実行したのでうまく言った感がある。公式ドキュメントを見ただけだとエラーが出たときの原因調査に時間がかかるが、AIが出てきてこういうのはだいぶ楽になった。
ただ、昔ならばいろいろと調べて試行錯誤する過程で知識を得ることができたが、今は一発でうまくいくやり方が出てきてしまう。そのため、別で知識の補充は行う必要は出てきそう。