概要
これまで提供してきたゲームサーバーや周辺サービス等は、基本的にはKubernetesクラスタ上にデプロイして運用してきています。環境はProductionとして1クラスタのみを、namespaceでマルチテナント運用する体制を取っています。(クラスタ本体をメンテナンスするユーザが1人のみ、かつオペレータはRBACが適用されたCD環境+Gitレポジトリでしか触らない為、この運用で必要十分なんですよね)
しかしながらちょっと訳あって、検証環境としてKubernetesクラスタを新規構築したので、2022年11月時点での構築について備忘録としてメモしていきます。
注意点
- 今回はデプロイツールにkubeadmのみを使用しています。microk8sやk3s、kubesprayは使用していません。
- CRIにはcontainerdを使用しています。
- サービスメッシュのネットワークアドオンにはcalicoを使用しています。
- 使用したkubernetesバージョンは1.25.4です。
環境情報
$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.1 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.1 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
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=jammy
有効なネットワークアダプタが1つ、ipv4およびipv6アドレスが名前解決可能な状態で付与されています。
$ 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
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:13:0f:02 brd ff:ff:ff:ff:ff:ff
inet 192.168.***.***/24 metric 100 brd 192.168.101.255 scope global dynamic eth0
valid_lft 258832sec preferred_lft 258832sec
inet6 2405:6584:8540:****:****:****:****:****/64 scope global dynamic mngtmpaddr noprefixroute
valid_lft 2591666sec preferred_lft 604466sec
inet6 fe80::215:5dff:fe13:f02/64 scope link
valid_lft forever preferred_lft forever
作業手順
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モジュールのうち、一部デフォルトで読み込まれていないものがあるので事前に有効化しておきます。
$ 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を招くため、無効化することが推奨されています。(1.22以降ではWorker nodeでのswap利用がサポートされているようですが、今回はシングルノード構成なので関係ないですね。)
既にswapが有効化/利用されている環境では、swapoffを実行してswap利用を止めます。fstabに起動時のswapのマウントについて設定が記述されているので削除(またはコメントアウト)しておきましょう。
$ sudo swapoff -a
/etc/fstab
# <file system> <mount point> <type> <options> <dump> <pass>
# / was on /dev/sda2 during curtin installation
/dev/disk/by-uuid/98955977-1ef1-4e72-a374-d764ef75a953 / ext4 defaults 0 1
# /swap.img none swap sw 0 0
CRIの導入
今回はCRIとしてcontainerdを導入、利用していきます。productionではなくdevelop環境として利用する為に、最新のバージョンを導入していきましょう。
containerdの比較的新しいパッケージについては、dockerのレポジトリより利用可能です。まずはDockerのubuntuレポジトリを追加するために、必要なパッケージを導入していきます。(Ubuntuをminimalでセットアップしていない限り、下記パッケージは既に導入されています。)
$ sudo apt update
$ sudo apt install -y ca-certificates curl gnupg lsb-release
Dockerレポジトリ追加の為に公式のGPGキーを追加します。以前はapt-key add等で追加してたと思うんですが、推奨される方法が変わったみたいですね。
$ sudo mkdir -p /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
aptレポジトリを追加、セットアップします。公式に用意されたインストールドキュメントのワンライナーそのままですね。
$ echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
パッケージリストを更新して、パッケージをインストールします。
$ sudo apt update && sudo apt install -y containerd.io
containerdデフォルトの設定では、criプラグインが無効化されているので予め有効化しておきます。config.toml上の該当行をコメントアウト後、containerdを再起動しておきましょう。また末尾にruncのoptionとして、systemdのcgroupドライバを使用するように明示的に指定します。
/etc/containerd/config.toml
# disabled_plugins = ["cri"]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
$ sudo systemctl restart containerd
(補遺)nerdctlの導入
今回のセットアップではDocker Engine自体を導入していないため、各コンテナをデバッグするにはインターフェースを追加で導入する必要があります。containerdにはcliとしてnerdctlが提供されているので、今回はこちらを利用していきましょう。
パッケージとして提供されてはいないので、GitHubからバイナリをダウンロードして適切なディレクトリに配置していきます。
$ curl -LO https://github.com/containerd/nerdctl/releases/download/v1.0.0/nerdctl-1.0.0-linux-amd64.tar.gz
$ tar zxvf nerdctl-1.0.0-linux-amd64.tar.gz
$ sudo cp nerdctl /usr/local/bin/
nerdctl自体はdocker-cliとほぼ互換性が保たれているので、特に変わりなく使用することができますね。k8sでデプロイされた各コンテナを参照する際には、–namespace=k8s.ioをオプション付与して利用します。
$ sudo nerdctl --namespace=k8s.io ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
00298ed4aa7a registry.k8s.io/coredns/coredns:v1.9.3 "/coredns -conf /etc…" 7 days ago Up k8s://kube-system/coredns-565d847f94-zwwhb/coredns
kubeadmの導入
CRIが準備できたので、実際にkubernetesのレポジトリ追加、パッケージ導入を作業していきます。レポジトリ追加作業前に必要となるパッケージについて導入しておきましょう。
$ sudo apt update && sudo apt install -y apt-transport-https ca-certificates curl
先ほどDockerレポジトリを追加したのと同様の手順で、kubernetesのレポジトリを追加していきます。GPGキーのインポートとレポジトリ情報のapt上への登録ですね。
$ sudo curl -fsSLo /etc/apt/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
$ echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main
登録された情報を元にパッケージリストを更新、実際にkubeadm/kubelet/kubectlの必要パッケージをインストールしていきます。
$ sudo apt update && sudo apt install -y kubelet kubeadm kubectl
クラスターのセットアップ
kubeadmを利用してクラスターをセットアップしていきます。今回はネットワークアドオンとしてcalicoを使用するので、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
k8s-develop1 NotReady control-plane 24m v1.25.4
ネットワークアドオンのインストール
kubernetes上で使用するネットワークアドオンを導入していきます。今回はcaliloを使用するので、最新版のマニフェストファイルを適用していきましょう。詳細なセットアップについては、Install Calico networking and network policy for on-premises deploymentsから確認できます。
$ curl -LO https://raw.githubusercontent.com/projectcalico/calico/v3.24.5/manifests/calico.yaml
またpod networkに特定のレンジ(10.244.0.0/16)を今回指定しているので、ダウンロードしたマニフェストファイルの特定の箇所を修正していきます。
calico.yaml(アンコメントと設定値の修正)
# The default IPv4 pool to create on startup if none exists. Pod IPs will be
# chosen from this range. Changing this value after installation will have
# no effect. This should fall within `--cluster-cidr`.
- name: CALICO_IPV4POOL_CIDR
value: "10.244.0.0/16"
上記のマニフェストの修正後、クラスターに適用および展開します。
$ kubectl apply -f calico.yaml
calicoに関連するPodのステータスを確認しましょう。各PodのステータスがRunningとなっていれば、CNIが問題なく展開されている状態です。
$ kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-798cc86c47-z4k7g 1/1 Running 0 2m20s
calico-node-vd8f2 1/1 Running 0 2m20s
coredns-565d847f94-6ctnq 1/1 Running 0 37m
coredns-565d847f94-pkz5w 1/1 Running 0 37m
etcd-k8s-develop1 1/1 Running 8 37m
kube-apiserver-k8s-develop1 1/1 Running 6 37m
kube-controller-manager-k8s-develop1 1/1 Running 10 37m
kube-proxy-6gthf 1/1 Running 0 37m
kube-scheduler-k8s-develop1 1/1 Running 8 37m
またCNIプラグインが正常に展開されたので、nodeのstatusがreadyとなっています。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-develop1 Ready control-plane 38m v1.25.4
シングルノードクラスターとして利用する準備
kubernetesのcontrol-planeには原則としてpodがscheduleされないように設定されています。(control-planeに過剰にpodがscheduleされ、リソースが逼迫するのを防ぐ為にこのような措置が取られています。)podのscheule可否はtaintで制御されていますでの、設定されたtaintを変更しましょう。
$ kubectl taint node k8s-develop1 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
ピンバック:kubeadm + containerdによるkubernetesクラスタ構築 – Canned Catfood Gaming