kindを用いてMacで手軽にマルチノードK8S環境を構築する

この記事は以下の記事での検証内容を発展させた内容です。この記事を読む前に読んで頂くと理解が深まります。
[Dockerとvctlコンテナでkind @ Mac](https://qiita.com/plumchang/items/3847f0bcf52b286a2b88) by @plumchang 

# 執筆の動機
K8Sの勉強をやろうやろうと思いつつ、先延ばしに。。。ところが、以前、ひょんなことから一念発起して専門書まで買って勉強しようとしたことが有りました。ところが、マルチノードK8Sの環境構築はその当時結構な難易度でした。故にそこで挫折。。。 :scream: :scream_cat: しかし、以前に比べて環境構築が容易になったと知り、@plumchangさんにお願いして検証して貰ったのが、お正月明け。 (@plumchangさん、協力感謝)ところが、マルチノード(例: Master x 1, Worker x 3)といった環境はあまりにもメモリを消費するため、検証が難しかったとのこと。そこで、メモリが豊富に使える環境を用意して筆者が検証をしてみることにしました。

# 検証環境

* macOS 11.4 Big Sur (MacBook Air 2018 DRAM 16GB)[^spec]
* Docker Desktop Version 3.4.0
* Docker Compose Version 1.29.2
* Go 1.16.4
* VMWare Fusion 12.1.2
* kind v0.11.1
* Homebrew 3.1.12
* kind v0.9.0(vctlコンテナ用)

# 環境構築
今回検証を行った環境では、既にDocker Desktop、Docker Compose、Go言語、VMWare Fusionのセットアップは完了していました。故に以下の行程は、これらのアプリケーションのインストールが完了してから実施して下さい。

## kind(Kubernetes in Docker)のインストール
まずは、kindをインストールします。Go言語のパッケージマネージャーである`go get`コマンドでインストール出来ます。

```bash
go get sigs.k8s.io/kind
```

### 2021/12/5追記: Homebrewでインストール可能になりました。
macOS 12.0.1 Montereyで以下の方法でインストール出来ることを確認しました。

```bash
brew install kind
```

* [参考](https://kind.sigs.k8s.io/docs/user/quick-start/#installation)

kind v0.11.1がインストールされました。

## kubectlのインストール
K8Sを操作するコマンドです。Homebrewで簡単にインストール出来ます。

```bash
brew install kubectl
```
インストールが完了したら、以下の内容を`.bashrc`に追記します。

```bash
source <(kubectl completion bash)
```

`.bashrc`を再読込し、有効化します。

```bash
source .bashrc
```

以上で、環境構築が完了しました。それでは、いよいよマルチクラスターK8Sの環境を構築します。

* [参考](https://kind.sigs.k8s.io/docs/user/quick-start#installation)
* [参考(Linux/WSL2@Win向けの情報)](https://kubernetes.io/ja/docs/tasks/tools/install-kubectl/#install-kubectl-on-linux)

# マルチクラスターK8S環境の構築
今回構築するK8Sの環境は、Masterノード x 1、Workerノード x 3という構成です。Dockerのコンテナを用いて構築する方法、vctlコンテナ(VMWare Fusion)を用いて構築する方法の2種類を試します。

## Dockerのコンテナを用いて構築する場合
まずはオーソドックスにDockerのコンテナを用いて環境構築を実施してみます。

### config.ymlの作成
マルチノード環境を構築するための設定を記述したconfig.ymlファイルを作成します。[公式](https://kind.sigs.k8s.io/docs/user/configuration/#name-your-cluster)のドキュメントを参考にします。

## 2022/11/20追記: 外部アクセスへの対応
K8s上で立てたサービスを外部からアクセスしたい場合が有りますが，デフォルトではアクセス出来ません．よってconfig.ymlに設定を追加し，サービスタイプをNodePortに設定したサービスを外部からアクセス出来る様にします．

```yml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30950
    hostPort: 9898
- role: worker
- role: worker
- role: worker
```

:warning: サービスを立てる時にポートを開放する場合，以下の記入例の様にコンテナ内部のポート番号に関係無く，常に30950と紐付けて立てる必要が有ります．外部からアクセスする時，例えばK8sを動作させているマシン上からアクセスする場合は，`http://localhost:9898`でアクセス出来ます．

```yaml:記入例(抜粋)
spec:
  type: NodePort
  ports:
  - name: http
    nodePort: 30950
    port: 5678
```

### 参考

https://kind.sigs.k8s.io/docs/user/configuration/#nodeport-with-port-mappings

https://qiita.com/fruscianteee/items/bee47afe3222f0d073ce

`kind`コマンドを実行します。これでマルチノードK8Sが起動します。

```bash
kind create cluster --name k8s --config=config.yml
```

<details><summary>実行結果</summary><div>

```bash
$ kind create cluster --name k8s --config=config.yml
Creating cluster "k8s" ...
 ✓ Ensuring node image (kindest/node:v1.21.1) 🖼 
 ✓ Preparing nodes 📦 📦 📦 📦  
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing CNI 🔌 
 ✓ Installing StorageClass 💾 
 ✓ Joining worker nodes 🚜 
Set kubectl context to "kind-k8s"
You can now use your cluster with:

kubectl cluster-info --context kind-k8s

Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
```

</div></details>

### ノードの確認
`kubectl`コマンドで確認すると、マルチノード環境が起動していることが分かります。

```bash
$ kubectl get nodes
NAME                STATUS   ROLES                  AGE     VERSION
k8s-control-plane   Ready    control-plane,master   3m19s   v1.21.1
k8s-worker          Ready    <none>                 2m49s   v1.21.1
k8s-worker2         Ready    <none>                 2m49s   v1.21.1
k8s-worker3         Ready    <none>                 2m49s   v1.21.1
```

### Deployの動作確認
動作確認を兼ねてnginxをデプロイしてみます。

```yml:deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
```

以下のコマンドを順に実行します。

```bash
kubectl apply -f deployment.yml
kubectl create service nodeport nginx --tcp=8080:80
kubectl port-forward --address localhost svc/nginx 8080:8080
```

<details><summary>実行結果</summary><div>

```bash
$ kubectl apply -f deployment.yml
deployment.apps/nginx-deployment created
```

```bash
$ kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-66b6c48dd5-fnj9z   1/1     Running   0          48s
nginx-deployment-66b6c48dd5-kssdc   1/1     Running   0          48s
nginx-deployment-66b6c48dd5-x6kcn   1/1     Running   0          48s
```

```bash
$ kubectl create service nodeport nginx --tcp=8080:80
service/nginx created
```

```bash
$ kubectl port-forward --address localhost svc/nginx 8080:8080
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Handling connection for 8080
```

</div></details>

* [参考](https://kubernetes.io/ja/docs/concepts/workloads/controllers/deployment/)
* [参考2](https://techblog.gmo-ap.jp/2019/09/19/kind-kubernetes-in-docker/)

新規ウィンドウでターミナルを起動し、以下のコマンドを実行します。

```bash
curl http://localhost:8080
```

<details><summary>実行結果</summary><div>

```bash
$ curl http://localhost:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```

</div></details>

### 後片付け
以下のコマンドを実行します。

```bash:実行結果
kubectl delete services nginx
kubectl delete -f deployment.yml
```

```bash:実行結果
$ kubectl delete services nginx
service "nginx" deleted
$ kubectl delete -f deployment.yml
deployment.apps "nginx-deployment" deleted
```

### 環境を破棄する場合
以下のコマンドを実行します。

```bash
kind delete cluster --name k8s
```

<details><summary>実行結果</summary><div>

```bash
$ kind delete cluster --name k8s
Deleting cluster "k8s" ...
```

</div></details>

### シェル化してみる
kindによる環境構築は永続的な環境と言うよりは、お試しの環境構築で必要な時に作って、要らなくなった破棄という様な使い方が出来るのではと思いました。(あくまで筆者の主観です)今回はお勉強に使う環境構築なので、必要な時に起動して、終わったら破棄できるように、起動シェルと終了シェルを作っておくことにします。

```bash:build_env.sh
#!/bin/bash

kind create cluster --name k8s --config=config.yml
```

```bash:delete_env.sh
#!/bin/bash

kind delete cluster --name k8s
```

実行権限付与は以下のコマンドを実行します。

```bash
chmod +x build_env.sh
chmod +x delete_env.sh
```

## vctlコンテナを用いて構築する場合
以下の行程はVMWare Fusionがインストールされている環境で実行されることを前提としています。VMWare Fusion Playerは無料で利用できます。今回は検証にMacを使用した為、Fusionを利用していますが、Linux/Windowsを利用されている方は、同じくVMWareのWorkstation ProまたはWorkstatio Playerを利用すると同様に構築出来る様です。([参考](https://qiita.com/YasuhiroABE/items/993a623c05d4dfe4d79c))

### 初期セットアップ
ターミナルを新しく起動し、以下のコマンドを順に実行します。初回のみkubectlやkindとの連携処理が実行されます。
:warning: vctl環境は起動を実行したターミナルを終了した時点で構築した環境が破棄されます。

```bash
vctl system start
```

<details><summary>実行結果</summary><div>

```bash
$ vctl system start
Downloading 3 files...
Downloading [kind-darwin-amd64 99.44% kubectl 84.97% crx.vmdk 46.15%]
Finished kind-darwin-amd64 100.00%
Downloading [kubectl 99.88% crx.vmdk 60.89%]
Finished kubectl 100.00%
Downloading [crx.vmdk 99.42%]
Finished crx.vmdk 100.00%
3 files successfully downloaded.
Preparing storage...
Container storage has been prepared successfully under /Users/yuhkiyano/.vctl/storage
Launching container runtime...
Container runtime has been started.
```

</div></details>

```bash
vctl kind
```

<details><summary>実行結果</summary><div>

```bash
$ vctl kind
vctl-based KIND is ready now. KIND will run local Kubernetes clusters by using vctl containers as "nodes"
* All Docker commands has been aliased to vctl in the current terminal. Docker commands performed in current window would be executed through vctl. If you need to use regular Docker commands, please use a separate terminal window.
```

</div></details>

kindのバージョンが異なることが分かります。vctl用のkindがインストールされました。

```bash
$ kind version
kind v0.9.0 go1.15.2 darwin/amd64
```

ここまで完了したら、一旦PCを再起動します。このまま実行すると、何故かエラー終了してマルチノード環境が構築出来ませんでした。(エラーメッセージの魚拓が残せず。。。)

### マルチノード環境の起動
PC再起動後に以下のコマンドを実行します。

```bash
kind create cluster --name k8s --config=config.yml
```

<details><summary>実行結果</summary><div>

筆者はシェルで実行してます。

```bash
$ kind create cluster --name k8s --config=config.yml
Creating cluster "k8s" ...
 ✓ Ensuring node image (kindest/node:v1.19.1) 🖼 
 ✓ Preparing nodes 📦 📦 📦 📦  
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing CNI 🔌 
 ✓ Installing StorageClass 💾 
 ✓ Joining worker nodes 🚜 
Set kubectl context to "kind-k8s"
You can now use your cluster with:

kubectl cluster-info --context kind-k8s

Thanks for using kind! 😊
```

</div></details>

以下の様にマルチノード環境が構築出来ています。

```bash
$ kubectl get nodes
NAME                STATUS   ROLES    AGE     VERSION
k8s-control-plane   Ready    master   2m48s   v1.19.1
k8s-worker          Ready    <none>   86s     v1.19.1
k8s-worker2         Ready    <none>   86s     v1.19.1
k8s-worker3         Ready    <none>   85s     v1.19.1
```

完成後のデスクトップは以下の様にVM集団に占拠されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469079/b3769404-a71f-2e66-98be-256880166403.png)


### Deployの動作確認
Dockerのコンテナと同様にDeployの動作確認をしました。動作は全く同じであるため割愛します。

### 後始末
以下のコマンドを順に実行します。

```bash
kubectl delete service nginx
kubectl delete -f deployment.yml 
kind delete cluster --name k8s
vctl system stop
```

<details><summary>実行結果</summary><div>

```bash
$ kubectl delete service nginx
service "nginx" deleted
$ kubectl delete -f deployment.yml 
deployment.apps "nginx-deployment" deleted 
$ kind delete cluster --name k8s 
Deleting cluster "k8s" ...
$ vctl system stop
Terminating container runtime...
Container runtime has been stopped.
Detaching storage...
Container storage has been detached.
```

</div></details>

# まとめ
kindを用いてDockerコンテント及びvctlコンテナを用いてマルチノードK8Sの環境構築を試みました。どちらも最終的に出来上がる環境の構成は同じですが、vctlコンテナはVMベースとなっているためか、動作速度がDockerコンテナを用いた環境より遅く感じます。また、メモリなどのリソースも多く消費します。手軽に利用するのであれば、余程の理由(Dockerが嫌い等)が無い限り、Dockerコンテナを用いる方が賢明だと思います。

# 今後の目標
* K8S完全マスター
* GPUサーバーにK8SをKVMで構築して、GPUも使えるWorkerノードを持つ環境を構築
* @plumchangさんも巻き込んでK8S資格 x 3に挑戦

# 補足事項
`kubectl get nodes`コマンドで、ノード情報をを取得した時に、workerノードだけROLESが`<none>`となっており、気持ち悪い状態です。

```bash
$ kubectl get nodes
NAME                STATUS   ROLES                  AGE     VERSION
k8s-control-plane   Ready    control-plane,master   5h31m   v1.21.1
k8s-worker          Ready    <none>                 5h31m   v1.21.1
k8s-worker2         Ready    <none>                 5h31m   v1.21.1
k8s-worker3         Ready    <none>                 5h31m   v1.21.1
```

以下のコマンドで解消。

```bash
kubectl label nodes k8s-worker kubernetes.io/role=worker
```

<details><summary>実行結果</summary><div>

```bash
$ kubectl label nodes k8s-worker kubernetes.io/role=worker
node/k8s-worker labeled
$ kubectl label nodes k8s-worker2 kubernetes.io/role=worker
node/k8s-worker2 labeled
$ kubectl label nodes k8s-worker3 kubernetes.io/role=worker
node/k8s-worker3 labeled
```

</div></details>

以下の様に、workerノードのROLESが`worker`になりました。

```bash
$ kubectl get nodes
NAME                STATUS   ROLES                  AGE     VERSION
k8s-control-plane   Ready    control-plane,master   5h38m   v1.21.1
k8s-worker          Ready    worker                 5h38m   v1.21.1
k8s-worker2         Ready    worker                 5h38m   v1.21.1
k8s-worker3         Ready    worker                 5h38m   v1.21.1
```

# Reference
* [Dockerとvctlコンテナでkind @ Mac](https://qiita.com/plumchang/items/3847f0bcf52b286a2b88)
* [vctl コマンドを使用したコンテナの管理と Kubernetes クラスタの実行](https://docs.vmware.com/jp/VMware-Fusion/12/com.vmware.fusion.using.doc/GUID-78E7339F-7294-4F3E-9AD0-1E14C201FA40.html)
* [VMware Workstation 16 Proでvctl kindを試してみた](https://qiita.com/YasuhiroABE/items/993a623c05d4dfe4d79c)
* [kind公式](https://kind.sigs.k8s.io/docs/user/configuration/#name-your-cluster)
* [kubectlのインストールおよびセットアップ](https://kubernetes.io/ja/docs/tasks/tools/install-kubectl/)
* [How to add roles to nodes in Kubernetes?](https://stackoverflow.com/questions/48854905/how-to-add-roles-to-nodes-in-kubernetes)

[^spec]: DRAM 8GBでは今回構築するマルチノード環境は動作が厳しい様です。