kubeadm での Kubernetes クラスタ構築

はじめに

今回使用した環境は以下の通り。

  • OS: Linux(Ubuntu22.04 LTS)
    • 2 vCPU
    • 2GB Memory
    • 60GB Disk
  • Kubernetes: v1.31.3
  • Containerd: v1.7.24
  • CNI: Calico v3.25.0

事前準備

kubeadm コマンドをインストールする前の事前準備を行う。実施する内容については、下記に記載されている。

kubeadmのインストール
このページではkubeadmコマンドをインストールする方法を示します。このインストール処理実行後にkubeadmを使用してクラスターを作成する方法については、kubeadmを使用したシングルマスタークラスターの作成を参照してください。 始める前に 次のいずれかが動作しているマシンが必要です Ubuntu 16.04+ Debian 9+ CentOS 7 Red Hat Enterprise Linux (RHEL) 7 Fedora 25+ HypriotOS v1.0.1+ Container Linux (tested with 1800.6.0) 1台あたり2GB以上のメモリ(2GBの場合、アプリ用のスペースはほとんどありません) 2コア以上のCPU クラスター内のすべてのマシン間で通信可能なネットワーク(パブリックネットワークでもプライベートネットワークでも構いません) ユニークなhostname、MACアドレス、とproduct_uuidが各ノードに必要です。詳細はここを参照してください。 マシン内の特定のポートが開いていること。詳細はここを参照してください。 Swapがオフであること。kubeletが正常に動作するためにはswapは必ずオフでなければなりません。 MACアドレスとproduct_uuidが全てのノードでユニークであることの検証 ネットワークインターフェースのMACアドレスはip linkもしくはifconfig -aコマンドで取得できます。 product_uuidはsudo cat /sys/class/dmi/id/product_uuidコマンドで確認できます。 ハードウェアデバイスではユニークなアドレスが割り当てられる可能性が非常に高いですが、VMでは同じになることがあります。Kubernetesはこれらの値を使用して、クラスター内のノードを一意に識別します。これらの値が各ノードに固有ではない場合、インストール処理が失敗することもあります。 ネットワークアダプタの確認 複数のネットワークアダプターがあり、Kubernetesコンポーネントにデフォルトで到達できない場合、IPルートを追加して、Kubernetesクラスターのアドレスが適切なアダプターを経由するように設定することをお勧めします。 必須ポートの確認 Kubernetesのコンポーネントが互いに通信するためには、これらの必要なポートが開いている必要があります。 netcatなどのツールを使用することで、下記のようにポートが開いているかどうかを確認することが可能です。 nc 127.0.0.1 6443 -v 使用するPodネットワークプラグインによっては、特定のポートを開く必要がある場合もあります。 これらは各Podネットワークプラグインによって異なるため、どのようなポートが必要かについては、プラグインのドキュメントを参照してください。 ランタイムのインストール Podのコンテナを実行するために、Kubernetesはコンテナランタイムを使用します。 Linuxノード その他のOS デフォルトでは、Kubernetesは選択されたコンテナランタイムと通信するためにContainer Runtime Interface (CRI)を使用します。 ランタイムを指定しない場合、kubeadmはよく知られたUnixドメインソケットのリストをスキャンすることで、インストールされたコンテナランタイムの検出を試みます。 次の表がコンテナランタイムと関連するソケットのパスリストです。 コンテナランタイムとソケットパス ランタイム Unixドメインソケットのパス Docker /var/run/docker.sock containerd /run/containerd/containerd.sock CRI-O /var/run/crio/crio.sock Dockerとcontainerdの両方が同時に検出された場合、Dockerが優先されます。Docker 18.
kubeadmのインストール favicon kubernetes.io
kubeadmのインストール

マシンに SSH して移行の手順を行った。

swap の無効化

$ swapoff -a

マシンを再起動した際も永続化されるように /etc/fstab を編集する。

/etc/fstab
# コメントアウトしておく
# /swap.img   none   swap   sw   0    0

コンテナランタイムのインストール

Kubernetes の稼働にはコンテナランタイムが必要となる。

