おうちクラウドにkubernetes環境を構築

# はじめに
Minikubeだと物足りない感じがあったので、ESXiで立てたVMでkubernetesの環境を作ります。
ラズパイクラスタ作るとかも考えてたのですが、半導体価格高騰の影響でラズパイが高いので、それは諦めてVM立てて作ることにしました。


# 準備
AlmaLinuxのノードを構築します。
メモリは2GだとMAXまで使ってしまってそうだったので4Gで設定。
|VM|CPU(Core)|MEM(GB)|DISK(GB)|0S|IP Address|comment|
|-|-|-|-|-|-|-|
|k8s-master|2|4|100|AlmaLinux8.7|192.168.0.111|コントローププレーン(マスターノード)|
|k8s-worker01|2|4|100|AlmaLinux8.7|192.168.0.112|ワーカーノード1台目|
|k8s-worker02|2|4|100|AlmaLinux8.7|192.168.0.113|ワーカーノード2台目|

CRI:containerd
CNI:Calico
上記設定で構築していく

# 共通設定
コントロールプレーンもワーカーノードも共通の設定をします。
root権限で実行していきます。

## パッケージアップデート
```console
# dnf -y update
```

## swap領域の無効化
swap領域とは、ストレージ上の領域をメモリの延長領域として使う技術をいう。
swap領域が設定されていると、kubeletが起動しません。なので無効化させる。
```console
# swapon --show
NAME      TYPE      SIZE USED PRIO
/dev/dm-1 partition   4G   0B   -2
```
一時的にswapを無効化
```console
# swapoff -a
# swapon --show
(何も表示されない) 
```
これだとまだ不完全で、再起動するとswapが復活してしまうので、
永続的にswapを無効化する必要がある。
以下のとおり、swapの記載がある行をコメントアウトすることで永続無効化させることができる。
```console
# cp -p /etc/fstab /etc/fstab.org
# vi /etc/fstab
```
コメントアウトさせる
```diff:/etc/fstab
- /dev/mapper/almalinux_k8s--master-swap none                    swap    defaults        0 0
+ # /dev/mapper/almalinux_k8s--master-swap none                    swap    defaults        0 0
```

## SELinux無効化
```console
# cp -p /etc/selinux/config /etc/selinux/config.org
# vi /etc/selinux/config
```
```diff:/etc/selinux/config
- SELINUX=enforcing
+ SELINUX=permissive
```
以下SELinuxの設定の参考
- enforcing: SELinux有効(アクセス制御有効、ログ出力あり)
- permissive: SELinux有効(アクセス制御無効、ログ出力あり)
- disabled: SELinux無効(アクセス制御無効、ログ出力なし)


## 名前解決の設定
各ノードで名前解決できるようにしておく。
DNSサーバーが立っていて設定済みであれば不要。
```console
# vi /etc/hosts
```
```diff:/etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.0.111 k8s-master
192.168.0.112 k8s-worker01
192.168.0.113 k8s-worker02
```
## 必要ポートの空き確認
`ss -atn`コマンドを使ってポート空き状態を確認します。
必要なポートは以下のリンクで確認できます。

https://kubernetes.io/ja/docs/reference/networking/ports-and-protocols/

## MACアドレスとproduct_uuidが一意であることを確認
他のノードとMACアドレス、プロダクトUUIDが重複していないことを確認します。
特にVMの場合は重複する可能性があるので。
```console
# ip link
```
```console
# cat /sys/class/dmi/id/product_uuid
```

## Firewalld無効化
自動起動設定の無効化。再起動したときに自動起動しない(=停止する)。
```console
# systemctl disable firewalld
```

## IPv6無効化
```console
# echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf
# echo "net.ipv6.conf.default.disable_ipv6 = 1" >> /etc/sysctl.conf
```

## 設定反映
設定反映のために再起動させておく。
```console
# shutdown -r now
```

## コンテナランタイムをインストール
コンテナランタイムには、各ノードがPodを生成処理を出す高レベルランタイム。
高レベルランタイムから指示を受けて、コンテナを生成する低レベルランタイムの2種類がある。

