Kubespray+Vagrant で かんたん Kubernetes デプロイ

## はじめに

[Non-graceful node shutdown](https://github.com/kubernetes/enhancements/issues/2268) や [kube-fencing](https://github.com/kvaps/kube-fencing) を試してみたくなり、Kubespray+Vagrant で Kubernetes のテスト環境を作る事にしました。
Non-graceful node shutdown の詳細については[毛利さんの記事](https://qiita.com/y-mo/items/ecb9207175543392ffbe)を参照して下さい。

なお、この記事は３部作の１番目です。続編は以下の２つです。
* [K8s v1.24 の non-graceful node shutdown を試す](https://qiita.com/yosshy/items/868d709e9e8d99634d90)
* [K8s v1.24 の non-graceful node shutdown を kube-fencing で自動化する](https://qiita.com/yosshy/items/c6882609dd20f4d3fe64)

## 用意するもの

Vagrant がインストールされた Linux マシン
* CPU：Intel または AMD の 64bit (x86-64/amd64)
* メモリ：最低 8GB は必要です。16GB あると安心。
* ネットワーク：インターネットに接続可能なネットワークが必要です。HTTP プロキシなしを前提に説明します。
* ディストリビューション：Ubuntu でも CentOS でも構いません。私の手元では Ubuntu Linux 20.04.5 を使用しました。
* その他：Python 3, Git, VirtualBox, Vagrant をインストールしておいて下さい。

## Kubespray 実行手順

Linux マシンにログインします。
後で kube-fencing を試したい場合は、kube-fencing からリモートログインする為に kubespray を実行したアカウント名やパスワードを登録しないといけなせん。そのため、kubespray 専用のアカウントを作って作業した方が良いでしょう。

GitHub から Kubespray を取ってきます。
```
yosshy@nuc2:~$ cd
yosshy@nuc2:~$ git clone https://github.com/kubernetes-sigs/kubespray.git
yosshy@nuc2:~$ cd kubespray
yosshy@nuc2:~/kubespray$ 
```
Ansible 用の virtualenv を作って有効にしましょう。
```
yosshy@nuc2:~/kubespray$ virtualenv --python=/usr/bin/python3 venv
yosshy@nuc2:~/kubespray$ source venv/bin/activate
(venv) yosshy@nuc2:~/kubespray$ pip install -r requirements.txt
```
Vagrantfile 中の設定を変更します。
今回は non-graceful node shutdown テストのため、1マスター＋2ワーカーの３ノード構成を作ります。
あと、最近の Ansible を使うためのオマジナイも追加。
```  
diff --git a/Vagrantfile b/Vagrantfile
index 63292bd1..123e0fa4 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -60,9 +60,9 @@ $multi_networking ||= "False"
 $download_run_once ||= "True"
 $download_force_cache ||= "False"
 # The first three nodes are etcd servers
-$etcd_instances ||= $num_instances
+$etcd_instances ||= 1
 # The first two nodes are kube masters
-$kube_master_instances ||= $num_instances == 1 ? $num_instances : ($num_instances - 1)
+$kube_master_instances ||= 1
 # All nodes are kube nodes
 $kube_node_instances ||= $num_instances
 # The following only works when using the libvirt provider
@@ -247,6 +247,7 @@ Vagrant.configure("2") do |config|
       # And limit the action to gathering facts, the full playbook is going to be ran by testcases_run.sh
       if i == $num_instances
         node.vm.provision "ansible" do |ansible|
+          ansible.compatibility_mode = "2.0"
           ansible.playbook = $playbook
           ansible.verbose = $ansible_verbosity
           $ansible_inventory_path = File.join( $inventory, "hosts.ini")
```
inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml 中の設定を変更します。
Feature Gate をいじって、non-graceful node shutdown を有効にします。
執筆時点の Kubespray では、kube_version: に v1.24.6 が指定されています。必要であれば変更して下さい。
```
diff --git a/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml b/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml
index af17a2cc..2dded166 100644
--- a/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml
+++ b/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml
@@ -155,6 +155,9 @@ kube_encrypt_secret_data: false
 # kubelet_shutdown_grace_period: 60s
 # kubelet_shutdown_grace_period_critical_pods: 20s
 
+kube_feature_gates:
+  - NodeOutOfServiceVolumeDetach=true
+
 # DNS configuration.
 # Kubernetes cluster name, also will be used as DNS domain
 cluster_name: cluster.local
```
いよいよ k8s のデプロイに入ります。
特に問題なければ 3個の VM が作成され、その上に K8s クラスターがデプロイされます。
```
(venv) yosshy@nuc2:~/kubespray$ vagrant up
（中略）
PLAY RECAP *********************************************************************
k8s-1                      : ok=813  changed=214  unreachable=0    failed=0    skipped=1068 rescued=0    ignored=4   
k8s-2                      : ok=497  changed=127  unreachable=0    failed=0    skipped=629  rescued=0    ignored=0   
k8s-3                      : ok=497  changed=127  unreachable=0    failed=0    skipped=629  rescued=0    ignored=0   
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

土曜日 15 10月 2022  12:49:55 +0900 (0:00:00.200)       0:22:16.166 *************** 
=============================================================================== 
download : download_container | Load image into the local container registry -- 43.01s
kubernetes/preinstall : Update package management cache (APT) ---------- 42.92s
kubernetes/preinstall : Install packages requirements ------------------ 37.48s
kubernetes/control-plane : kubeadm | Initialize first master ----------- 34.43s
kubernetes/kubeadm : Join to cluster ----------------------------------- 33.99s
download : download_container | Load image into the local container registry -- 33.72s
kubernetes-apps/network_plugin/flannel : Flannel | Wait for flannel subnet.env file presence -- 31.90s
download : download_container | Load image into the local container registry -- 25.73s
download : download_container | Load image into the local container registry -- 24.48s
download : download_container | Load image into the local container registry -- 21.55s
download : download_container | Load image into the local container registry -- 21.36s
container-engine/containerd : containerd | Unpack containerd archive --- 20.15s
kubernetes-apps/ansible : Kubernetes Apps | Start Resources ------------ 17.48s
download : download_container | Load image into the local container registry -- 14.53s
kubernetes/node : install | Copy kubelet binary from download dir ------ 13.67s
container-engine/validate-container-engine : Populate service facts ---- 13.46s
download : download_container | Download image if required ------------- 13.38s
download : download_file | Copy file from cache to nodes, if it is available -- 12.57s
kubernetes/preinstall : Preinstall | wait for the apiserver to be running -- 10.85s
download : download_container | Download image if required ------------- 10.76s

```
ノードを確認してみましょう。
```
(venv) yosshy@nuc2:~/kubespray$ ./inventory/sample/artifacts/kubectl.sh get nodes 
NAME    STATUS   ROLES           AGE   VERSION
k8s-1   Ready    control-plane   12m   v1.24.6
k8s-2   Ready    <none>          10m   v1.24.6
k8s-3   Ready    <none>          10m   v1.24.6
```
無事に3ノード動いていますね。Pod も確認してみましょう。
```
(venv) yosshy@nuc2:~/kubespray$ ./inventory/sample/artifacts/kubectl.sh get pods -A
NAMESPACE     NAME                                  READY   STATUS    RESTARTS       AGE
kube-system   coredns-74d6c5659f-2zftd              1/1     Running   0              170m
kube-system   coredns-74d6c5659f-82ncg              1/1     Running   0              169m
kube-system   dns-autoscaler-59b8867c86-sl9b7       1/1     Running   0              169m
kube-system   kube-apiserver-k8s-1                  1/1     Running   2              173m
kube-system   kube-controller-manager-k8s-1         1/1     Running   2 (168m ago)   173m
kube-system   kube-flannel-rsnf8                    1/1     Running   0              171m
kube-system   kube-flannel-xx5wl                    1/1     Running   0              171m
kube-system   kube-flannel-z5s8p                    1/1     Running   0              171m
kube-system   kube-proxy-grz5k                      1/1     Running   1              171m
kube-system   kube-proxy-vnnfq                      1/1     Running   0              171m
kube-system   kube-proxy-xnj64                      1/1     Running   0              171m
kube-system   kube-scheduler-k8s-1                  1/1     Running   3 (168m ago)   173m
kube-system   nginx-proxy-k8s-2                     1/1     Running   0              171m
kube-system   nginx-proxy-k8s-3                     1/1     Running   0              171m
kube-system   nodelocaldns-2xvwp                    1/1     Running   0              169m
kube-system   nodelocaldns-h449k                    1/1     Running   0              169m
kube-system   nodelocaldns-v8jhc                    1/1     Running   0              169m
```

kubectl をそのまま実行できるようにします。
```
(venv) yosshy@nuc2:~/kubespray$ mkdir ~/.kube
(venv) yosshy@nuc2:~/kubespray$ cp inventory/sample/artifacts/admin.conf ~/.kube/config 
(venv) yosshy@nuc2:~/kubespray$ sudo cp inventory/sample/artifacts/kubectl /usr/local/bin/
(venv) yosshy@nuc2:~/kubespray$ cd
(venv) yosshy@nuc2:~$ 
```

## PVの準備

### NFSサーバのセットアップ

今回は VM ホストに NFS サーバをインストールして PV のバックエンドにします。
ディストリビューションによって必要なパッケージ名は異なりますので、各ディストリビューションの NFS 関連情報に従って NFS サーバをインストールして下さい。
```
(venv) yosshy@nuc2:~$ sudo apt install -y nfs-kernel-server
```
パッケージのインストールが終わったら、/etc/exports を編集します。
```
# /etc/exports: the access control list for filesystems which may be exported
#               to NFS clients.  See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes       hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4        gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes  gss/krb5i(rw,sync,no_subtree_check)
#
/srv/nfs4       172.18.8.0/24(rw,sync,fsid=0,crossmnt,no_subtree_check)
/srv/nfs4/pvs   172.18.8.0/24(rw,sync,no_subtree_check)
```
NFS でエクスポートするディレクトリを作成します。
```
(venv) yosshy@nuc2:~$ sudo mkdir -p /srv/nfs4/pvs
(venv) yosshy@nuc2:~$ sudo chmod 1777 /srv/nfs4/pvs
```
エクスポートします。
```
(venv) yosshy@nuc2:~$ sudo exportfs -a
(venv) yosshy@nuc2:~$ sudo exportfs 
/srv/nfs4     	172.18.8.0/24
/srv/nfs4/pvs 	172.18.8.0/24
```

### NFS CSI ドライバのセットアップ

GitHub から [NFS CSI driver for Kubernetes](https://github.com/kubernetes-csi/csi-driver-nfs) のリポジトリを取ってきます。
```
(venv) yosshy@nuc2:~$ git clone https://github.com/kubernetes-csi/csi-driver-nfs.git
(venv) yosshy@nuc2:~$ cd csi-driver-nfs/
(venv) yosshy@nuc2:~/csi-driver-nfs$ git checkout release-4.0
```

次にドライバのマニフェストを適用します。
```
(venv) yosshy@nuc2:~/csi-driver-nfs$ ./deploy/install-driver.sh v4.0.0 local
use local deploy
Installing NFS CSI driver, version: v4.0.0 ...
serviceaccount/csi-nfs-controller-sa created
clusterrole.rbac.authorization.k8s.io/nfs-external-provisioner-role created
clusterrolebinding.rbac.authorization.k8s.io/nfs-csi-provisioner-binding created
csidriver.storage.k8s.io/nfs.csi.k8s.io created
deployment.apps/csi-nfs-controller created
daemonset.apps/csi-nfs-node created
NFS CSI driver installed successfully.
```

しばらくしたら CSI-NFS 関連のコンテナが動いているかどうか確認します。
```
(venv) yosshy@nuc2:~/csi-driver-nfs$ kubectl get pods -A | grep csi
kube-system   csi-nfs-controller-84bff54b84-rk5kv   3/3     Running   0              70s
kube-system   csi-nfs-node-8x9qg                    3/3     Running   0              69s
kube-system   csi-nfs-node-j78l5                    3/3     Running   0              69s
kube-system   csi-nfs-node-wjbws                    3/3     Running   0              69s
(venv) yosshy@nuc2:~/csi-driver-nfs$ 
```

次に、deploy/example/storageclass-nfs.yaml を変更します。
172.18.8.1 はホストの IP アドレスです。違う場合は適宜変更して下さい。
```
diff --git a/deploy/example/storageclass-nfs.yaml b/deploy/example/storageclass-nfs.yaml
index 2fd2cdae..7c9e85a7 100644
--- a/deploy/example/storageclass-nfs.yaml
+++ b/deploy/example/storageclass-nfs.yaml
@@ -5,14 +5,11 @@ metadata:
   name: nfs-csi
 provisioner: nfs.csi.k8s.io
 parameters:
-  server: nfs-server.default.svc.cluster.local
-  share: /
-  # csi.storage.k8s.io/provisioner-secret is only needed for providing mountOptions in DeleteVolume
-  # csi.storage.k8s.io/provisioner-secret-name: "mount-options"
-  # csi.storage.k8s.io/provisioner-secret-namespace: "default"
+  server: 172.18.8.1
+  share: /pvs
 reclaimPolicy: Delete
 volumeBindingMode: Immediate
 mountOptions:
-  - nconnect=8  # only supported on linux kernel version >= 5.3
+  # - nconnect=8  # only supported on linux kernel version >= 5.3
   - hard
   - nfsvers=4.1
```

上記の YAML を使って CSI-NFS の StorageClass を登録します。
```
(venv) yosshy@nuc2:~/csi-driver-nfs$ kubectl apply -f deploy/example/storageclass-nfs.yaml
storageclass.storage.k8s.io/nfs-csi created
(venv) yosshy@nuc2:~/csi-driver-nfs$ 
```

PVC 作成で自動的に PV が作成されるかテストします。
後でテスト用リソースを処分しやすいよう、テスト専用のネームスペース csitest を作って作業します。
```
(venv) yosshy@nuc2:~/csi-driver-nfs$ kubectl create ns csitest
namespace/csitest created
(venv) yosshy@nuc2:~/csi-driver-nfs$ kubectl -n csitest apply -f deploy/example/pvc-nfs-csi-dynamic.yaml 
persistentvolumeclaim/pvc-nfs-dynamic created
(venv) yosshy@nuc2:~/csi-driver-nfs$ kubectl -n csitest get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                     STORAGECLASS   REASON   AGE
pvc-f26e733c-20f5-4108-848b-a8849c7980f8   10Gi       RWX            Delete           Bound    csitest/pvc-nfs-dynamic   nfs-csi                 58s
yosshy@nuc2:~/csi-driver-nfs$ kubectl delete ns csitest
namespace "csitest" deleted
(venv) yosshy@nuc2:~/csi-driver-nfs$ 
```
PVC 作成をトリガーに PV が自動作成されました。

## まとめ

Kubespray と Vagrant を使って、１台の VirtualBox ホスト上に自動的に VM を作成して Kubernetes クラスタを構成できる事を確認しました。手っ取り早く複数台構成の Kubernetes 開発環境や評価環境を用意したい方には便利なソリューションの１つと言えるでしょう。

続編「[K8s v1.24 の non-graceful node shutdown を試す](https://qiita.com/yosshy/items/868d709e9e8d99634d90)」に続きます。