K8S Kubeadm による構築の流れ
最近 kubeadm で K8S クラスタを構築した流れと、途中で遭遇したいくつかの問題を、ノートとして記録しておきます。
デプロイ環境
- 構築バージョン:kubernetes-1.24.4(HA)
- コンテナランタイム:containerd-1.6.8
- OS:CentOS7
前提条件
1)クラスタノードの計算リソースは 2 コア CPU・2GB RAM 以上のマシンであること。ノード間は内部ネットワークまたはパブリックネットワークで到達可能であること。
2)各マシンの hostname と MAC アドレスは重複してはならず、product_uuid も一意である必要があります。hostname で対応するノードにアクセスできるよう、/etc/hosts ファイルを設定します。
3)kubernetes 1.24 以降、デフォルトのコンテナランタイムは Docker ではなく Containerd に変わりました。docker を使いたい場合は cri-dockerd をインストールする必要があります。
4)HA アーキテクチャには 3 つの master コントロールプレーンノードが必須です。高可用性には外部 etcd とスタック型 etcd の 2 種類のトポロジーがあり、アーキテクチャは下図のとおりです。
外部 etcd

メリット:コントロールプレーンインスタンスや etcd メンバーを失ったときの影響が小さく、スタック型 HA トポロジーのようにクラスタの冗長性に影響することがありません。
デメリット:より多くのマシンノードが必要で、etcd HA クラスタを個別にデプロイするための 3 台のサーバーが必要です。
スタック型 etcd