コンテナランタイムの選択肢はいくつかあります。
今回は`containerd`をインストールしてみる。

以下のページを参考にインストールすればいいのだが・・・。
https://kubernetes.io/ja/docs/setup/production-environment/container-runtimes/#containerd
上記ページでは、こちらのGitHubのドキュメントを参考にするよう書いてある。
https://github.com/containerd/containerd/blob/main/docs/getting-started.md
そして、上記ドキュメントを読むと、Docker公式を読めと書いてある。
https://docs.docker.com/engine/install/centos/

ということで、Docker公式の手順に従ってインストールする。

### 古いパッケージのアンインストール
インストールされていなくても、以下実行する分には問題なし。
```console
# dnf remove docker \
             docker-client \
             docker-client-latest \
             docker-common \
             docker-latest \
             docker-latest-logrotate \
             docker-logrotate \
             docker-engine
```
### Dockerインストール
```console
# dnf config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
# dnf -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```

### Docker起動時の設定
プライベートレジストリがある場合は、`insecure-registries`を設定する。
```console
# cat << EOF > /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2",
  "storage-opts": [
    "overlay2.override_kernel_check=true"
  ],
  "insecure-registries": [
    "192.168.0.105"
  ]
}
EOF
```
### Docker起動＆起動設定
`systemctl daemon-reload`は設定を変更した場合に実行しないといけない。
```console
# systemctl daemon-reload
# systemctl start docker
# systemctl enable docker
```

### Kubernetesパッケージリポジトリ設定
```console
# cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kube*
EOF
```

### Kubernetes関連パッケージのインストール
```console
# dnf -y install kubelet kubeadm kubectl --disableexcludes=kubernetes
```
### kubeletの設定
cgroupfsドライバーとして利用できるのは、cgroupfsとsystemdがあるようで、initプロセスでsystemdを使っている場合は、systemdを使うようにするらしい。
https://kubernetes.io/ja/docs/setup/production-environment/container-runtimes/
```console
# mkdir /var/lib/kubelet
# cat <<EOF > /var/lib/kubelet/config.yaml
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
cgroupDriver: "systemd"
EOF
# mkdir /etc/systemd/system/kubelet.service.d
# cat <<EOF > /etc/systemd/system/kubelet.service.d/20-extra-args.conf
[Service]
Environment="KUBELET_EXTRA_ARGS=--fail-swap-on=false"
EOF
# systemctl enable --now kubelet
# systemctl daemon-reload
```
## iproute-tcインストール
```console
# dnf -y install iproute-tc
```

### containerdインストール
CRIのひとつである、containerdをインストールします。
公式ドキュメントを参考にインストールしました。（というか公式そのまま）

https://kubernetes.io/ja/docs/setup/production-environment/container-runtimes/#containerd

```console
# cat > /etc/modules-load.d/containerd.conf <<EOF
overlay
br_netfilter
EOF
```
```console
# modprobe overlay
# modprobe br_netfilter
```
### ブリッジでのトラフィック許可設定
以下の設定を入れて、`sysctl --system`で設定を反映させる。(設定反映は再起動でも良い)
```console
# cat <<EOF >  /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward                 = 1
EOF
# sysctl --system
```
### モジュールが読み込まれていることを確認
`br_netfilter`と`overlay`モジュールが表示されること
```console
# lsmod | grep br_netfilter
br_netfilter           24576  0
bridge                290816  1 br_netfilter
# lsmod | grep overlay
overlay               139264  0
```
### カーネルパラメータ確認
各パラメータが1で設定されていることを確認する
```console
# sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
```
### config変更
`config.toml`の`disable_plugin`リストからcriを削除する。
```console
# cp -p /etc/containerd/config.toml /etc/containerd/config.toml.org
# vi /etc/containerd/config.toml
```
```diff:/etc/containerd/config.toml
- disabled_plugins = ["cri"]
+ disabled_plugins = [""]
```
```console
# systemctl restart containerd
```


