Certified Kubernetes Administrator (CKA) #4 kubeadm でのクラスターインストール: 単一 control plane のブートストラップ
#3 ノードと Pod ネットワーキングモデル では、kubelet と kube-proxy、CRI がノードでどう噛み合うかを整理しました。今度はそのコンポーネントを実際にインストールして、空の Linux マシンを Kubernetes クラスターにする 番です。クラウドのマネージドクラスターは control plane を隠してくれますが、CKA はその control plane を自分で立てる能力を見ます。この記事では kubeadm で単一 control plane クラスターをゼロからブートストラップします。
kubeadm はクラスターを立てる公式のブートストラップツールです。apiserver・etcd・scheduler・controller-manager を static Pod として起動し、証明書を発行し、ワーカーが付くためのトークンを作ってくれます。試験でよく出る作業がまさにこの kubeadm init と kubeadm join なので、コマンド一つひとつを正確に手に馴染ませます。
大きな絵: kubeadm がやること #
kubeadm でクラスターを立てる手順は、マシンごとに次に分かれます。
- すべてのノード共通の事前準備。swap の無効化、カーネルモジュール、sysctl、コンテナランタイム、kubeadm・kubelet・kubectl のインストール
- control plane ノードでのブートストラップ。
kubeadm initで apiserver・etcd・scheduler・controller-manager を static Pod として起動 - CNI のインストール。Pod ネットワークプラグインを入れて初めてノードが Ready になり、Pod 同士が通信できます
- ワーカーノードのジョイン。各ワーカーで
kubeadm joinを実行して control plane に合流
control plane もワーカーも 1 段階までは同じように準備します。2 段階から役割が分かれます。一つずつコマンドで追っていきます。
1. 事前準備 (すべてのノード共通) #
control plane でもワーカーでも、次の準備を同じように行います。抜けた段階が一つでもあると kubeadm init や kubelet が起動に失敗するので、順番に確認していきます。
swap の無効化 #
kubelet はデフォルト設定では swap が有効だと起動を拒否します。メモリ圧迫時の動作を予測可能にするためです。まず現在のセッションで切り、再起動後も切れたままになるよう /etc/fstab の swap 行をコメントアウトします。
# 即座に swap を切る
sudo swapoff -a
# 再起動後も維持 (fstab の swap 項目をコメントアウト)
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
# 確認 (出力が空であること)
swapon --show
free -hカーネルモジュール: overlay と br_netfilter #
コンテナランタイムの overlay ファイルシステムと、bridge を通過するトラフィックを iptables に通す br_netfilter が必要です。再起動後も自動ロードされるよう設定ファイルに書き、今のセッションにも即座にロードします。
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
# ロード確認
lsmod | grep -E 'overlay|br_netfilter'sysctl: ブリッジトラフィックと IP フォワーディング #
br_netfilter がロードされた状態で、bridge を通るトラフィックが iptables ルールを経由するようにし、ノードの IP フォワーディングを有効にします。CNI と kube-proxy が正常に動作するための前提です。
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
# 確認 (3 つとも 1 であること)
sysctl net.ipv4.ip_forward net.bridge.bridge-nf-call-iptablesコンテナランタイム: containerd #
#3 で見たとおり、Kubernetes は CRI を通じてランタイムと通信します。最も広く使われる containerd をインストールし、kubelet と同じ cgroup ドライバー (systemd) を使うよう設定します。この cgroup ドライバーの不一致が、初心者が最もよく遭遇する失敗の原因です。
# containerd のインストール (ディストリのパッケージまたは公式バイナリ)
sudo apt-get update && sudo apt-get install -y containerd
# デフォルト設定の生成
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null
# systemd cgroup ドライバーを使用 (kubelet と一致させる)
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
# 再起動と起動時の自動起動
sudo systemctl restart containerd
sudo systemctl enable containerdkubeadm、kubelet、kubectl のインストール #
3 つのパッケージを公式リポジトリからインストールします。バージョンを固定 (hold) しておくと、意図しない自動アップグレードでクラスターが揺らぐのを防げます。アップグレードは #6 で意図的に扱います。
# リポジトリ利用のためのパッケージ
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
# Kubernetes apt リポジトリのキーとソースを追加 (バージョンは受験時点に合わせる)
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key \
| sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.31/deb/ /' \
| sudo tee /etc/apt/sources.list.d/kubernetes.list
# インストール後にバージョン固定
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
# kubelet を起動時に自動起動 (init 前は crashloop 状態が正常)
sudo systemctl enable --now kubeletここまでがすべてのノード共通です。ワーカーノードはこの状態で止め、control plane ノードで次の段階に進みます。
2. control plane のブートストラップ: kubeadm init #
control plane ノード 1 台で kubeadm init を実行します。このコマンド一つが証明書を発行し、control plane コンポーネントを static Pod として起動し、etcd を起動し、ワーカーが付くためのトークンを作ります。
sudo kubeadm init \
--pod-network-cidr=192.168.0.0/16 \
--apiserver-advertise-address=10.0.0.102 つのオプションの意味を押さえます。
--pod-network-cidr。Pod に割り当てる IP 帯域です。後でインストールする CNI が期待する帯域と一致 させる必要があります。Calico のデフォルトが192.168.0.0/16、Flannel のデフォルトが10.244.0.0/16です。--apiserver-advertise-address。apiserver が広告するノードの IP です。ノードにインターフェースが複数ある場合は明示する方が安全です。
kubeadm init が成功すると、最後に 2 つ を出力します。どちらもそのままコピーしておく必要があります。
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 10.0.0.10:6443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:1234...cdefkubeconfig の設定 #
上記出力の最初のブロックをそのまま実行して初めて、kubectl がクラスターに接続できます。admin.conf を一般ユーザーの ~/.kube/config にコピーする作業です。
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 接続確認 (control plane ノードが 1 行見えること)
kubectl get nodesこの時点で kubectl get nodes を実行すると control plane ノードが 1 つ見えますが、状態は NotReady です。まだ CNI を入れていないためであり、正常です。
3. CNI のインストール: ノードを Ready に #
Kubernetes は Pod ネットワークプラグイン (CNI) を自身では提供しません。別途インストールして初めてノードが Ready になり、Pod 同士が通信できます。CNI を入れるまでは CoreDNS Pod も Pending に留まります。
Calico のインストール例 #
# Calico マニフェストの適用
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml
# CNI Pod が Running になる過程を観察
kubectl get pods -n kube-system -wFlannel のインストール例 #
Flannel を使うなら、kubeadm init を --pod-network-cidr=10.244.0.0/16 で実行している必要があります。マニフェストと CIDR がずれると Pod が IP をもらえません。
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.ymlCNI Pod がすべて Running になると、ノード状態が NotReady から Ready に変わります。
# しばらくすると Ready に切り替わる
kubectl get nodes4. ワーカーノードのジョイン: kubeadm join #
各ワーカーノードで、kubeadm init の出力にあった kubeadm join コマンドを root で 実行します。ワーカーも 1 段階の事前準備がすべて終わっている必要があります。
sudo kubeadm join 10.0.0.10:6443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:1234...cdefコマンドの 3 つの断片がそれぞれ信頼関係を作ります。
10.0.0.10:6443。ワーカーが接続する apiserver のアドレスとポートです。--token。ブートストラップトークンです。ワーカーが自身を証明するのに使います。デフォルトの有効期間が 24 時間 なので失効に注意します。--discovery-token-ca-cert-hash。control plane の CA 証明書のハッシュです。ワーカーが接続した apiserver が本当に自分たちのクラスターかを検証します。
トークンを失くした、または失効したとき #
kubeadm init の出力を取り損ねた、または 24 時間が過ぎたなら、control plane ノードで join コマンドを新たに発行します。この 1 行がトークンと CA ハッシュをすべて含んだ完成版の join コマンドを出力します。
# control plane ノードで: 完成版の join コマンドを再発行
kubeadm token create --print-join-commandトークンだけを別に作ったり、現在生きているトークンを確認したりもできます。
# 新しいトークンだけを生成
kubeadm token create
# 現在有効なトークン一覧
kubeadm token list5. 検証 #
ジョインが終わったら control plane ノードでクラスター全体の状態を確認します。CKA では作業後にこの検証を必ず通さないと点数を落とします。
# すべてのノードが Ready か
k get nodes
# control plane コンポーネントと CNI、CoreDNS が Running か
k get pods -n kube-system正常なら control plane とワーカーがすべて Ready で、kube-system ネームスペースの apiserver・etcd・scheduler・controller-manager・kube-proxy・CoreDNS・CNI Pod がすべて Running で見えます。
NAME STATUS ROLES AGE VERSION
controlplane Ready control-plane 8m v1.31.0
node01 Ready <none> 3m v1.31.0ノードの中で kubelet 自体の状態を確認するには systemctl と journalctl を使います。#1 でセットアップしたコマンドです。
systemctl status kubelet
journalctl -u kubelet -fよくある落とし穴 #
インストール作業で点数を落とすパターンはほぼ決まっています。
- CNI を入れずノードが NotReady。
kubeadm init直後にノードがNotReadyなのは正常ですが、CNI を入れないと永遠にNotReadyのままです。CoreDNS もPendingに留まります。インストールを全部終えたのにノードが Ready にならないなら、まずkubectl get pods -n kube-systemで CNI Pod を確認します。 - swap が有効で kubelet が起動しない。
kubeadm initが事前点検 (preflight) で swap エラーに止まります。swapoff -aを抜かしたか、再起動後に/etc/fstabの処理をしていない場合です。 - トークンの失効。ブートストラップトークンの有効期間が 24 時間なので、時間が過ぎた後にワーカーを付けようとすると認証に失敗します。
kubeadm token create --print-join-commandで新たに発行します。 - cgroup ドライバーの不一致。containerd が cgroupfs、kubelet が systemd のように互いに異なる cgroup ドライバーを使うと、kubelet がコンテナを起動できません。containerd の
SystemdCgroup = true設定を確認します。 - pod-network-cidr と CNI の不一致。
kubeadm initに与えた CIDR と CNI マニフェストが期待する帯域がずれると Pod が IP をもらえません。Calico は192.168.0.0/16、Flannel は10.244.0.0/16をデフォルトに合わせます。 - CRI ソケットの未設定。ランタイムが複数入っている場合は
kubeadm init --cri-socket unix:///run/containerd/containerd.sockのように明示する必要があります。
失敗した init を巻き戻すには #
設定を間違えて最初からやり直したいときは、kubeadm reset でノードを初期状態に戻します。
sudo kubeadm reset -f
sudo rm -rf /etc/cni/net.d $HOME/.kube/config試験ポイント #
kubeadm initは control plane を static Pod として起動します。apiserver・etcd・scheduler・controller-manager のマニフェストは/etc/kubernetes/manifests/にあります。kubeadm init直後の出力の 2 ブロック (kubeconfig 設定と join コマンド) を両方とも確保します。join コマンドを逃したらkubeadm token create --print-join-commandで再発行します。- ノードが
NotReadyなら、まず CNI のインストール有無 を疑います。CNI なしではノードが Ready になりません。 - 事前準備 4 種 (swap off、カーネルモジュール、sysctl、コンテナランタイム) が抜けると init や kubelet が起動に失敗します。
--pod-network-cidrはインストールする CNI のデフォルト帯域と一致させます。- kubelet の状態は
systemctl status kubeletとjournalctl -u kubeletで追跡します。
まとめ #
この記事で押さえたこと:
- 事前準備 (すべてのノード)。swap の無効化、カーネルモジュール (overlay・br_netfilter)、sysctl (ip_forward・bridge-nf)、containerd のインストールと systemd cgroup、kubeadm・kubelet・kubectl のインストール
- control plane のブートストラップ。
kubeadm init --pod-network-cidr=...、出力の kubeconfig 設定 (mkdir -p ~/.kube; cp ...) と join コマンドの確保 - CNI のインストール。Calico や Flannel のマニフェストを適用して初めてノードが Ready
- ワーカーのジョイン。
kubeadm join(トークン + discovery hash)、失効時はkubeadm token create --print-join-commandで再発行 - 検証と落とし穴。
k get nodes・k get pods -n kube-systemで確認し、NotReady・swap・トークンの失効・cgroup の不一致を点検
次へ: HA クラスター #
単一 control plane クラスターを立てました。ところが control plane ノードが 1 台だけだと、その 1 台が落ちた瞬間にクラスター管理が止まります。
#5 HA クラスター: 複数 control plane、外部 etcd cluster では、control plane を複数台に増やして可用性を確保する構成を扱います。apiserver の前にロードバランサーを置く方法、kubeadm init に --control-plane-endpoint を与えて追加の control plane が付くようにする方法、そして etcd を control plane に一緒に置く stacked 構成と別クラスターに分離する external etcd 構成の違いまで整理します。