はじめに
自分でkubernetesクラスタを構築する際の備忘録。VirtualBox+vagrantで構築した場合。
基本的にkubernetesマニュアルのkubeadmを使ってクラスターを構築するをベースにvagrant固有の差分や他の方が記載された記事を反映。
kubernetesクラスタ構成
MasterノードとWorkerノードをそれぞれ1台構成とする。
ちなみにMasterノードはコントロールプレーン
とかAdminノード
など色々書かれているが、ここではMasterノード
で統一して書く。
kubernetesクラスタ構築の流れ
以下の流れでkubernetesクラスタを構築する。
Workerノードで実施するkubernetesクラスタに参加
は、Masterノードで実施するPodネットワークアドオンのインストール
終了後に実施すること。

インストールするソフトウェアとバージョン
- ゲストVM: ubuntu22.04
- Masterノード:192.168.56.53
- Workerノード:192.168.56.54
- コンテナランタイム: containerd2.1.1
- runc: 1.2.6
- CNI Plugin: 1.7.1
- cgroupドライバ: systemd cgroupドライバ
- kubernetes: 1.33
- Podネットワークアドオン: Calico v3.30.0
いざ、インストール
MasterノードとWorkerノードで共通の手順、MasterノードやWorkerノードどちらか一方で実施する手順については、Masterノードで実施
、Workerノードで実施
の識別子をつけて区別できるようにする。
インストールするVMがない場合
手元にインストールするVMがないとき、VirtualBox+vagrant
が利用可能ならkubernetesクラスタ構成に示したVM用のVagrantfileをGitHubのレポジトリから持ってきてゲストVMを構築する。
# 作業ディレクトリで実行
git clone https://github.com/nkoseki/vagrant.git
cd vagrant
# Masterノード構築用
cd 10.master
vagrant up
# Workerノード構築用
cd ../11.worker1/
vagrant up
VMのプロビジョニングはタイムゾーンや日本語化、vimの設定を実施している。起動したらvagrant ssh
でログイン。
コンテナランタイムのインストール
Masterノードで実施
、Workerノードで実施
何はともあれVM上でコンテナの動く環境が必要。コンテナ単体で動作させる場合はDockerをコンテナランタイムとして使うケースもあるが、kubernetesが利用するコンテナランタイムとしてはメジャーなcontainerdを使う。
containerdの場合、Dockerのよう直接的なコマンドラインツールはないため、crictlやnerdctlなどの補助ツールを使用してコンテナ操作をする必要があるが、今回補助ツールはインストールしない。
IPv4フォワーディングを有効化
VMのIPv4フォワーディングを有効化し、iptablesからブリッジされたトラフィックを見えるようにする。
# カーネルモジュールの設定
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
カーネルモジュール、カーネルパラメータの確認
# カーネルモジュールがロードされたか確認
lsmod | grep -e br_netfilter -e overlay
# カーネルパラメータが正しく設定されたか確認
sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward
cgroupドライバーの選択
cgroupドライバとして、昔はcgroupfs
が利用されていたが、現在のLinuxの起動方式がsystemdになっているのでsystemd cgroupドライバ
にするのが普通なのか。kubernetesマニュアルにもsystemdが推奨とあるので素直に従う。
コンテナランタイムとkubeletで同じcgroupドライバを使う必要がある。ここではどのドライバにするか決めるだけで設定は後ほど実施する。
コンテナランタイムのインストール
コンテナランタイムとしてcontainerdをインストールする場合、下記をインストールする。
コンテナランタイム本体
高レベルのCNIインターフェースとも言う。containerd本体。プロセスとして動作しruncに指示をしたり、使用するcgroupドライバの種別を決める。
コンテナランタイムのサービス起動スクリプト
オプションとあるが、サービスとしてcontainerdを動かすので入れといた方がいい
runc
低レベルのCNIインタフェースとも言う。containerdから呼ばれて実際にコンテナを動かすプログラム
CNI Plugin
Podの通信用。VMをまたがったPod同士の通信はPodネットワークアドオン
としてCalicoで実現する。
Podネットワークアドオン
もCNI Plugin
と表現されている場合があるが、両者は別物。
コンテナランタイム本体のインストール
VER=2.1.1
curl -OL https://github.com/containerd/containerd/releases/download/v${VER}/containerd-${VER}-linux-amd64.tar.gz
sudo tar Cxzvf /usr/local containerd-${VER}-linux-amd64.tar.gz
# インストール確認
ls -l /usr/local/bin
コンテナランタイムのサービス起動スクリプトのインストール
curl -OL https://raw.githubusercontent.com/containerd/containerd/main/containerd.service
sudo cp ./containerd.service /lib/systemd/system/containerd.service
sudo systemctl daemon-reload
sudo systemctl enable --now containerd
# サービス起動確認
systemctl status containerd
低レベルCNIインタフェースのruncがインストールされていなくてもcontainerdが正常に起動される。
runcのインストール
VER=1.2.6
curl -OL https://github.com/opencontainers/runc/releases/download/v${VER}/runc.amd64
sudo install -m 755 runc.amd64 /usr/local/sbin/runc
# インストール確認
ls -l /usr/local/sbin
runcはサービスとして起動しているわけじゃなく、あくまでもcontainerdから呼ばれるプログラム。
Dockerコンテナでも低レベルCNIインタフェースとしてruncを利用している。
CNI Pluginのインストール
VER=1.7.1
curl -OL https://github.com/containernetworking/plugins/releases/download/v${VER}/cni-plugins-linux-amd64-v${VER}.tgz
sudo mkdir -p /opt/cni/bin
sudo tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v${VER}.tgz
# インストール確認
ls -l /opt/cni/bin/
コンテナランタイムとcgroupドライバの紐付け
containerdとsystemd cgrupドライバが紐付けていないので紐づける。
containerdをインストールしても設定変更用のconfig.toml
ファイルが出力されないので手動で出力する。containerdはデフォルトで/etc/containerd
配下を読み込むとのこと。
sudo mkdir /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
# 設定情報確認
cat /etc/containerd/config.toml
systemd cgroupドライバを利用するように書き換える。
sudo vi /etc/containerd/config.toml
# config.tomlファイル
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
...
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
# 下記を追加
SystemdCgroup = true
修正後、containerdを再起動
sudo systemctl restart containerd
コンテナランタイムのプロセス起動確認
正常にcontainerdが起動していることが確認できる。
補助ツールを入れると、この段階でコンテナのインストールや起動ができるはず(試したことない)
systemctl status containerd
kubeadm,kubelet,kubectlのインストール
一部Masterノード、Workerノードだけの手順があるので注意。
事前準備
Masterノードで実施
、Workerノードで実施
SwapをOFFにする
/etc/fstab
を書き換える
sudo vi /etc/fstab
# コメントアウトしてswap を永続的にOFFにする
# /swap.img none swap sw 0 0
再起動してSwapがOFFを有効にする
# 再起動
sudo shutdown -r now
再起動後、再度ログインしSwapのサイズが0になっていることを確認
free -m
total used free shared buff/cache available
Mem: 1983 124 1630 0 228 1711
Swap: 0 0 0
レポジトリ登録
aptのパッケージ更新と必要なパッケージインストール
# お約束
sudo apt-get update
# 必要なパッケージのインストール
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
Kubernetesパッケージリポジトリーの公開署名キーをダウンロード
# `/etc/apt/keyrings`フォルダーが存在しない場合は、curlコマンドの前に作成する必要があります。
# sudo mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.33/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
aptレポジトリを追加
# これで/etc/apt/sources.list.d/kubernetes.listにある既存の設定が上書きされます
VER=v1.33
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/${VER}/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
kubeadm,kubelet,kubectlのインストール
事前準備が終わったら、kubeadm等のツールインストール
Masterノードの場合
sudo apt-get update
# kubelet, kubeadm, kubectlのインストール
sudo apt-get install -y kubelet kubeadm kubectl
# バージョンを固定する
sudo apt-mark hold kubelet kubeadm kubectl
Workerノードの場合
Masterノードの実行コマンドからkubectlを取り除くだけだけど、一応書いておく。
sudo apt-get update
# kubelet, kubeadmのインストール。Workerノードなのでkubectlは不要
sudo apt-get install -y kubelet kubeadm
# バージョンを固定する
sudo apt-mark hold kubelet kubeadm
kubeletが使用するcgroupドライバの設定
containerdで使うのと同じsystemdを指定する。後述のkubeadm init
およびkubeadm join
実行時に参照される。
vagrantでゲストVMを作成した場合、VMに割り当てられるIPはHostOnlyネットワーク用のIPとNAT用IPの2つのIPが存在するため、--node-ip
でどちらのIPを利用するか明示する必要がある。
Masterノードの場合
cat << EOF | sudo tee /etc/default/kubelet
KUBELET_EXTRA_ARGS=--cgroup-driver=systemd --node-ip=192.168.56.53
EOF
Workerノードの場合
cat << EOF | sudo tee /etc/default/kubelet
KUBELET_EXTRA_ARGS=--cgroup-driver=systemd --node-ip=192.168.56.54
EOF
kubeletのプロセス再起動
Masterノードで実施
、Workerノードで実施
kubeletの起動オプションを変更したので、kubelet再起動
# kubeletの再起動
sudo systemctl daemon-reload
sudo systemctl restart kubelet
Masterノード、Workerノードとも、kubernetesの初期化が完了していないためkubeletは正常起動せず再起動を繰り返しているが問題ない.
kubeletの状態確認
vagrant@master:~$ systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor preset: enabled)
Drop-In: /usr/lib/systemd/system/kubelet.service.d
└─10-kubeadm.conf
Active: activating (auto-restart) (Result: exit-code) since Sun 2025-06-01 10:53:34 JST; 9s ago
Docs: https://kubernetes.io/docs/
Process: 3083 ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS (code=exited, status=1/FAILURE)
Main PID: 3083 (code=exited, status=1/FAILURE)
CPU: 94ms
kubernetesの初期化
Masterノードで実施
--pod-network-cidr
のアドレスはPodネットワークとして利用するアドレス帯、後述のPodネットワークアドオンのインストールで指定するCalicoのPodネットワークアドレスと同じにする。
またkubernetesマニュアルには明示的に指定しない限り、kubeadmはデフォルトゲートウェイに関連付けられたネットワークインターフェースを使用して、この特定のコントロールプレーンノードのAPIサーバーのadvertise addressを設定する
とあるので、今回構築する構成だとNATのIP10.0.2.15
が割当てられ、通信ができない。
このためマニュアルの異なるネットワークインターフェースを使用するには、kubeadm initに--apiserver-advertise-address=<ip-address>引数を指定
に従って設定。
--node-name
は'kubectl get node`で表示されるノード名、デフォルトではhostnameが割り当てられるためノード名を変更したい場合に利用。
sudo kubeadm init --pod-network-cidr=172.16.0.0/16 \
--apiserver-advertise-address=192.168.56.53 \
--node-name=master
ログインユーザにkubectlコマンドの利用を許可する。
これがない場合、root権限をもつユーザしかkubectlコマンドを実行できない。
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubeletは正常に起動、PodはcorednsがPendig状態のため、NodeがNotReadyとなる。
Podネットワークアドオンをインストールするとcorednsが正常になり、Node、Podともに正常起動する。
kubeletの状態確認(kubeadm init後)
vagrant@master:~$ systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor preset: enabled)
Drop-In: /usr/lib/systemd/system/kubelet.service.d
└─10-kubeadm.conf
Active: active (running) since Sun 2025-06-08 15:25:25 JST; 2min 17s ago
Docs: https://kubernetes.io/docs/
Main PID: 6215 (kubelet)
Tasks: 11 (limit: 2223)
Memory: 33.3M
CPU: 10.324s
CGroup: /system.slice/kubelet.service
└─6215 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/l>
6月 08 15:26:25 master kubelet[6215]: I0608 15:26:25.617572 6215 kuberuntime_gc.go:361] "Error getting ContainerStatus for containerID" containerID="a4c>
6月 08 15:26:25 master kubelet[6215]: E0608 15:26:25.619963 6215 log.go:32] "ContainerStatus from runtime service failed" err="rpc error: code = NotFoun>
6月 08 15:26:25 master kubelet[6215]: I0608 15:26:25.620050 6215 kuberuntime_gc.go:361] "Error getting ContainerStatus for containerID" containerID="c46>
6月 08 15:26:25 master kubelet[6215]: E0608 15:26:25.623226 6215 log.go:32] "ContainerStatus from runtime service failed" err="rpc error: code = NotFoun>
6月 08 15:26:25 master kubelet[6215]: I0608 15:26:25.623537 6215 kuberuntime_gc.go:361] "Error getting ContainerStatus for containerID" containerID="00b>
6月 08 15:27:25 master kubelet[6215]: E0608 15:27:25.598317 6215 kubelet_node_status.go:460] "Node not becoming ready in time after startup"
ノード、Podの状態確認(kubeadm init後)
### PodのcorednsがPending状態なのが原因で、ノードがNotReadyで正常起動していない。
vagrant@master:~$ kubectl get node -owide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
master NotReady control-plane 7m36s v1.33.1 192.168.56.53 <none> Ubuntu 22.04.5 LTS 5.15.0-133-generic containerd://2.1.1
vagrant@master:~$
vagrant@master:~$ kubectl get pod -A -owide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-system coredns-674b8bbfcf-c8jv4 0/1 Pending 0 7m35s <none> <none> <none> <none>
kube-system coredns-674b8bbfcf-f2s7x 0/1 Pending 0 7m35s <none> <none> <none> <none>
kube-system etcd-master 1/1 Running 0 7m41s 192.168.56.53 master <none> <none>
kube-system kube-apiserver-master 1/1 Running 0 7m39s 192.168.56.53 master <none> <none>
kube-system kube-controller-manager-master 1/1 Running 1 7m39s 192.168.56.53 master <none> <none>
kube-system kube-proxy-67ndr 1/1 Running 0 7m35s 192.168.56.53 master <none> <none>
kube-system kube-scheduler-master 1/1 Running 1 7m40s 192.168.56.53 master <none> <none>
Podネットワークアドオンのインストール
Masterノードで実施
ノード間で通信を可能にするため、Podネットワークアドオンをインストールする。CNI Pluginのインストール
と書いているサイトもあるが、containerdのインストールで実施したCNI Plugin
と混乱しないようPodネットワークアドオン
の名称にする。
PodネットワークアドオンインストールするまでCluster DNS(CoreDNS)は起動しない。
Calicoのインストールページから資材をダウンロードしてインストール。
Calicoのインストール
curl -OL https://raw.githubusercontent.com/projectcalico/calico/v3.30.0/manifests/operator-crds.yaml
kubectl create -f operator-crds.yaml
curl -OL https://raw.githubusercontent.com/projectcalico/calico/v3.30.0/manifests/tigera-operator.yaml
kubectl create -f tigera-operator.yaml
Customリソースのダウンロード、修正
CalicoのPodネットワークのデフォルト設定が192.168.0.0/16
だがこれはVirtualBoxのHostOnlyネットワークのデフォルト192.168.56.0/24
と重なるため、CalicoのPodネットワークを172.16.0.0/16
に修正した上でCustomリソースを適用する。
Customリソースのダウンロード
Customリソースの修正
$ vi custom-resources.yaml
### custom-resources.yamlファイル
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
name: default
spec:
# Configures Calico networking.
calicoNetwork:
ipPools:
- name: default-ipv4-ippool
blockSize: 26
#### このアドレスは使用しない
# cidr: 192.168.0.0/16
### 新たにPodネットワークを割り当てる
cidr: 172.16.0.0/16
encapsulation: VXLANCrossSubnet
natOutgoing: Enabled
nodeSelector: all()
修正したCustomリソースを適用
kubectl apply
でも良さそうだが、本家に従ってcreate
kubectl create -f custom-resources.yaml
全てのPodが正常に起動するまで少し時間がかかる。
Podの起動確認
vagrant@master:~/calico$ kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
calico-apiserver calico-apiserver-559c99b8d5-cqnc2 1/1 Running 0 3m28s
calico-apiserver calico-apiserver-559c99b8d5-gw72x 1/1 Running 0 3m28s
calico-system calico-kube-controllers-5d5749f6d8-zcsl4 1/1 Running 0 3m24s
calico-system calico-node-kbnnl 1/1 Running 0 3m24s
calico-system calico-typha-7bdf95c55d-vm56k 1/1 Running 0 3m25s
calico-system csi-node-driver-w9744 2/2 Running 0 3m24s
calico-system goldmane-7b5b4cd5d9-4kmgp 1/1 Running 0 3m25s
calico-system whisker-f7894bbc-jc7xh 2/2 Running 0 2m37s
kube-system coredns-674b8bbfcf-bxz5m 1/1 Running 0 150m
kube-system coredns-674b8bbfcf-dvnpj 1/1 Running 0 150m
kube-system etcd-master 1/1 Running 1 150m
kube-system kube-apiserver-master 1/1 Running 1 150m
kube-system kube-controller-manager-master 1/1 Running 1 150m
kube-system kube-proxy-l8gmk 1/1 Running 0 150m
kube-system kube-scheduler-master 1/1 Running 1 150m
tigera-operator tigera-operator-844669ff44-9q55l 1/1 Running 0 7m50s
Calico管理用のコマンドラインツールcalicoctl
の導入手順もこの後書かれているがスキップ、そのうちやる。
kubernetesクラスタへ参加(Workerノードがクラスタに参加)
Workerノードで実施
MasterノードでPodネットワークアドオンのインストールまで終わっていて、Masterノードのkubernetesクラスタが正常に起動している必要がある。
Workerノードがkubernetesクラスタに参加するにはTokenが必要となる。
kubernetesクラスタ参加用のToken
Workerノードをkubernetesクラスタに参加させる場合、Masterノードが発行するTokenを取得する必要がある。Token情報は以下で取得可能。
- クラスタ初期化のときのログからToken情報取得
- コマンドラインからTokenを再発行
クラスタ初期化のときのログからToken情報取得
クラスタ初期化した際、ログ出力内容にToken情報も出力されている。
....(色々なログ出力された後、最後に出力)
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.56.53:6443 --token xxxxxx.yyyyyyyyyyy \
--discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxx
コマンドラインからTokenを再発行
クラスタ初期化時のログがない場合、コマンドラインでToken情報の出力ができる。
Masterノードで実施する必要がある。
# Tokenの再発行(Masterノードで実施)
```shell
sudo kubeadm token create --print-join-command
出力結果
kubeadm join 192.168.56.53:6443 --token xxxxxx --discovery-token-ca-cert-hash sha256:xxxx
ちなみにTokenの有効期限は24時間、有効期限は以下で確認可能。
kubeadm token list
kubernetesクラスタに参加する
Masterノードに対しkubernetesクラスタへの参加を依頼する。成功するとkubernetesクラスタにWorkerノードが追加される。--apiserver-advertise-address
と--node-name
についてはMasterノードの初期化のところに記載。
sudo kubeadm join 192.168.56.53:6443 --token xxxxxxx \
--discovery-token-ca-cert-hash sha256:xxxxxxx \
--apiserver-advertise-address=192.168.56.54 \
--node-name=worker01
--node-name
としてhostnameと異なるworker01
を指定するとWarningがでるもののkubectl get node
ではnode nameとしてworker01
が表示される。
kubernetesクラスタの状態確認(Masterノードで実施)
vagrant@master:~$ kube get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
master Ready control-plane 3h10m v1.33.1 192.168.56.53 <none> Ubuntu 22.04.5 LTS 5.15.0-133-generic containerd://2.1.1
worker01 Ready <none> 11m v1.33.1 192.168.56.54 <none> Ubuntu 22.04.5 LTS 5.15.0-133-generic containerd://2.1.1
ここまでくれば、あとはIPアドレス変えたWorkerノードを作成してkubernetesクラスタに追加していけば良い。
おわりに
kubernetesのマニュアル、初見でも理解できるようなわかりやすい記述だったら、こんな覚書不要だった・・・
構築失敗したときのリセット手順も整理したい。