Kindを使ったKubernetes環境構築

### 背景とか目的

- 最近Kubernetesを使って、色々試したいことが多い。
- 一発コマンドでマルチクラスタを作って、きれいさっぱりさよならしたい。
- 物理マシン数台でclusterを構築したり、管理するのがめんどくさい。
- 自分の開発マシンだけでマルチノードクラスタを立ち上げて、アプリの動作を確認したい。

### Kindとは

`K`ubernetes `IN` `D`ocker らしいです。少しだけ概要にも触れておきます。

![diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112819/9018ca44-63e3-9edf-1a48-69552f316d91.png)

Kubernetesのノードとしてdockerコンテナを起動しています。dockerコンテナ一つがKubernetesのノードとして割り当てられます。
dockerコンテナの中(仮想的なノードの中)ではコンテナランタイム(containerd)が起動しています。このコンテナランタイムがKubernetesのコンポーネントやアプリケーションをコンテナとして管理しています。dockerコンテナの中ではkubeletが起動してコンテナインスタンスをノードと勘違いさせています。
dockerコンテナ内でコンテナランタイムとkubeletが動作することで、コンテナの中でコンテナ制御を行えるようにしています。
ホストからのコマンド通信(例えばkubectl)はport mappingされて最終的にはdockerコンテナ内のapi-serverに通信するようにしています。

### 環境と事前準備

Ubuntu 20.04で動作確認しています。

