kubeadm + containerdによるkubernetesクラスタ構築

概要

 Canned-Catfood Gamingを提供しているインフラ基盤は基本的にはコンテナベースで稼働しています。以前にも当ブログの記事にてクラスタの構築方法として、kubeadm + containerdによる環境構築方法を紹介していました。
検証用Kubernetesクラスタの作成 – Canned Catfood Gaming
 今回開発環境を再構築するにあたり、前回構築時の2022年よりインストール方法に変更点が複数ありましたので改めて記事にしました。

 kubeadmおよびcontainerdによるインストール方法については kubeadmを使ってクラスターを構築する | Kubernetes の公式のドキュメントでも記載されています。しかしながら、containerd周りのアップデートなどの影響により dockershimとして利用する際のパラメータ等に細かな修正点があるため、ドキュメントの記載内容を実行するのみでは正常にkube-api, etcd等のコンテナが起動しない状態となっていました。

注意点

  • デプロイツールには kubeadm を使用しています。
  • CRIにはcontainerdを使用しています。
  • サービスメッシュのネットワークアドオンにはciliumを使用しています。
  • 使用したkubernetesバージョンはv1.30.3です。

環境情報

各コンポーネントのバージョンは以下の通りです。

ソフトウェアバージョン
Ubuntu (server)24.04.3 LTS(Noble Numbat)
Kubernetes (kubeadm, kubelet, kubectl)v1.30.3
Containerdversion 1.7.19
HelmVersion: “v3.15.3”
Ciliumversion 1.16.0
$ cat /etc/os-release
PRETTY_NAME="Ubuntu 24.04 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo
$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"30", GitVersion:"v1.30.3", GitCommit:"6fc0a69044f1ac4c13841ec4391224a2df241460", GitTreeState:"clean", BuildDate:"2024-07-16T23:53:15Z", GoVersion:"go1.22.5", Compiler:"gc", Platform:"linux/amd64"}
$ helm version
version.BuildInfo{Version:"v3.15.3", GitCommit:"3bb50bbbdd9c946ba9989fbe4fb4104766302a64", GitTreeState:"clean", GoVersion:"go1.22.5"}
$ containerd --version
containerd containerd.io 1.7.19 2bf793ef6dc9a18e00cb12efb64355c2c9d5eb41

 またサーバには有効なネットワークアダプタが2つ、うち一つにIPv4アドレスが付与されてインターネットに対して疎通可能な状態、かつデフォルトルートが設定されています。(kubeadmでは特に指定しない場合、デフォルトルートが設定されているインターフェースが使用されます。)

$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:15:5d:e6:e9:00 brd ff:ff:ff:ff:ff:ff
    inet 192.168.150.1/24 brd 192.168.150.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::215:5dff:fee6:e900/64 scope link
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 00:15:5d:e6:e9:01 brd ff:ff:ff:ff:ff:ff
$ ip route
default via 192.168.150.254 dev eth0 proto static

構築手順

nftablesからレガシーiptablesへのモード切替え

 前回から引き続いて、Linuxカーネルのiptablesサブシステムは、最近のバージョンではnftablesへの置換えが進んでいます。Ubuntuでも他のディストリと同様に、19.04以降ではnftablesが使用されています。nftablesはkubeadmと現状で非互換であるため、正常に動作させる為にレガシーなiptablesへ切り換えておきます。

 切り替えに必要なパッケージを下記でインストールします。

$ sudo apt install -y iptables arptables ebtables

 update-alternativesを利用して、各機能をlegacy iptablesに実際に切り替えていきます。

$ sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
update-alternatives: using /usr/sbin/iptables-legacy to provide /usr/sbin/iptables (iptables) in manual mode
$ sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
update-alternatives: using /usr/sbin/ip6tables-legacy to provide /usr/sbin/ip6tables (ip6tables) in manual mode
$ sudo update-alternatives --set arptables /usr/sbin/arptables-legacy
update-alternatives: using /usr/sbin/arptables-legacy to provide /usr/sbin/arptables (arptables) in manual mode
$ sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy
update-alternatives: using /usr/sbin/ebtables-legacy to provide /usr/sbin/ebtables (ebtables) in manual mode