# コントロールプレーンのセットアップ
本番運用する場合は、コントロールプレーンを複数台でロードバランシングするみたいですが、今回はお試しでの構築のため、コントロールプレーン1台構成(シングルコントロールプレーン)とします。

https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/

## コントロールプレーンノードの初期化
`kubeadm init <args>`で初期化する。
<args>の部分で色々とオプションを設定する必要があるようです。
| オプション | 内容 |
|-|-|
| --pod-network-cidr | CNI(Podネットワークアドオン)のCIDR(サイダー)を指定。この設定がpodに払い出されるIPアドレスセグメントになる。下記の設定ではCalicoのデフォルト値(192.168.0.0/16)を設定している。 |
| --control-plane-endpoint | HA構成にする予定がある場合は、LBのIPアドレスを設定すれば良さそう。シングル構成にしない場合はオプションつけなくて良さそう。  ※シングル構成にしてから、あとでHA構成にはできないようなので作り直しになるっぽい。 |
| --apiserver-advertise-address | ノードに複数のIPアドレスを割り当ててる場合に、どのIPアドレスを使うかを指定する。 |
| --cri-socket | 異なるランタイムを複数インストールしている状態の場合、どれを使用するかを指定するのに使う。 |

以下のとおりで設定。
※成功時に表示される、`kubeadm join...`の部分はワーカーノードを加えるときに必要になるため控えておくこと。
```console:コンソール
# kubeadm init --pod-network-cidr=192.168.0.0/16
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/

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

kubeadm join 192.168.0.111:6443 --token nekoto.o0nekotonustimih \
        --discovery-token-ca-cert-hash sha256:6e55555da567890517b35d60df22222d7a2bc53f85f3fc0d7633b7ff13049453
```
## 環境変数のパスを指定
rootユーザーの場合は、以下を実行する。(kubeadm initに成功した場合のコメントにも書いてある)
```console
# export KUBECONFIG=/etc/kubernetes/admin.conf
```
## 動作確認
ブラウザでアクセス可能かどうかを確認。
`http://192.168.0.111:6443/api`にアクセスできること。
この時点ではコントロールプレーンはNotReady状態。
```console
# kubectl get node
NAME         STATUS     ROLES           AGE     VERSION
k8s-master   NotReady   control-plane   2d23h   v1.27.1
```
corednsがPendingの状態。
```console
# kubectl get pod -A
NAMESPACE     NAME                                 READY   STATUS    RESTARTS      AGE
kube-system   coredns-5d78c9869d-24jbg             0/1     Pending   0             2d23h
kube-system   coredns-5d78c9869d-tqfcs             0/1     Pending   0             2d23h
kube-system   etcd-k8s-master                      1/1     Running   1 (12h ago)   2d23h
kube-system   kube-apiserver-k8s-master            1/1     Running   1 (12h ago)   2d23h
kube-system   kube-controller-manager-k8s-master   1/1     Running   1 (12h ago)   2d23h
kube-system   kube-proxy-4z55l                     1/1     Running   1 (12h ago)   2d23h
kube-system   kube-scheduler-k8s-master            1/1     Running   1 (12h ago)   2d23h
```
## Calicoインストール
公式に書いてあるとおりにインストールする。

