kubeadmを使ってubuntu22.04にKubernetes環境を構築する

こんにちは。
株式会社クラスアクト インフラストラクチャ事業部の大塚です。

本記事ではubuntu22.04にkubeadmを使ってKubernetes環境を構築していきたいと思います。
master1台×worker1台で環境を作ります。

# なぜkubeadmでKubernetes環境を構築するのか？
今まで散々、microk8sやRancher、GKE等を使用してKubernetes環境を構築してきましたが、これらの環境だとKubernetesのnetworkやcertificate、ETCD周りの検証がしにくいなという問題にぶつかりました。

私は近いうちにCKA(Certified Kubernetes Administrator)と呼ばれるKuberenetesの資格を取得しようと考えております。

https://training.linuxfoundation.org/ja/certification/certified-kubernetes-administrator-cka/

そのためにUdemyの以下の動画（界隈では鉄板の教材であるみたい）を使用してKubernetesを勉強したりいじったりしているのですが、上記であげたETCDなどの検証がしにくいようなイメージがあります。
誤認であれば恐縮なのですが、microk8sやGKEは便利であるがゆえにブラックボックスのようになっている部分が多いように感じ、その部分が勉強をしているときに気持ち悪かったりもやもやしたり、色々調べまわったりして疲れるなと。

https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests/

また、CKAの試験範囲にkubeadmはばっちり入ってるっぽいので避けては通れません汗
そのため、改めてKubernetes環境構築をすることになりました。


# 用語

kubeadm
---
>Kubeadmは、Kubernetesクラスタを作成するためのベストプラクティスの「ファストパス」としてkubeadm initとkubeadm joinを提供するために構築されたツールである。
>kubeadmは、最小限の実行可能なクラスタを立ち上げて実行するために必要なアクションを実行します。設計上、マシンのプロビジョニングではなく、ブートストラップのみに関心があります。同様に、Kubernetes Dashboard、監視ソリューション、クラウド固有のアドオンなど、さまざまな便利なアドオンをインストールすることも、対象外です。
>その代わり、kubeadmの上に、より高レベルでよりカスタマイズされたツールが構築されることを期待しています。理想的には、すべてのデプロイの基礎としてkubeadmを使用することで、適合するクラスタを簡単に作成することができます。

https://kubernetes.io/docs/reference/setup-tools/kubeadm/

EOF
---
ヒアドキュメントというやつを使うときに使う（？）
以下のような記述をすると、終了文字として"EOF"を指定し、"EOF"が入力されるまでファイルの入力を続ける様子。
```
cat > sample.txt << EOF
> text1
> text2
> text3 
> EOF
```

containerd
---
Dockerから派生したソフトウェアのようです。

https://www.designet.co.jp/faq/term/?id=Y29udGFpbmVyZA

https://containerd.io/

Docker内部で使っているやつっぽい？
以下の書籍ではそのように記載されておりました。

https://www.amazon.co.jp/Kubernetes%E5%AE%8C%E5%85%A8%E3%82%AC%E3%82%A4%E3%83%89-%E7%AC%AC2%E7%89%88-Top-Gear-%E9%9D%92%E5%B1%B1/dp/4295009792


# 構築時参考にしているサイト
基本的に以下のサイト通りにやれば問題ないと思います。少なくとも私の環境は問題なかったです。
ただ、書いていないこともありそうだったのでそこを備忘録として残しておこうかなと。。。

https://www.linuxtechi.com/install-kubernetes-on-ubuntu-22-04/


# HWスペック
参考程度に記載しておきます。

#### k8s-master 
![Image](https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/ac5526f7-545c-41ad-8d2d-3ce2c71091f4)

#### k8s-worker01
![Image](https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/19a3be97-aad9-4f50-93a4-79220f25e224)

# 構築

## master/worker全ノードで実行すること
まず、名前解決できるように/etc/hostsに対してデータを入れ込みます。
以下はmasterノードのhostsファイルになり、★がついているところが追記したデータとなります。
worker02や03は今後追加するものなのでここでは気になさらないでください。
```/etc/hosts
127.0.0.1 localhost
127.0.1.1 k8s-master
192.168.2.30 k8s-master　　　★
192.168.2.31 k8s-worker01　　★
192.168.2.32 k8s-worker02　　★
192.168.2.33 k8s-worker03　　★

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
```