メリット:このトポロジーはコントロールプレーンと etcd メンバーを同一ノード上に結合します。外部 etcd クラスタを使う場合に比べてセットアップが簡単で、レプリカ管理もしやすくなります。
デメリット:スタック型クラスタには結合による同時障害のリスクがあります。1 つのノードに障害が発生すると、etcd メンバーとコントロールプレーンインスタンスの両方が失われ、冗長性が損なわれます。コントロールプレーンノードを増やすことでこのリスクを下げられます。
load balancer: 内部ロードバランサーで、それ自体の HA を保証する必要があります。worker ノードのために apiServer をリバースプロキシし、apiServer への統一されたアクセス IP を提供するとともに、master の可用性を検知できます。
5)Linux のスワップパーティションを無効化します。(スワップパーティション:メモリ不足時にハードディスクをストレージとして使う仕組みで、システム性能は低下しますが、より多くのメモリデータを保持でき、メモリが満杯になってもサーバーがすぐに固まらないという利点があります)
# CentOS でスワップパーティションを無効化する:
# 恒久的に無効化:
# 1、vim /etc/fstab で設定ファイルに入り、以下の設定をコメントアウトする
/dev/mapper/cl-swap swap swap defaults 0 0
# 2、コメントアウトして保存する
# 一時的に無効化:
# swapoff -a コマンドを実行する
swapoff -a
6)ファイアウォールを無効化するか、公式サイト指定のポートを開放します
# ファイアウォールの状態を確認
firewall-cmd --state
# 1、ファイアウォールのポートを開放。master ノードには 6443 2379-2380 10250 10251 10252 が必要
firewall-cmd --permanent --add-port=6443/tcp
firewall-cmd --permanent --add-port=2379-2380/tcp
firewall-cmd --permanent --add-port=10250/tcp
firewall-cmd --permanent --add-port=10251/tcp
firewall-cmd --permanent --add-port=10252/tcp
# ファイアウォールのポートを開放。node-worker ノードには 10250 30000-32767 が必要
firewall-cmd --permanent --add-port=10250/tcp
firewall-cmd --permanent --add-port=30000-32767/tcp
# ファイアウォール設定のリロード
firewall-cmd --reload
# ポートが正しく設定されたか確認
firewall-cmd --zone=public --list-ports
# 2、ファイアウォールを無効化する選択肢もある!安全ではないが、多くの問題を回避できる。たとえば後述の CNI ネットワークコンポーネントに必要なポートなど
# firewall を停止
systemctl stop firewalld.service
# firewall の自動起動を無効化
systemctl disable firewalld.service
7)yum リポジトリ情報 /etc/yum.repos.d/kubernetes.repo を設定します(公式リポジトリは使えない可能性があり、VPN 経由のアクセスが必要になるため、ここでは中国国内の Alibaba リポジトリを使用します)
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
8)カーネルのフォワーディングとブリッジフィルタリングを有効にし、ipset と ipvsadm をインストールして、対応するカーネルモジュールを有効化します
公式ドキュメント:コンテナランタイム | Kubernetes
# ロードするカーネルモジュールを宣言する
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-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
vm.swappiness = 0
EOF
# 設定を反映させる
sudo sysctl --system
# ipset ipvsadm をインストール
yum -y install ipset ipvsadm
mkdir -p /etc/sysconfig/modules/
# ipvs に必要なカーネルモジュールを宣言する
cat > /etc/sysconfig/modules/ipvs.modules <<EOF
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack
EOF
# 実行権限を付与して実行する
chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules
# 反映されたか確認する
lsmod | grep ip_vs
# コンソール出力
ip_vs_sh 16384 0
ip_vs_wrr 16384 0
ip_vs_rr 16384 0
ip_vs 180224 6 ip_vs_rr,ip_vs_sh,ip_vs_wrr
nf_conntrack 176128 1 ip_vs
nf_defrag_ipv6 24576 2 nf_conntrack,ip_vs
libcrc32c 16384 2 nf_conntrack,ip_vs
9)SELinux を permissive モードに設定します(実質的に無効化するのと同等です)
sudo setenforce 0
コンテナランタイムのインストール
Containerd
参考:実戦:centos7 での containerd のインストール-20211023_一念一生~one のブログ-CSDN ブログ_centos で containerd をインストール
Containerd 1.6 以降のバージョンでは、ローカル依存の libseccomp を 2.4 以上にアップグレードする必要があります。さもないとコンテナが起動できない事態が起こり得ます。私自身が実際にハマったポイントで、エラー情報が非常に見つけにくいうえ、CentOS の yum リポジトリには最高でも 2.3.1 しかないため、公式から自分でダウンロードしてインストールするしかありません。
関連コマンド:
# 現在の libseccomp バージョンを確認
rpm -qa | grep libseccomp
# 旧バージョンをアンインストール
rpm -e libseccomp-devel-2.3.1-4.el7.x86_64 --nodeps
rpm -e libseccomp-2.3.1-4.el7.x86_64 --nodeps
# 新バージョンの libseccomp をダウンロード
wget http://rpmfind.net/linux/centos/8-stream/BaseOS/x86_64/os/Packages/libseccomp-2.5.1-1.el8.x86_64.rpm
# libseccomp をインストール
rpm -ivh libseccomp-2.5.1-1.el8.x86_64.rpm
クラスタの初期化
1)kubeadm、kubectl、kubelet の三大コンポーネントをインストールする
# コンポーネントをインストール
sudo yum install -y kubelet-1.24.4 kubeadm-1.24.4 kubectl-1.24.4 --disableexcludes=kubernetes
# kubelet の自動起動を設定
sudo systemctl enable --now kubelet
2)コントロールプレーンノードを初期化する
kubeadm でノードを初期化します。コマンドは kubeadm init【オプション】です。
より詳しいオプションは公式ドキュメントを参照してください:Kubeadm | Kubernetes
ここでは私のローカル環境の設定値を使っています。本番環境では対応する IP アドレスを本番の内部ネットワーク IP に変えるだけで OK です。
# コマンドでコントロールプレーンを初期化できる
sudo kubeadm init \
--control-plane-endpoint "k8s-cluster:6443" \
--apiserver-advertise-address=192.168.5.27 \
--apiserver-bind-port=6443 \
--image-repository registry.aliyuncs.com/google_containers \
--kubernetes-version v1.24.4 \
--cert-dir=/etc/kubernetes/pki \
--service-cidr=10.96.0.0/12 \
--pod-network-cidr=172.24.0.0/16 \
--upload-certs
# 初期化ファイルを使う方法もある。まず初期化ファイルを生成し、自分のシナリオに合わせてパラメータを修正する
kubeadm config print init-defaults > kubeadm-config.yaml
kubeadm init --config = kubeadm-config.yaml --upload-certs
主要なオプションの説明:
--control-plane-endpoint:内部の load balancer の IP アドレス、または内部ネットワークの DNS ドメイン名情報を記入します。worker ノードはこのドメイン名または IP を通じてクラスタ内部の master ノードにアクセスし、master の高可用性を確保します。
--apiserver-advertise-address:現在のコントロールプレーンの内部ネットワーク IP アドレスを記入します。
--pod-network-cidr:pod の内部ネットワークセグメントの IP。
--service-cidr:service の内部ネットワークセグメントの IP。
--image-repository:イメージリポジトリのアドレス。中国国内向けに設定しています。私はここで Alibaba のリポジトリを使い、取得速度を向上させました。
初期化時、kubeadm は k8s.gcr.io/pause イメージを取得する必要がありますが、中国国内ではブロックされているため、ダウンロードに失敗して初期化がこける可能性が高いです。初期化に失敗した場合はコンテナランタイムのログを確認し、対応するイメージを取得できていないことが分かれば、この問題です(私は数日間ハマって原因を探しました)。解決方法:
# コンテナランタイムのログを確認
journalctl -xeu containerd
# k8s.gcr.io は海外ネットワークに接続しないと取得できないため、k8s の基盤コンテナ pause がしばしば取得できない。
# crictl で中国国内のイメージリポジトリから取得し、ctr tag を使って解決できる
# 国内ミラーからイメージを取得
crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6
# タグを付け替える
ctr -n k8s.io image tag registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6 k8s.gcr.io/pause:3.6
3)初期化成功後の出力
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
Alternatively, if you are the root user, you can run:
export KUBECONFIG=/etc/kubernetes/admin.conf
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
You can now join any number of the control-plane node running the following command on each as root:
kubeadm join k8s-cluster:6443 --token v6rrcg.vnl78b94fo740xv9 \
--discovery-token-ca-cert-hash sha256:5dd2edf3d668bd0608897a0097455c6a514a91fd0b1ad4d1044789d9f56123ce \
--control-plane --certificate-key 4c9b2b3a2d19db4b4f58681e6cf264a0b7bb1eacf443ae90a50bd0e8c11e0c31
Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join k8s-cluster:6443 --token v6rrcg.vnl78b94fo740xv9 \
--discovery-token-ca-cert-hash sha256:5dd2edf3d668bd0608897a0097455c6a514a91fd0b1ad4d1044789d9f56123ce
以上の出力情報から、対応する node ノードをクラスタに参加させるためのコマンド情報が分かります。
ノードのクラスタ参加
kubeadm join コマンドに token 情報を付ければ、他の master ノードや worker ノードを参加させられます。token はデフォルトで 6 時間有効、再生成した token は 24 時間有効です。token の期限が切れた場合は、コマンドで再生成できます。
# master コントロールプレーンノードの参加時に実行:(証明書の暗号化情報を取得)コントロールプレーンノードの参加には token + certs 情報が必要
kubeadm init phase upload-certs --upload-certs
# worker ノード参加用の token 情報を生成
kubeadm token create --print-join-command
# 現在有効な token の一覧を確認
kubeadm token list
ネットワーク CNI コンポーネントのインストール
自社のビジネスシナリオと規模に応じて、セキュリティとパフォーマンスの観点から適切な CNI コンポーネントを選択できます。注意:クラスタに存在できる CNI コンポーネントは一種類だけです。
詳細は公式ドキュメントも参照してください:kubeadm を使ったクラスタの作成 | Kubernetes