kernelパラメータの調整

 使用するkernelモジュールのうち、一部(overlayおよびbr_netfilter)デフォルトで読み込まれていないものがあるので事前に有効化しておきます。

$ echo -e "overlay\nbr_netfilter" | sudo tee /etc/modules-load.d/containerd.conf
$ sudo modprobe overlay
$ sudo modprobe br_netfilter

 iptablesがブリッジインターフェースを通過するパケットを、正常にフォワーディングできるようにkernelパラメータを調整します。

$ echo -e "net.bridge.bridge-nf-call-ip6tables = 1\nnet.bridge.bridge-nf-call-iptables = 1\nnet.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/kubernetes.conf
$ sudo sysctl --system

swapの無効化(有効化されている場合のみ)

 kubeletの実行時にswapが有効化されていると、メモリ使用が逼迫した場合にMemory Evictationを招くため、無効化することが推奨されています。(kubernetes 1.22以降ではWorker nodeでのswap利用がサポートされています。)

 既にswapが有効化/利用されている環境では、swapoffを実行してswap利用を止めます。fstabに起動時のswapのマウントについて設定が記述されているので削除(またはコメントアウト)しておきましょう。

$ sudo swapoff -a

/etc/fstab の記載内容(swap.imgファイルを再起動後にマウントしないため、コメントアウトにて対応します。)

# <file system> <mount point>   <type>  <options>       <dump>  <pass>
# / was on /dev/sda2 during curtin installation
/dev/disk/by-uuid/58dc637c-9e1f-45e1-9543-d1dd29c96122 / ext4 defaults 0 1
# /boot/efi was on /dev/sda1 during curtin installation
/dev/disk/by-uuid/D128-E041 /boot/efi vfat defaults 0 1
# /swap.img     none    swap    sw      0       0

CRI(container runtime interface)の導入

 前回導入次点と同様に、Containerd をCRIとして導入していきます。containerdのパッケージはUbuntuレポジトリでも提供されていますが、新しいパッケージについてはdockerのレポジトリより利用可能です。

 まずはDockerのubuntuレポジトリを追加するために、必要なパッケージを導入していきます。(Ubuntuをminimalでセットアップしていない限り、下記パッケージは既に導入されています。)

$ sudo apt-get update
$ sudo apt-get install ca-certificates curl

Dockerレポジトリ追加の為に公式のGPGキーを追加します。

$ sudo install -m 0755 -d /etc/apt/keyrings
$ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
$ sudo chmod a+r /etc/apt/keyrings/docker.asc

aptレポジトリを追加、セットアップします。Docker Engineのインストール手順として、公式のドキュメントにて提示されている手順通りに実行します。

$ echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt-get update

apt パッケージリストを更新して、パッケージをインストールします。

$ sudo apt update && sudo apt install -y containerd.io

 containerdデフォルトの設定では、criプラグインが無効化されているので予め有効化しておきます。また末尾にruncのoptionとして、systemdのcgroupドライバを使用するように明示的に指定します。
(参考)containerd/docs/cri/config.md at main · containerd/containerd · GitHub

 更に一部の kubeletによって起動されるコンテナ(kube-api, etcd)がCrashLoopBackoff にて再起動を繰り返すためcontainerdで使用する sandbox containerを明示的に指定します。
(参考)Container Runtimes | Kubernetes

/etc/containerd/config.toml

# disabled_plugins = ["cri"]
version = 2
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.k8s.io/pause:3.9"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true

config.toml上の該当行をコメントアウト後、および必要な記述を追記後にcontainerdを再起動します。

$ sudo systemctl restart containerd

(補遺)nerdctlの導入

 今回のセットアップではDocker Engine自体を導入していないため、各コンテナをデバッグするにはインターフェースを追加で導入する必要があります。containerdにはcliとしてnerdctlが提供されているので、今回はこちらを利用していきましょう。

 パッケージとして提供されてはいないので、GitHubからバイナリをダウンロードして適切なディレクトリに配置していきます。提供されている最新のバージョンについては Releases · containerd/nerdctl より確認できます。