swapを無効にするために以下のコマンドを実行
```
root@k8s-master:~# swapoff -a
root@k8s-master:~# sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
```

カーネルモジュールにロードする（らしい）
```
root@k8s-master:~# tee /etc/modules-load.d/containerd.conf <<EOF
overlay
br_netfilter
EOF
root@k8s-master:~# modprobe overlay
root@k8s-master:~# modprobe br_netfilter
```

kubernetesのconfigにカーネルパラメータを設定
(EOFの後に">"みたいなのが出てきたらEOFを入力してエンターキーを押下すれば問題なさそうです。)
```
root@k8s-master:~# tee /etc/sysctl.d/kubernetes.conf <<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
```

上記変更を反映させます。バーッと出力されますが気にしなくてよいかと・・・
```
root@k8s-master:~# sysctl --system
```

containerdをインストールし、起動します。
```
root@k8s-master:~# apt install -y curl gnupg2 software-properties-common apt-transport-https ca-certificates
root@k8s-master:~# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmour -o /etc/apt/trusted.gpg.d/docker.gpg
root@k8s-master:~# add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
root@k8s-master:~# apt update
root@k8s-master:~# apt install -y containerd.io
root@k8s-master:~# containerd config default | sudo tee /etc/containerd/config.toml >/dev/null 2>&1
root@k8s-master:~# sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml
root@k8s-master:~# systemctl restart containerd
root@k8s-master:~# systemctl enable containerd
```

Kubernetes関連のコマンドなどをインストールします。
```
root@k8s-master:~# curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmour -o /etc/apt/trusted.gpg.d/kubernetes-xenial.gpg
root@k8s-master:~# apt-add-repository "deb http://apt.kubernetes.io/ kubernetes-xenial main"
root@k8s-master:~# apt update
root@k8s-master:~# apt install -y kubelet kubeadm kubectl
root@k8s-master:~# apt-mark hold kubelet kubeadm kubectl
```

ここまですべてのノードで実行します。masterもworkerも関係ありません。

## masterで実行すること 01

以下コマンドを実行してKubernetesクラスタを初期化していきます。
出力が長いので途中のものは省きますが、必要そうな部分は残しておきます。
また、このあたりの出力はコピーしておきましょう。
また、コマンドのendpointのオプションは各個人の環境に依存するので気を付けてください。基本的にmasterとなるノードを指定してあげればOKの認識です。
```
root@k8s-master:~# kubeadm init --control-plane-endpoint=k8s-master
~ 中略 ~
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 control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:

  kubeadm join k8s-master:6443 --token pg46jl.pvynngmtqsrukf6l \
        --discovery-token-ca-cert-hash sha256:0497a12ab9c4ff1eb3c21bf6f92d2346c3645a00f54c746a5000f1ffe9a107fc \
        --control-plane

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join k8s-master:6443 --token pg46jl.pvynngmtqsrukf6l \
        --discovery-token-ca-cert-hash sha256:0497a12ab9c4ff1eb3c21bf6f92d2346c3645a00f54c746a5000f1ffe9a107fc
```

問題なく初期化ができたら以下のコマンドを実行します。
このコマンドを実行しないと、Kubernetesクラスタの初期化が完了した後に「やったー！試しにkubectl get podコマンドたたいてみよー！」と叩いてみても、tcp connection refused的なエラーではじかれます。（1敗）
```
root@k8s-master:~# mkdir -p $HOME/.kube
root@k8s-master:~# cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
root@k8s-master:~# chown $(id -u):$(id -g) $HOME/.kube/config
root@k8s-master:~# kubectl cluster-info
Kubernetes control plane is running at https://k8s-master:6443
CoreDNS is running at https://k8s-master:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
```

試しにkubectl get nodeとget podをたたいてみます。
dnsのpodがpendingステートとなっていますが、これでいったん問題ありません。後ほどCalicoをデプロイしますがその時に稼働を開始します。（おそらくこのタイミングだとk8s内の内部ネットワークがまだない的な判断をして稼働しないのかなぁと？）
ノードもNotReadyとなっていますが同様に今は気にしないで大丈夫です。
```
root@k8s-master:~# kubectl get node
NAME         STATUS     ROLES           AGE     VERSION
k8s-master   NotReady   control-plane   6m11s   v1.27.2

root@k8s-master:~# kubectl get pod -A
NAMESPACE     NAME                                 READY   STATUS    RESTARTS   AGE
kube-system   coredns-5d78c9869d-sswnk             0/1     Pending   0          6m11s
kube-system   coredns-5d78c9869d-zmjp6             0/1     Pending   0          6m11s
kube-system   etcd-k8s-master                      1/1     Running   0          6m18s
kube-system   kube-apiserver-k8s-master            1/1     Running   0          6m15s
kube-system   kube-controller-manager-k8s-master   1/1     Running   0          6m20s
kube-system   kube-proxy-jkfzg                     1/1     Running   0          6m11s
kube-system   kube-scheduler-k8s-master            1/1     Running   0          6m22s
```

## workerで実行すること
kubeadm initで実は出力されていた"kubeadm join"コマンドを実行します。
これは個々人の環境によって異なりますので出力されたコマンドをコピペして使用してください。
microk8sを使ったことがある方ならピンとくると思いますが、microk8s add-nodeコマンドをmaster側で実行してコマンドを吐き出させて、そのコマンドをworker側で入力してクラスタ化していると思うのですが基本的にそれと同じニュアンスで問題なさそうです。
```
root@k8s-worker01:~# kubeadm join k8s-master:6443 --token pg46jl.pvynngmtqsrukf6l --discovery-token-ca-cert-hash sha256:0497a12ab9c4ff1eb3c21bf6f92d2346c3645a00f54c746a5000f1ffe9a107fc
```

master側でノードが認識されているかを確認します。
worker01となっているので問題なさそうですね。ステートがNotReadyとなっていますが、これも今は気にしないで大丈夫です。
```
root@k8s-master:~# kubectl get node
NAME           STATUS     ROLES           AGE    VERSION
k8s-master     NotReady   control-plane   135m   v1.27.2
k8s-worker01   NotReady   <none>          58s    v1.27.2
```

# masterで実行すること 02
以下のyamlファイルを実行してCalicoをローカルのKubernetes環境にデプロイしてきます。
```
root@k8s-master:~# kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/calico.yaml
```

これを実行した後、5~10分程度待つと以下のようにpendingステートだったDNS podもrunnningになったり、NotReadyだったノードもReadyに代わるはずです。
ここまでやってKubernetes環境構築完了となります。
```
root@k8s-master:~# kubectl get pod -A
NAMESPACE     NAME                                       READY   STATUS    RESTARTS      AGE
kube-system   calico-kube-controllers-6c99c8747f-xhc26   1/1     Running   0             4m3s
kube-system   calico-node-8lhx6                          1/1     Running   0             4m3s
kube-system   calico-node-w8vcl                          1/1     Running   0             4m3s
kube-system   coredns-5d78c9869d-sswnk                   1/1     Running   0             140m
kube-system   coredns-5d78c9869d-zmjp6                   1/1     Running   0             140m
kube-system   etcd-k8s-master                            1/1     Running   0             140m
kube-system   kube-apiserver-k8s-master                  1/1     Running   2 (82m ago)   140m
kube-system   kube-controller-manager-k8s-master         1/1     Running   4 (80m ago)   140m
kube-system   kube-proxy-gjlhj                           1/1     Running   0             6m15s
kube-system   kube-proxy-jkfzg                           1/1     Running   0             140m
kube-system   kube-scheduler-k8s-master                  1/1     Running   1 (90m ago)   140m

root@k8s-master:~# kubectl get node
NAME           STATUS   ROLES           AGE     VERSION
k8s-master     Ready    control-plane   140m    v1.27.2
k8s-worker01   Ready    <none>          6m19s   v1.27.2
```