コンテナランタイム
備考: dockershimは1.24のリリースをもってKubernetesプロジェクトから削除されました。詳しくは、dockershimの削除に関するFAQをご覧ください。 クラスター内の各ノードがPodを実行できるようにするため、コンテナランタイムをインストールする必要があります。 このページでは、ノードをセットアップするための概要と関連する作業について説明します。 Kubernetes 1.31においては、Container Runtime Interface (CRI)に準拠したランタイムを使用する必要があります。 詳しくはサポートするCRIのバージョンをご覧ください。 このページではいくつかの一般的なコンテナランタイムをKubernetesで使用する方法の概要を説明します。 containerd CRI-O Docker Engine Mirantis Container Runtime 備考:v1.24以前のKubernetesリリースでは、 dockershim という名前のコンポーネントを使用したDocker Engineとの直接の統合が含まれていました。 この特別な直接統合は、もはやKubernetesの一部ではありません(この削除はv1.20リリースの一部として発表されています)。 dockershimの廃止がどのような影響を与えるかについては、dockershim削除の影響範囲を確認する をご覧ください。 dockershimからの移行について知りたい場合、dockershimからの移行を参照してください。 v1.31以外のバージョンのKubernetesを実行している場合、そのバージョンのドキュメントを確認してください。 インストールと設定の必須要件 以下の手順では、全コンテナランタイムに共通の設定をLinux上のKubernetesノードに適用します。 特定の設定が不要であることが分かっている場合、手順をスキップして頂いて構いません。 詳細については、Network Plugin Requirementsまたは、特定のコンテナランタイムのドキュメントを参照してください。 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 以下のコマンドを実行してbr_netfilterとoverlayモジュールが読み込まれていることを確認してください。
コンテナランタイム favicon kubernetes.io
コンテナランタイム

Docker, Containerd, CRI-O などがあるが、今回は Kubernetes でデファクトスタンダードとなっている Containerd を採用した。

IPv4フォワーディングを有効化し、iptablesからブリッジされたトラフィックを見えるようにする

まずは、こちらを参考に作業を行う。

コンテナランタイム
備考: dockershimは1.24のリリースをもってKubernetesプロジェクトから削除されました。詳しくは、dockershimの削除に関するFAQをご覧ください。 クラスター内の各ノードがPodを実行できるようにするため、コンテナランタイムをインストールする必要があります。 このページでは、ノードをセットアップするための概要と関連する作業について説明します。 Kubernetes 1.31においては、Container Runtime Interface (CRI)に準拠したランタイムを使用する必要があります。 詳しくはサポートするCRIのバージョンをご覧ください。 このページではいくつかの一般的なコンテナランタイムをKubernetesで使用する方法の概要を説明します。 containerd CRI-O Docker Engine Mirantis Container Runtime 備考:v1.24以前のKubernetesリリースでは、 dockershim という名前のコンポーネントを使用したDocker Engineとの直接の統合が含まれていました。 この特別な直接統合は、もはやKubernetesの一部ではありません(この削除はv1.20リリースの一部として発表されています)。 dockershimの廃止がどのような影響を与えるかについては、dockershim削除の影響範囲を確認する をご覧ください。 dockershimからの移行について知りたい場合、dockershimからの移行を参照してください。 v1.31以外のバージョンのKubernetesを実行している場合、そのバージョンのドキュメントを確認してください。 インストールと設定の必須要件 以下の手順では、全コンテナランタイムに共通の設定をLinux上のKubernetesノードに適用します。 特定の設定が不要であることが分かっている場合、手順をスキップして頂いて構いません。 詳細については、Network Plugin Requirementsまたは、特定のコンテナランタイムのドキュメントを参照してください。 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 以下のコマンドを実行してbr_netfilterとoverlayモジュールが読み込まれていることを確認してください。
コンテナランタイム favicon kubernetes.io
コンテナランタイム
# Linux カーネルの IPv4 フォワーディング機能を有効にする
$ cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
EOF

$ sudo modprobe overlay
$ sudo modprobe br_netfilter

# Kubernetes クラスタ内で iptables を使用してトラフィックを制御するように設定
$ 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

これらの作業は Kubernetes クラスタでコンテナや Pod 間のネットワーク通信を正しく処理し、トラフィックが意図した通りにルーティングされるようにするために必要となる。

Containerd のインストール

下記の公式ドキュメントに従って進めた。インストールしたバージョンは記事執筆時の最新バージョンを使っているので、適宜利用したいバージョンに置き換えてもらえればと思う。

containerd/docs/getting-started.md at main · containerd/containerd
An open and reliable container runtime. Contribute to containerd/containerd development by creating an account on GitHub.
containerd/docs/getting-started.md at main · containerd/containerd favicon github.com
containerd/docs/getting-started.md at main · containerd/containerd
# ダウンロードしたいバージョンを指定する: containerd-<VERSION>-<OS>-<ARCH>.tar.gz
$ wget https://github.com/containerd/containerd/releases/download/v1.7.24/containerd-1.7.24-linux-amd64.tar.gz
$ tar Cxzvf /usr/local/ containerd-1.7.24-linux-amd64.tar.gz

# systemd 経由で containerd を起動する場合は、下記もダウンロード
$ wget https://raw.githubusercontent.com/containerd/containerd/main/containerd.service
$ mkdir -p /usr/local/lib/systemd/system
$ mv containerd.service /usr/local/lib/systemd/system/

# systemd へ変更を反映させる
$ systemctl daemon-reload
$ systemctl enable --now containerd
Created symlink /etc/systemd/system/multi-user.target.wants/containerd.service → /usr/local/lib/systemd/system/containerd.service.

runc のインストール

引き続き containerd のドキュメントを参照して、低レベルのコンテナランタイムとして runc を導入した。

$ wget https://github.com/opencontainers/runc/releases/download/v1.2.2/runc.amd64

$ install -m 755 runc.amd64 /usr/local/sbin/runc

CNI プラグインのインストール

$ wget https://github.com/containernetworking/plugins/releases/download/v1.6.0/cni-plugins-linux-amd64-v1.6.0.tgz
$ mkdir -p /opt/cni/bin
$ tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.6.0.tgz

systemd cgroup ドライバーを構成

下記を参照すると runc が systemd cgroup ドライバーを使うようにするには設定をする必要がある。

コンテナランタイム
備考: dockershimは1.24のリリースをもってKubernetesプロジェクトから削除されました。詳しくは、dockershimの削除に関するFAQをご覧ください。 クラスター内の各ノードがPodを実行できるようにするため、コンテナランタイムをインストールする必要があります。 このページでは、ノードをセットアップするための概要と関連する作業について説明します。 Kubernetes 1.31においては、Container Runtime Interface (CRI)に準拠したランタイムを使用する必要があります。 詳しくはサポートするCRIのバージョンをご覧ください。 このページではいくつかの一般的なコンテナランタイムをKubernetesで使用する方法の概要を説明します。 containerd CRI-O Docker Engine Mirantis Container Runtime 備考:v1.24以前のKubernetesリリースでは、 dockershim という名前のコンポーネントを使用したDocker Engineとの直接の統合が含まれていました。 この特別な直接統合は、もはやKubernetesの一部ではありません(この削除はv1.20リリースの一部として発表されています)。 dockershimの廃止がどのような影響を与えるかについては、dockershim削除の影響範囲を確認する をご覧ください。 dockershimからの移行について知りたい場合、dockershimからの移行を参照してください。 v1.31以外のバージョンのKubernetesを実行している場合、そのバージョンのドキュメントを確認してください。 インストールと設定の必須要件 以下の手順では、全コンテナランタイムに共通の設定をLinux上のKubernetesノードに適用します。 特定の設定が不要であることが分かっている場合、手順をスキップして頂いて構いません。 詳細については、Network Plugin Requirementsまたは、特定のコンテナランタイムのドキュメントを参照してください。 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 以下のコマンドを実行してbr_netfilterとoverlayモジュールが読み込まれていることを確認してください。
コンテナランタイム favicon kubernetes.io
コンテナランタイム
$ mkdir -p /etc/containerd
$ containerd config default > /etc/containerd/config.toml

# SystemdCgroup を false -> true に置換
$ sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