$ curl -LO https://github.com/containerd/nerdctl/releases/download/v1.7.6/nerdctl-1.7.6-linux-amd64.tar.gz
$ tar zxvf nerdctl-1.7.6-linux-amd64.tar.gz
$ sudo cp nerdctl /usr/local/bin/

 k8sでデプロイされた各コンテナを参照する際には、–namespace=k8s.ioをオプション付与して利用します。

$ sudo nerdctl --namespace=k8s.io ps
[[CONTAINER ID    IMAGE
     COMMAND                   CREATED              STATUS    PORTS    NAMES
16dd5316a7b3    registry.k8s.io/coredns/coredns:v1.11.1
   "/coredns -conf /etc…"    About an hour ago    Up                 k8s://kube-system/coredns-7db6d8ff4d-qlfwb/coredns
1a90962165df    registry.k8s.io/coredns/coredns:v1.11.1
   "/coredns -conf /etc…"    About an hour ago    Up                 k8s://kube-system/coredns-7db6d8ff4d-dcz2x/coredns
1c2709d17a4d    registry.k8s.io/pause:3.9
   "/pause"                  13 hours ago         Up                 k8s://kube-system/kube-controller-manager-ubuntusrv-stg01

kubeadm, kubelet, kubectl の導入

 CRIが準備できたので、実際にkubernetesのレポジトリ追加、パッケージ導入を作業していきます。レポジトリ追加作業前に必要となるパッケージについて導入しておきましょう。

$ sudo apt-get update
$ sudo apt-get install -y apt-transport-https ca-certificates curl gpg

 先ほどDockerレポジトリを追加したのと同様の手順で、kubernetesのレポジトリを追加していきます。まずはGPGキーのインポートとレポジトリ情報をapt上へ登録します。Kubernetesのレポジトリ構造が変更されたため、major + minorバージョンで個別にキーファイルをインストール、レポジトリを追加する必要があります。

$ curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/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.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list

 パッケージリストを更新し、kubeadm, kubelet, kubectlの必要パッケージをインストールします。

$ sudo apt update && sudo apt install -y kubelet kubeadm kubectl

(補遺)各パッケージのhold

 kubernetes以外のパッケージを更新する際に、合わせてkubernetes関連のパッケージが更新されてしまうことを避けるため、kubelet, kubeadm, kubectlの各パッケージについてholdします。

$ sudo apt-mark hold kubelet kubeadm kubectl

kubernetes クラスタのセットアップ(kubeadm init)

 kubeadmを利用してクラスターをセットアップしていきます。今回はネットワークアドオンとしてciliumを使用するので、podのアドレス範囲が他のネットワークと衝突しないように引数を追加しています。

$ sudo kubeadm init --pod-network-cidr="10.244.0.0/16"

問題無くクラスターが作成されると、下記のメッセージが表示されます。

Your Kubernetes control-plane has initialized successfully!

 現在使用している一般ユーザでkubectl経由でクラスターを利用できるように、configを~/.kubeディレクトリに配置していきます。

$ 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ノードのstatusはNotReadyとなっています。

$ kubectl get nodes
NAME              STATUS      ROLES           AGE   VERSION
ubuntusrv-stg01   NotReady    control-plane   1h    v1.30.3

ネットワークアドオンのインストール

 今回は eBPFを利用しservice meshやVXLANの実装、また各パケットの動作についてトレース可能な可観測性を高めた cilium を利用します。cilium は cilium cliまたは kubernetes向けの構成管理ソフトウェア Helm からインストールすることができます。前者の手順は Cilium Quick Installation — Cilium 1.16.0 documentation から参照可能です。

 今回のインストールではインストール後の構成管理の側面や、設定投入(cilium-cliからのインストールの場合、設定変更に必要なリファレンスがすべて公開されていない)の側面から Helm よりインストールします。

Helmインストール手順

 ubuntuの場合、パッケージマネージャ snap より最新の Helm をインストールできます。下記コマンドを実行して最新のHelmを導入してください。

$ sudo snap install helm --classic

Ciliumインストール手順

 インストールしたHelmを利用して、Ciliumを導入します。ubuntu server 24.04.3 のsnap(2.63+24.04ubuntu0.1) より導入する場合は、–classic オプションを付与してインストールする必要があります。

 またインストール先のnamespaceを –namespaceオプションにて指定してインストールします。通常は kube-systemにリソースをデプロイしますが、個別にテナントを分離する必要がある場合は別namespaceに導入しましょう。

$ helm install cilium cilium/cilium --version 1.16.0 --namespace kube-system

 Helm経由でのciliumのデプロイ後、すべてのリソースが正常に展開されているかどうか確認します。kube-system namespaceに展開されているpodを一覧で参照します。一部、cilium-operator podがシングルノードクラスタの場合に1nodeに複数展開できないため、Pendingにて保留されます。

$ kubectl get pods -n kube-system
NAME                                      READY   STATUS    RESTARTS   AGE
cilium-envoy-rfp4q                        1/1     Running   0          151m
cilium-nntbn                              1/1     Running   0          151m
cilium-operator-7ddc48bb97-cqjs6          1/1     Running   0          151m
cilium-operator-7ddc48bb97-xt6hf          0/1     Pending   0          151m
coredns-7db6d8ff4d-dcz2x                  1/1     Running   0          14h
coredns-7db6d8ff4d-qlfwb                  1/1     Running   0          14h
etcd-ubuntusrv-stg01                      1/1     Running   101        14h
kube-apiserver-ubuntusrv-stg01            1/1     Running   96         14h
kube-controller-manager-ubuntusrv-stg01   1/1     Running   97         14h
kube-proxy-qjbt9                          1/1     Running   0          14h
kube-scheduler-ubuntusrv-stg01            1/1     Running   103        14h

 このままシングルノードクラスタにて使用する場合は、kube-system namespaceのcilium-operator deploymentを編集して、replicasを2から1へ変更します。

$ kubectl edit deployments.apps -n kube-system cilium-operator
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      io.cilium/app: operator
      name: cilium-operator

 レプリカ数が2から1に変更され、不要となったcilium operator podがスケジュールされなくなります。

$ kubectl get pods -n kube-system
NAME                                      READY   STATUS    RESTARTS   AGE
cilium-envoy-rfp4q                        1/1     Running   0          155m
cilium-nntbn                              1/1     Running   0          155m
cilium-operator-7ddc48bb97-cqjs6          1/1     Running   0          155m
coredns-7db6d8ff4d-dcz2x                  1/1     Running   0          14h
coredns-7db6d8ff4d-qlfwb                  1/1     Running   0          14h
etcd-ubuntusrv-stg01                      1/1     Running   101        14h
kube-apiserver-ubuntusrv-stg01            1/1     Running   96         14h
kube-controller-manager-ubuntusrv-stg01   1/1     Running   97         14h
kube-proxy-qjbt9                          1/1     Running   0          14h
kube-scheduler-ubuntusrv-stg01            1/1     Running   103        14h

 また、CNIプラグインがインストールされたことにより coredns podが正常にデプロイされ、nodeのSTATUSがReadyとなりリソースがデプロイ可能となります。

$ kubectl get nodes
NAME              STATUS   ROLES           AGE   VERSION
ubuntusrv-stg01   Ready    control-plane   14h   v1.30.3

シングルノードクラスターとして利用する準備

 以前のバージョンの kubernetesのcontrol-planeには原則としてpodがscheduleされないように設定されています。(control-planeに過剰にpodがscheduleされ、リソースが逼迫するのを防ぐ為にこのような措置が取られています。)podのscheule可否はtaintで制御されていますでの、設定されたtaintを変更しましょう。

$ kubectl taint node ubuntusrv-stg01 node-role.kubernetes.io/control-plane:NoSchedule-
node/k8s-develop1 untainted

(補遺)auto-completion 入力補完の設定

 kubectlの使用時に入力補完を利用可能にするためには、下記を実行してbash_completion.dに適切なファイルを配置します。

$ kubectl completion bash | sudo tee /etc/bash_completion.d/kubectl > /dev/null

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください