軽量なKubernetes「K3s」でクラスタを組んでみる(Ubuntu Server)

# TL;DR
* めちゃくちゃ軽量なK8sディストリビューション「K3s」をUbuntu Serverで動かす 
* マスター(server)1台、ワーカー(agent)2台のKubernetes(K3s)クラスタを組む
* deployment、service、ingressをデプロイする（サンプルymlあり）

# 構成
K3sをコントロールするマスター(server)を1台と、コンテナ(Pod)を動かすワーカー(agent)を2台用意します。
最低限しか書いていないので厳密にはもっと複雑ですが、こんな感じです。
![構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429161/b5a6af99-6671-680a-74c8-599869c8a10f.png)

クラウドVMでも、ローカルマシン上にVMを立てても、物理マシンを用意しても大丈夫です。
私の場合はローカルマシン(CPU:i5-10500)のHyper-V上にUbuntu Serverの仮想マシンを作成しました。

| OSバージョン | 20.04.2 LTS (Focal Fossa) | 
|:-:|:-:|
| **CPU** | **1コア** | 
| **メモリ**| **1GB** | 

# 構築
以下の流れで構築していきます。

1. ホスト名を一意に設定したUbuntu Serverを3台用意
2. 各サーバで/etc/hostsを設定
3. マスターノードでK3sインストール & 起動
4. ワーカーノードでK3sインストール & クラスタに追加
5. リソースのデプロイ

## 1. ホスト名を一意に設定したUbuntu Serverを3台用意する
ホスト名を重複させないようにするため、以下のように設定をしました。

|  | マスターノード | ワーカーノード1 | ワーカーノード2 |
|:-:|:-:|:-:|:-:|
| ホスト名 | k3s-master | k3s-wk1 | k3s-wk2 |
| IPアドレス | 192.168.1.150 | 192.168.1.151 | 192.168.1.152 |

> ホスト名が重複する場合は、環境変数もしくは起動時のオプションで一意なノード名を指定する必要があります。
> 環境変数の場合　$K3S_NODE_NAME
> 起動時のオプションの場合　--node-name ノード名


## 2. 各サーバで/etc/hostsを設定する
各ノードの/etc/hostsに情報を追記します。

* 単純に名前解決できたほうが楽
* issue「[cannot run k3s server](https://github.com/k3s-io/k3s/issues/60)」の対応策としてhostsに定義を追加することが有効である

**マスターノード(k3s-master)の/etc/hosts**

```shell_session
root@k3s-master:~# cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 k3s-master
192.168.1.150 k3s-master
192.168.1.151 k3s-wk1
192.168.1.152 k3s-wk2
```

**ワーカーノード1(k3s-wk1)の/etc/hosts**

```shell_session
root@k3s-wk1:~# cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 k3s-wk1
192.168.1.150 k3s-master
192.168.1.151 k3s-wk1
192.168.1.152 k3s-wk2
```
**ワーカーノード2(k3s-wk2)の/etc/hosts**

```shell_session
root@k3s-wk2:~# cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 k3s-wk2
192.168.1.150 k3s-master
192.168.1.151 k3s-wk1
192.168.1.152 k3s-wk2
```

## 3. マスターノードでK3sインストール & 起動
K3sが用意しているインストールスクリプトを使います。

```shell_session
root@k3s-master:~# curl -sfL https://get.k3s.io | sh -
[INFO]  Finding release for channel stable
（中略）
[INFO]  systemd: Starting k3s
root@k3s-master:~# 
```

スクリプトの実行が完了するとK3sが起動しているはずです。
kubectlコマンドでnodeが起動していることを確認できます。

```shell_session
root@k3s-master:~# k3s kubectl get nodes
NAME         STATUS   ROLES                  AGE    VERSION
k3s-master   Ready    control-plane,master   2m3s   v1.21.1+k3s1
```

次はワーカーノードの起動ですが、その前に...
クラスタに組み込むために必要な情報(トークン)がマスターノードにあるので確認しておきます。

```shell_session
root@k3s-master:~# cat /var/lib/rancher/k3s/server/token
K109adb1784f9ffbc3ff5e272a6956d9407d7f96f9fcf742586c9fc108a031fc9b9::server:1158cefa7ef95ea0894d3446079d9c4a
```

## 4. ワーカーノードでK3sインストール & クラスタに追加

マスターノードでの起動とトークン確認が終わったら、次はワーカーノードでの作業です。
マスターノードと同じようにインストールスクリプトを使って導入していきます。
ただし、**ワーカーノードとして起動させるためのオプション指定**があるため、少しコマンドが異なります。

* K3S_TOKENにマスターノードで確認した情報(トークン)を設定する
* K3S_URLにマスターノードの6443ポートを設定する


```shell_session
root@k3s-wk1:~# curl -sfL https://get.k3s.io |\
K3S_TOKEN="K109adb1784f9ffbc3ff5e272a6956d9407d7f96f9fcf742586c9fc108a031fc9b9::server:1158cefa7ef95ea0894d3446079d9c4a" \
K3S_URL=https://k3s-master:6443 sh -
[INFO]  Finding release for channel stable
（中略）
[INFO]  systemd: Starting k3s-agent
root@k3s-wk1:~# 
```

マスターノードに戻って、kubectlコマンドでワーカーノードが追加されていることを確認できます。

```shell_session
root@k3s-master:~# k3s kubectl get nodes
NAME         STATUS   ROLES                  AGE   VERSION
k3s-master   Ready    control-plane,master   17m   v1.21.1+k3s1
k3s-wk1      Ready    <none>                 20s   v1.21.1+k3s1
```

もう一台のワーカーノードでも同じインストールコマンドを実行することで、同様にクラスタに追加されます。

```shell_session
root@k3s-wk2:~# curl -sfL https://get.k3s.io |\
> K3S_TOKEN="K109adb1784f9ffbc3ff5e272a6956d9407d7f96f9fcf742586c9fc108a031fc9b9::server:1158cefa7ef95ea0894d3446079d9c4a" \
> K3S_URL=https://k3s-master:6443 sh -
[INFO]  Finding release for channel stable
（中略）
[INFO]  systemd: Starting k3s-agent
------------------------------------------------------------------------ 
root@k3s-master:~# k3s kubectl get nodes
NAME         STATUS   ROLES                  AGE    VERSION
k3s-master   Ready    control-plane,master   24m    v1.21.1+k3s1
k3s-wk1      Ready    <none>                 7m4s   v1.21.1+k3s1
k3s-wk2      Ready    <none>                 1s     v1.21.1+k3s1
```

## 5. リソースをデプロイする
ここまで来たらあとはいつものKubernetesと同じように
kubectlコマンドを使ってリソースをデプロイできます。

ただ、今回はk3sの特徴を生かした[自動デプロイ](https://rancher.com/docs/k3s/latest/en/advanced/#auto-deploying-manifests)機能を使ってみます。
> Any file found in /var/lib/rancher/k3s/server/manifests will automatically be deployed to Kubernetes in a manner similar to kubectl apply.
> /var/lib/rancher/k3s/server/manifests にファイルを配置することで、自動的にKubernetesにデプロイされます(kubectl applyのように)

### 5-1. 使用するリソース
今回はK3sを動かすことにフォーカスしているので、そんなに凝ったものはデプロイしません。
nginxが動くpod(deploymentから作成します)、pod接続用のservice、serivceの外部公開ロードバランサのingressで構成しています。
serviceは8088番ポートでトラフィックを受け、podの80番ポートへトラフィックを流します。
![構成図 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429161/8814b4fb-83b2-8a38-ceb5-0e222261a12f.png)


> Loadbalancerタイプのserviceを作成すればingressは無しでもOKです

今回使用したymlは[github](https://github.com/otsukousan/sample-k3s-resource)に公開しておいたので、K3sを試したい方は使ってください。

```shell_session
root@k3s-master:~# git clone https://github.com/otsukousan/sample-k3s-resource.git
```

### 5-2. deployment

```shell_session
root@k3s-master:~# cp deployment.yml /var/lib/rancher/k3s/server/manifests/deployment.yml
（十数秒後）
root@k3s-master:~# k3s kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
nginx-7dc45fbd74-dfjg6   1/1     Running   0          23s
nginx-7dc45fbd74-lc5q7   1/1     Running   0          23s
nginx-7dc45fbd74-bjgxq   1/1     Running   0          23s
root@k3s-master:~# k3s kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE    IP           NODE      NOMINATED NODE   READINESS GATES
nginx-7dc45fbd74-dfjg6   1/1     Running   0          31s   10.42.2.9    k3s-wk2   <none>           <none>
nginx-7dc45fbd74-lc5q7   1/1     Running   0          31s   10.42.2.8    k3s-wk2   <none>           <none>
nginx-7dc45fbd74-bjgxq   1/1     Running   0          31s   10.42.1.11   k3s-wk1   <none>           <none>
```

きちんと各ノードに分散配置されていますね。
試しにpodの中から自身のnginxに向かってcurlを打ってみました。

```shell_session
root@k3s-master:~# k3s kubectl exec -it nginx-7dc45fbd74-dfjg6 -- curl http://localhost
<!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>
root@k3s-master:~#
```

> いつものやつ🤗

### 5-3. service 
serviceもdeploymentと同様に自動デプロイ機能を使ってデプロイします。

```shell_session
root@k3s-master:~# cp service.yml /var/lib/rancher/k3s/server/manifests/service.yml
（数秒後）
root@k3s-master:~# k3s kubectl get svc
NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
kubernetes      ClusterIP   10.43.0.1      <none>        443/TCP    62m
nginx-service   ClusterIP   10.43.74.131   <none>        80/TCP      6s
root@k3s-master:~#
```

### 5-4. ingress
ingressも同様です。

```shell_session
root@k3s-master:~# cp ingress.yml /var/lib/rancher/k3s/server/manifests/ingress.yml
（数秒後）
root@k3s-master:~# k3s kubectl get ing
NAME            CLASS    HOSTS   ADDRESS                                     PORTS   AGE
nginx-ingress   <none>   *       192.168.1.150,192.168.1.151,192.168.1.152   80      12s
root@k3s-master:~#
```

ちなみにK3sはTraefikというingressコントローラが標準で入っているので、コントローラのデプロイなしで使用することができます。
> 逆にTrarfik以外のingressコントローラを使う場合は、マスターノードでのk3s起動時に指定が必要みたいです。
>
>```shell_session
> curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable=traefik" sh -
>```

## 結果確認
ローカルPCのブラウザからingressで公開したアドレスを入力するとnginxのトップページが表示されました。
![result2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429161/fb4fa270-81e1-e684-af73-03278b805eb3.png)

また、podのログからもアクセスが来ていることがわかります。

```shell_session
root@k3s-master:~# k3s kubectl logs nginx-7dc45fbd74-dfjg6
（中略）
10.42.0.7 - - [26/Jun/2021:10:30:28 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36" "10.42.0.1"
------------------------------------------------------------------------ 
root@k3s-master:~# k3s kubectl logs nginx-7dc45fbd74-lc5q7
（中略）
10.42.0.7 - - [26/Jun/2021:10:35:18 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36" "10.42.0.1"
------------------------------------------------------------------------ 
root@k3s-master:~# k3s kubectl logs nginx-7dc45fbd74-bjgxq
（中略）
10.42.0.7 - - [26/Jun/2021:10:30:31 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36" "10.42.0.1"
```

お疲れさまでした！！！

# 感想

マネージドサービスを使わずにKubernetesクラスタを組んだのは初めてでしたが、思ったよりもスムーズに進められました。
せっかく軽量なKubernetesなので、今度はシングルボードコンピュータでk3sクラスタを組んで遊ぼうと思います。

# 参考
[日本語版k3s マニュアルダウンロード - 株式会社スタイルズ](https://www.stylez.co.jp/rancher-k3s-pdf/)
[k3s でクラスタ (Master 1台/Worker 2台) を構築してみた - SIOS TECH.LAB ](https://tech-lab.sios.jp/archives/15620)