まず、Dockerをインストールします。([docker.ce](https://docs.docker.com/engine/install/ubuntu/)でもOK)

```bash
> sudo apt install docker.io
```

dockerがちゃんとインストールされたか確認します。

```bash
> docker version
Client:
 Version:           20.10.12
 API version:       1.41
 Go version:        go1.16.2
 Git commit:        20.10.12-0ubuntu2~20.04.1
 Built:             Wed Apr  6 02:14:38 2022
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server:
 Engine:
  Version:          20.10.12
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.2
  Git commit:       20.10.12-0ubuntu2~20.04.1
  Built:            Thu Feb 10 15:03:35 2022
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.5.9-0ubuntu1~20.04.1
  GitCommit:
 runc:
  Version:          1.1.0-0ubuntu1~20.04.1
  GitCommit:
 docker-init:
  Version:          0.19.0
  GitCommit:
```

次にKubernetesをインストールします。

```bash
> sudo apt install apt-transport-https curl

> curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add
> sudo apt-add-repository "deb http://apt.kubernetes.io/ kubernetes-xenial main"
> sudo apt install kubeadm kubelet kubectl kubernetes-cni

> kubectl version --client=true --short
Flag --short has been deprecated, and will be removed in the future. The --short output will become the default.
Client Version: v1.24.0
Kustomize Version: v4.5.4
```

### Kindでクラスタ構築

#### Kindをインストール

```bash
> go install sigs.k8s.io/kind@v0.12.0
> kind version
kind v0.12.0 go1.18.1 linux/amd64
```

#### シングルノードクラスタ作成と削除

```bash
> kind create cluster

> kubectl cluster-info --context kind-kind
Kubernetes control plane is running at https://127.0.0.1:39857
CoreDNS is running at https://127.0.0.1:39857/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

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

これだけです。これでシングルノードのクラスタが構築されています。

```bash
> kubectl get nodes -o wide
NAME                 STATUS   ROLES                  AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE       KERNEL-VERSION      CONTAINER-RUNTIME
kind-control-plane   Ready    control-plane,master   72s   v1.23.4   172.18.0.2    <none>        Ubuntu 21.10   5.13.0-40-generic   containerd://1.5.10
```

KindはKubernetesを起動するときにconfigを`${HOME}/.kube/config`に追加し、ホストのポート6443をコンテナとして起動したapi-serverへマッピングしているため、ホストマシンから`kubectl`にてクラスタを管理することも可能です。

```bash
> docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS          PORTS                       NAMES
10e90347f982   kindest/node:v1.23.4   "/usr/local/bin/entr…"   55 seconds ago   Up 52 seconds   127.0.0.1:39857->6443/tcp   kind-control-plane
```

`kind-control-plane`がKubernetesのcontrol-planeとして起動していることがわかります。

```bash
kind delete cluster
Deleting cluster "kind" ...
```

削除するときはこれだけです。

ただ、これだとシングルノードのマスターだけなのでさみしい。。。
なのでマルチノードクラスタをcontainerで作ってみます。(あくまで物理マシンは一台です。)

#### マルチノードクラスタ

以下のyamlをkindコマンドに渡して、マルチノードクラスタを作成します。

```bash

> cat multi-node.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker

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

kubectl cluster-info --context kind-kind

Not sure what to do next? 😅  Check out https://kind.sigs.k8s.io/docs/user/quick-start/
```

すると、Kubernetesのノードとして複数のdockerコンテナが起動して、マルチノードクラスタを構築できます。

```bash
> kubectl get nodes -o wide
NAME                 STATUS   ROLES                  AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE       KERNEL-VERSION      CONTAINER-RUNTIME
kind-control-plane   Ready    control-plane,master   59s   v1.23.4   172.18.0.3    <none>        Ubuntu 21.10   5.13.0-40-generic   containerd://1.5.10
kind-worker          Ready    <none>                 34s   v1.23.4   172.18.0.4    <none>        Ubuntu 21.10   5.13.0-40-generic   containerd://1.5.10
kind-worker2         Ready    <none>                 34s   v1.23.4   172.18.0.2    <none>        Ubuntu 21.10   5.13.0-40-generic   containerd://1.5.10

> docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED              STATUS              PORTS                       NAMES
026f6377ccb8   kindest/node:v1.23.4   "/usr/local/bin/entr…"   About a minute ago   Up About a minute                               kind-worker2
cbf0ef62fc77   kindest/node:v1.23.4   "/usr/local/bin/entr…"   About a minute ago   Up About a minute                               kind-worker
84c9aab0833c   kindest/node:v1.23.4   "/usr/local/bin/entr…"   About a minute ago   Up About a minute   127.0.0.1:41081->6443/tcp   kind-control-plane
```

次にアプリ(Pods)をデプロイしてみます。

Kindに渡すyamlを少し修正して、ホストポート30070からWebにつなげるようにしておきます。

```bash
> cat multi-node.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30080
    hostPort: 30070
- role: worker
- role: worker

> kind create cluster --config=multi-node.yaml
...
```

KubernetesのServiceとしてポート30080を設定し、Podのポート80とマッピングしておきます。アプリケーションはDockerHubにあるnginxを利用します。

```bash
> cat app-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web-nginx
  name: web-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-nginx
  template:
    metadata:
      labels:
        app: web-nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: web-nginx
spec:
  selector:
    app: web-nginx
  type: NodePort
  ports:
    - port: 80
      nodePort: 30080

> kubectl create -f app-nginx.yaml
```

すると、kubectlでPod/Serviceが以下のように起動できたことが確認できます。

```bash
> kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP           NODE           NOMINATED NODE   READINESS GATES
web-nginx-585955c9c8-hkrk4   1/1     Running   0          73s   10.244.1.3   kind-worker2   <none>           <none>
web-nginx-585955c9c8-jrccb   1/1     Running   0          73s   10.244.1.2   kind-worker2   <none>           <none>
web-nginx-585955c9c8-kz49v   1/1     Running   0          73s   10.244.2.2   kind-worker    <none>           <none>

> kubectl get services -o wide
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE     SELECTOR
kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP        5m46s   <none>
web-nginx    NodePort    10.96.10.179   <none>        80:30080/TCP   110s    app=web-nginx
```

port mappingは30070(host) --- 30080(k8s service) --- 80(pod)になっているので、` http:/ホストマシンのIP/:30070/ ` にブラウザでアクセスすると以下が見えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112819/643b4633-48c3-6585-4725-3a7c246147cf.png)

これ以降は補足情報になりますが、

```bash
> docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED         STATUS         PORTS                                                 NAMES
05fca9ee8e9e   kindest/node:v1.23.4   "/usr/local/bin/entr…"   4 minutes ago   Up 4 minutes   0.0.0.0:30072->30080/tcp                              kind-worker2
8abd9d1e1926   kindest/node:v1.23.4   "/usr/local/bin/entr…"   4 minutes ago   Up 4 minutes   127.0.0.1:40971->6443/tcp, 0.0.0.0:30070->30080/tcp   kind-control-plane
b557b30529b4   kindest/node:v1.23.4   "/usr/local/bin/entr…"   4 minutes ago   Up 4 minutes   0.0.0.0:30071->30080/tcp                              kind-worker
```

なぜアプリケーションpodsのコンテナがホストから見えないかというと、dockerコンテナの中のcontainerdで管理されているからです。例えば、上記のデプロイメントの場合、アプリケーションPodは、kind-worker もしくは kind-worker2 のノードで起動していることがわかります。

一旦dockerコマンド(`docker exec -it [container id] /bin/bash`とか)を利用して、ホストマシンからkind-workerに乗り込みます。(これは特定のノードの中にログインした状態と同じです。)
dockerコンテナの中にはdockerはいないので、`ctr`コマンドを使って`containerd`が管理するコンテナをチェックします。

```bash
> ctr -n k8s.io container list
CONTAINER                                                           IMAGE                                            RUNTIME     
32c712d22a7f60bec95c4d2d51c9d0057222fbd4d032a5f351bea7675188ef84    k8s.gcr.io/pause:3.6                             io.containerd.runc.v2
40a7ef598849e6709d11e0c6ae8bda1ac3047c0ee9f6b3e8cdfc7dcdcbac1cd8    k8s.gcr.io/kube-proxy:v1.23.4                    io.containerd.runc.v2
93496727074f147a1446d91d887d3e64a7d55710cec583e46de861fb84840628    k8s.gcr.io/pause:3.6                             io.containerd.runc.v2
943d7c26a01f3aef786c3f26e731a52362c6ec8844283d1a6bb83081c25c1a89    docker.io/kindest/kindnetd:v20211122-a2c10462    io.containerd.runc.v2
9a157042a8068975b92fc8091b73e5180936265612bd73a86a3905bb40ba857a    k8s.gcr.io/pause:3.6                             io.containerd.runc.v2
ea2c62477b749ef208f11c108a1396bbebca64c00398b8a5a169555af157ebc4    docker.io/library/nginx:latest                   io.containerd.runc.v2
```

上記の通り、Kubenretesのコンポーネント群とアプリケーションPod(nginx)が起動していることがわかります。
namespaceがk8s.gcr.ioになっていますが、調べ方は[こちら](https://qiita.com/tomoyafujita/items/fde08478b729413275d6)を参照ください

最後にすべての過去を消し去ります。

```bash
> kind delete cluster
Deleting cluster "kind" ...
```

### 参照

- [kind](https://kind.sigs.k8s.io/)