なぜこの作業が必要かというと Kubernetes(kubelet)側では、systemd をデフォルトで cgroup ドライバーとして期待している一方で、コンテナランタイム(Containerd)側では、cgroupfs が使用されるためドライバーを一致させるために念の為設定しておく。

Kubernetesでcontainerdを使用するには、CRIサポートを有効にする必要があります。 /etc/containerd/config.toml内のdisabled_pluginsリストにcriが含まれていないことを確認してください。

また、上記の通り公式ドキュメントに記載があるので、config.toml を編集するついでに上記も確認しておくと良い。

kubelet, kubeadm, kubectl のインストール

事前準備が長かったが、いよいよコアとなるパッケージをインストールしていく。 公式のドキュメントは下記を参照。

kubeadmのインストール
このページではkubeadmコマンドをインストールする方法を示します。このインストール処理実行後にkubeadmを使用してクラスターを作成する方法については、kubeadmを使用したシングルマスタークラスターの作成を参照してください。 始める前に 次のいずれかが動作しているマシンが必要です Ubuntu 16.04+ Debian 9+ CentOS 7 Red Hat Enterprise Linux (RHEL) 7 Fedora 25+ HypriotOS v1.0.1+ Container Linux (tested with 1800.6.0) 1台あたり2GB以上のメモリ(2GBの場合、アプリ用のスペースはほとんどありません) 2コア以上のCPU クラスター内のすべてのマシン間で通信可能なネットワーク(パブリックネットワークでもプライベートネットワークでも構いません) ユニークなhostname、MACアドレス、とproduct_uuidが各ノードに必要です。詳細はここを参照してください。 マシン内の特定のポートが開いていること。詳細はここを参照してください。 Swapがオフであること。kubeletが正常に動作するためにはswapは必ずオフでなければなりません。 MACアドレスとproduct_uuidが全てのノードでユニークであることの検証 ネットワークインターフェースのMACアドレスはip linkもしくはifconfig -aコマンドで取得できます。 product_uuidはsudo cat /sys/class/dmi/id/product_uuidコマンドで確認できます。 ハードウェアデバイスではユニークなアドレスが割り当てられる可能性が非常に高いですが、VMでは同じになることがあります。Kubernetesはこれらの値を使用して、クラスター内のノードを一意に識別します。これらの値が各ノードに固有ではない場合、インストール処理が失敗することもあります。 ネットワークアダプタの確認 複数のネットワークアダプターがあり、Kubernetesコンポーネントにデフォルトで到達できない場合、IPルートを追加して、Kubernetesクラスターのアドレスが適切なアダプターを経由するように設定することをお勧めします。 必須ポートの確認 Kubernetesのコンポーネントが互いに通信するためには、これらの必要なポートが開いている必要があります。 netcatなどのツールを使用することで、下記のようにポートが開いているかどうかを確認することが可能です。 nc 127.0.0.1 6443 -v 使用するPodネットワークプラグインによっては、特定のポートを開く必要がある場合もあります。 これらは各Podネットワークプラグインによって異なるため、どのようなポートが必要かについては、プラグインのドキュメントを参照してください。 ランタイムのインストール Podのコンテナを実行するために、Kubernetesはコンテナランタイムを使用します。 Linuxノード その他のOS デフォルトでは、Kubernetesは選択されたコンテナランタイムと通信するためにContainer Runtime Interface (CRI)を使用します。 ランタイムを指定しない場合、kubeadmはよく知られたUnixドメインソケットのリストをスキャンすることで、インストールされたコンテナランタイムの検出を試みます。 次の表がコンテナランタイムと関連するソケットのパスリストです。 コンテナランタイムとソケットパス ランタイム Unixドメインソケットのパス Docker /var/run/docker.sock containerd /run/containerd/containerd.sock CRI-O /var/run/crio/crio.sock Dockerとcontainerdの両方が同時に検出された場合、Dockerが優先されます。Docker 18.
kubeadmのインストール favicon kubernetes.io
kubeadmのインストール
$ sudo apt-get update
$ sudo apt-get install -y apt-transport-https ca-certificates curl gpg

$ 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

コントロールプレーンノードの初期化

これ以降は Calico の公式ドキュメントに従って進めた。