https://docs.tigera.io/calico/latest/getting-started/kubernetes/quickstart
```console
# kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.1/manifests/tigera-operator.yaml
# kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.1/manifests/custom-resources.yaml
```
コントロールプレーンがReady状態になっていることを確認。
```console
# kubectl get nodes
NAME         STATUS   ROLES           AGE   VERSION
k8s-master   Ready    control-plane   3d    v1.27.1
```
corednsを含む、すべてのPodがRunning状態であることを確認。
```console
# kubectl get pod -A
NAMESPACE          NAME                                       READY   STATUS    RESTARTS      AGE
calico-apiserver   calico-apiserver-6d788c89ff-lbwbn          1/1     Running   0             4m54s
calico-apiserver   calico-apiserver-6d788c89ff-s2zsp          1/1     Running   0             4m54s
calico-system      calico-kube-controllers-789dc4c76b-blwh2   1/1     Running   0             5m39s
calico-system      calico-node-jj54g                          1/1     Running   0             5m39s
calico-system      calico-typha-6b5f86658b-khcl5              1/1     Running   0             5m39s
calico-system      csi-node-driver-6mbmj                      2/2     Running   0             5m39s
kube-system        coredns-5d78c9869d-24jbg                   1/1     Running   0             3d
kube-system        coredns-5d78c9869d-tqfcs                   1/1     Running   0             3d
kube-system        etcd-k8s-master                            1/1     Running   1 (13h ago)   3d
kube-system        kube-apiserver-k8s-master                  1/1     Running   1 (13h ago)   3d
kube-system        kube-controller-manager-k8s-master         1/1     Running   1 (13h ago)   3d
kube-system        kube-proxy-4z55l                           1/1     Running   1 (13h ago)   3d
kube-system        kube-scheduler-k8s-master                  1/1     Running   1 (13h ago)   3d
tigera-operator    tigera-operator-549d4f9bdb-p8qp4           1/1     Running   0             6m45s
```

# ワーカーノードの構築
コントロールプレーンと同じく環境変数のパスを指定しておく。
```console
# export KUBECONFIG=/etc/kubernetes/admin.conf
```
コントロールプレーンで`kubeadm init`したときに表示されていたコマンドを実行する。
```console
# kubeadm join 192.168.0.111:6443 --token nekoto.o0nekotonustimih \
        --discovery-token-ca-cert-hash sha256:6e55555da567890517b35d60df22222d7a2bc53f85f3fc0d7633b7ff13049453
```
トークンは、発行されてから24時間しか有効期限がないようである。必要であれば再発行すること。
```console
# kubeadm token create
e1oxsm.m7p2c9t9pv5oat84
```
トークンを忘れてしまった場合は、こちらのコマンドで確認が可能である。
```console
# kubeadm token list
TOKEN                     TTL         EXPIRES                USAGES                   DESCRIPTION                                                EXTRA GROUPS
e1oxsm.m7p2c9t9pv5oat84   23h         2023-05-06T14:10:28Z   authentication,signing   <none>                                                     system:bootstrappers:kubeadm:default-node-token
```
コントロールプレーンで確認すると、ワーカーノードが追加されていることが分かる。
joinしてすぐだとNotReadyになっているが、しばらく待つとReadyになる。
```console
# kubectl get nodes
NAME           STATUS   ROLES           AGE     VERSION
k8s-master     Ready    control-plane   3d1h    v1.27.1
k8s-worker01   Ready    <none>          4m15s   v1.27.1
```
同じ要領でもう1つワーカーノードを追加して終わり。
```console
# kubectl get nodes
NAME           STATUS   ROLES           AGE    VERSION
k8s-master     Ready    control-plane   3d2h   v1.27.1
k8s-worker01   Ready    <none>          68m    v1.27.1
k8s-worker02   Ready    <none>          24s    v1.27.1
```

# 参考書籍

構築はこちらの書籍が一番参考になりました。

https://book.impress.co.jp/books/1118101053

# 参考にしたかった書籍
評価高かったので最初にこっち買ったけど、構築に関する部分が全然なくて積読してた。

https://book.impress.co.jp/books/1119101148

レビューに分かりやすいと書いてあって次に購入。こちらも構築に関する情報が足りず構築できず。

https://www.ric.co.jp/book/development/detail/253

# 参考サイト様
お世話になったサイト様。理解深まりました。

https://tech-mmmm.blogspot.com/2023/03/kubernetes-cri-dockerd.html

https://engineeroutput.com/kubernetes%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%97%E3%81%A6%E3%81%BF%E3%81%BE%E3%81%97%E3%81%9F/

https://none06.hatenadiary.org/entry/2022/05/28/025115