ここでは定番の CNI コンポーネントである Calico を採用します。
Calico 公式サイト:オンプレミス環境への Calico ネットワークとネットワークポリシーのインストール (tigera.io)
# まず、クラスタに operator をインストールする。
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.24.0/manifests/tigera-operator.yaml
# Calico の設定に必要なカスタムリソースをダウンロードする
curl https://raw.githubusercontent.com/projectcalico/calico/v3.24.0/manifests/custom-resources.yaml -O
# custom-resources.yaml ファイルをカスタマイズする
# その中の pod ネットワークセグメントの IP 情報を修正する
# calicoNetwork:
# # Note: The ipPools section cannot be modified post-install.
# ipPools:
# - blockSize: 26
# cidr: 172.24.0.0/16 (ここを kubeadm 実行時と同じ pod ネットワークセグメントに修正する)
# encapsulation: VXLANCrossSubnet
# natOutgoing: Enabled
# nodeSelector: all()
# マニフェストを作成して Calico をインストールする。
kubectl create -f custom-resources.yaml
# Calico 関連の pod の稼働状況を確認する。4 分ほど待つ必要がある
kubectl get pod -all-namespace
コントロールプレーンのデプロイ
kuboard
公式ドキュメント:https://www.kuboard.cn/
docker イメージでワンコマンドでコンテナを起動し、その後クラスタのインポートウィザードを進めます。
クラスタ情報の監視に成功:

まとめ
ここまでで、クラスタはほぼ使える状態になりました。kubeadm がコントロールプレーンの初期化を担い、ノードは token でクラスタに参加し、Calico が pod ネットワークを提供し、kuboard が可視化監視を担当します。この一連の流れで最もハマりやすいポイントは 2 か所に集中しています。Containerd の libseccomp のバージョンが古いこと、そして pause イメージがネットワークの都合で取得に失敗することです。どちらもエラーメッセージが分かりにくく、コンテナランタイムのログから手がかりを探す必要があります。今後はクラスタ内部のファイルストレージのために NAS ファイルサービスを追加すること、さらに Istio の導入やネットワーク入口 Ingress の設定を検討し、企業の実情に応じて適切に構成していく必要があります。
COMMENTS