Quickstart for Calico on Kubernetes | Calico Documentation
Install Calico on a single-host Kubernetes cluster for testing or development in under 15 minutes.
Quickstart for Calico on Kubernetes | Calico Documentation favicon docs.tigera.io

クラスタの作成

ここでは、使用する Calico の推奨 CIDR を指定した上で kubeadm init を実行する。

$ sudo kubeadm init --pod-network-cidr=192.168.0.0/16
# ここで `kubeadm join` コマンドが表示されていると思うので控えておく

# 上記コマンドが成功すると、出力されるので以下を実行
$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

# ノードを確認
$ kubectl get nodes
NAME        STATUS     ROLES           AGE   VERSION
k8s-study   NotReady   control-plane   54s   v1.31.3

Calico をインストール

$ kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.29.1/manifests/tigera-operator.yaml
$ kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.29.1/manifests/custom-resources.yaml

# 全ての Pod が正常に動作しているか確認する
$ kubectl get po -A

ワーカーノードの追加

ノードとして追加したいマシンに別途 SSH して入り、下記のコマンドを実行する。

$ kubeadm join <CONTROL_PLANE_IP>:6443 --token <TOKEN> \
        --discovery-token-ca-cert-hash sha256:<DISCOVERY_TOKEN_CA_CERT_HASH>
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[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 1.002026242s
[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.

上記のようなログが確認できると、コントロールプレーン側で kubectl を叩くとノードの状態を確認できる。

$ kubectl get nodes
NAME         STATUS   ROLES           AGE   VERSION
k8s-study    Ready    control-plane   21h   v1.31.3
k8s-study2   Ready    <none>          27m   v1.31.3

上記の kubectl join コマンドを忘れたり、トークンの有効期限が失効してしまった場合は、下記のコマンドで再度作成できる。

$ kubeadm token create --print-join-command

まとめ

この記事では、kubeadm を用いた Kubernetes クラスタ構築の手順を紹介した。

最終的には、コントロールプレーンノード x 1、ワーカーノード x 2 の構成になった。

$ kubectl get nodes
NAME         STATUS   ROLES           AGE    VERSION
k8s-study    Ready    control-plane   21h    v1.31.3
k8s-study2   Ready    worker          36m    v1.31.3
k8s-study3   Ready    worker          6m1s   v1.31.3

今回実施した手順を通じて、Kubernetes クラスタの構築を体系的に理解し、Calico を活用したネットワーク設定を行うことができた。 公式ドキュメントに比較的手順がまとまっていたので、リンク先を参照しながら進めればあまり苦戦することなく完了できた。

参考

kubeadmを使ってクラスターを構築する
プロダクショングレードのコンテナ管理基盤
kubeadmを使ってクラスターを構築する favicon kubernetes.io
kubeadmを使ってクラスターを構築する
containerdとkubeadmを使用してkubernetesを構築① - Qiita
環境ubuntu:22.0.4.1 LTSkubernetes:1.28.2要件確認公式ドキュメントより確認https://kubernetes.io/ja/docs/setup/prod…
containerdとkubeadmを使用してkubernetesを構築① - Qiita favicon qiita.com
containerdとkubeadmを使用してkubernetesを構築① - Qiita
[kubernetes] kubeadmで構築した後トークン失効後にworkerノードを追加する - zaki work log
kubeadmでクラスタ構築するとworkerノード追加のためのコマンドが表示されて簡単にノード追加できるが、使用するtokenの期限が(デフォルトでは24時間で)切れてしまうため、同じ引数でkubeadm joinしてもこの通り、失敗する。 [zaki@k8s-worker03 ~]$ sudo kubeadm join 192.168.0.121:6443 --token 0di1qt.ju1y8vkmfhpxmytl \ --discovery-token-ca-cert-hash sha256:73df38227ad8bd6332ae0abf6d20ccdaa1c0cf8b254e24…
 [kubernetes] kubeadmで構築した後トークン失効後にworkerノードを追加する - zaki work log favicon zaki-hmkc.hatenablog.com
 [kubernetes] kubeadmで構築した後トークン失効後にworkerノードを追加する - zaki work log