# 2025/3/17 追記
上記手順では構築できなくなっていたので更新します。
以下をとりあえず実行。
ここまで環境用意したら、snapshotとっておいた方が無難。
```
sudo su -
apt update && apt upgrade -y
vi /etc/hosts
以下を記載
127.0.0.1 localhost
172.18.251.19 k8s-master
172.18.251.16 k8s-worker01
172.18.251.18 k8s-worker02
172.18.251.20 k8s-worker03
```
これを実行
```
swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
tee /etc/modules-load.d/containerd.conf <<EOF
overlay
br_netfilter
EOF
tee /etc/sysctl.d/kubernetes.conf <<EOF
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system
apt install -y curl gnupg2 software-properties-common apt-transport-https ca-certificates
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmour -o /etc/apt/trusted.gpg.d/docker.gpg
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
apt update
apt install -y containerd.io
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null 2>&1
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
systemctl restart containerd
systemctl enable containerd
```
これを実行
```
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.32/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.32/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
```

masterで以下を実行
```
kubeadm init --control-plane-endpoint=k8s-master --pod-network-cidr=192.168.0.0/16
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
kubectl cluster-info
kubectl get node
```

workerで以下を実行
※この出力はkubeadm initコマンドで出力されている。
```
kubeadm join k8s-master:6443 --token mdnrvz.w6qhxk7y9owdkzh8 \
        --discovery-token-ca-cert-hash sha256:8e909e6c462c1f913008fdb94f87069b128ccbc47ec7ef6faa602667623f8289
```

masterでkubectl get nodeコマンドを事項
workerノードが出力されればOK
```
root@k8s-master:~# kubectl get nodes
NAME           STATUS     ROLES           AGE   VERSION
k8s-master     NotReady   control-plane   32m   v1.32.3
k8s-worker01   NotReady   <none>          8s    v1.32.3
```

masterで以下を実施。実行後しばらくしないとpodとかdeployされない。10分以上待ってもいいかも？
Calico公式：
https://docs.tigera.io/calico/latest/getting-started/kubernetes/quickstart
```
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.29.2/manifests/tigera-operator.yaml
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.29.2/manifests/custom-resources.yaml
```

masterで以下を実行してReadyになっていればOK
podも以下のようになる。
```
root@k8s-master:~# kubectl get nodes
NAME           STATUS   ROLES           AGE   VERSION
k8s-master     Ready    control-plane   13m   v1.32.3
k8s-worker01   Ready    <none>          12m   v1.32.3

root@k8s-master:~# kubectl get pod -A
NAMESPACE          NAME                                       READY   STATUS    RESTARTS   AGE
calico-apiserver   calico-apiserver-7ddd789855-dv4mw          1/1     Running   0          98s
calico-apiserver   calico-apiserver-7ddd789855-xpkhl          1/1     Running   0          98s
calico-system      calico-kube-controllers-5f975465c6-mcxjl   1/1     Running   0          98s
calico-system      calico-node-bqhjm                          1/1     Running   0          98s
calico-system      calico-node-xclnt                          1/1     Running   0          98s
calico-system      calico-typha-66dd857759-tdrln              1/1     Running   0          98s
calico-system      csi-node-driver-fwq7b                      2/2     Running   0          98s
calico-system      csi-node-driver-qgsbv                      2/2     Running   0          98s
kube-system        coredns-668d6bf9bc-2f5fn                   1/1     Running   0          3m54s
kube-system        coredns-668d6bf9bc-jd94h                   1/1     Running   0          3m54s
kube-system        etcd-k8s-master                            1/1     Running   2          3m59s
kube-system        kube-apiserver-k8s-master                  1/1     Running   2          4m1s
kube-system        kube-controller-manager-k8s-master         1/1     Running   0          3m59s
kube-system        kube-proxy-c99cw                           1/1     Running   0          3m55s
kube-system        kube-proxy-w5bhf                           1/1     Running   0          3m
kube-system        kube-scheduler-k8s-master                  1/1     Running   2          3m59s
tigera-operator    tigera-operator-ccfc44587-m6ncv            1/1     Running   0          106s
```