苦学生のためのk8sクラスタ構築方法【月々1000円】

この記事は [WESEEK Advent Calendar](https://qiita.com/advent-calendar/2020/weseek) 2020 18日目の記事です。

こんにちは！ [WESEEK, Inc.](https://weseek.co.jp/) で長期インターンをさせていただいております [yamagai] (https://twitter.com/yamagai_0919)と申します。

今回は、
「Kubernetesクラスタを構築したいものの、全てのクラウドサービスの無料クレジットを使い切ってしまった。。。」
「とはいえ GKE や EKS などのクラスタ運用に払えるほどお金はない。。。」

という苦学生のために、出来るだけお安くクラスタを構築する方法を書いてみようと思います！

(2021/01/10 追記) 「RKE2 のインストール」のインストールの章に 　`2. service から起動する方法` を加えました。


# VPSを立てよう
安さとスペックのバランスを考えると結局VPSに辿り着きました。

今回はそのVPSの中でも圧倒的に安いと言われる [TIME4VPS](https://www.time4vps.com/) を使って構築していきます。

圧倒的な安さの TIME4VPS ですが、実は欠点があって、データセンターがリトアニアにあるため

```jsx
$ ping 80.209.234.201
PING 80.209.234.201 (80.209.234.201): 56 data bytes
64 bytes from 80.209.234.201: icmp_seq=0 ttl=51 time=262.969 ms
64 bytes from 80.209.234.201: icmp_seq=1 ttl=51 time=283.641 ms
64 bytes from 80.209.234.201: icmp_seq=2 ttl=51 time=303.660 ms
```
RTT が 300ms 程度とだいぶ長めで、ターミナルの反応速度がちょっと遅いです。
(日本 - アメリカ西海岸だと 100ms 程度)

さて、スペックについてですが、クラスタ構築に使用するツールにもよりますが今回は以下のようにしました。

Linux VPS - Linux 4×2台(master1台, worker1台)
- OS: **Ubuntu 20.04 (64-bit)**
- Processor: **1 x 2.6 GHz**
- Memory: **4096 MB**
- Hard disk: **40 GB**
- Bandwidth: **1000 Mbps (Monthly limit: 4 TB)**

k3s や k0s など軽量なものを使う場合は Linux 2 で十分だと思います。(この場合、月々1000円です！！)


# クラスタを構築しよう

クラスタを構築するのにどのツールを使うか迷ったのですが、今回は Rancher社の [RKE2](https://github.com/rancher/rke2) を使ってみようと思います。

公式によると、RKE2 は k3s と RKE の良いとこ取りらしいです。
意図せず世間をざわつかせた kubernetes 1.20 の docker非推奨問題についても、[こちら](https://forums.rancher.com/t/docker-support-deprecated/18959)にある通り問題無いようです。

ちなみに、kubernetes 1.20 の docker非推奨問題については、以下の記事がとてもわかりやすかったです。

- https://blog.inductor.me/entry/2020/12/03/061329
- https://jaco.udcp.info/entry/2020/12/03/172843


## RKE2 のインストール
公式ドキュメント → https://docs.rke2.io/

### 事前準備
RKE を使うと、OS の設定や docker のインストールなどの事前準備が必要だったのですが、RKE2 では firewall の設定以外は不要でした。

それでは、firewall の設定をしていきましょう。
以下のポートを ufw を使って開けていくだけです。 master と worker 両方で行ってください！
![スクリーンショット 2020-12-18 21.05.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640444/c5b3d1e5-ce8e-7a74-fab0-eb1d72a9c0d8.png)
(引用: https://docs.rke2.io/install/requirements/#networking)

ufw を有効化

```jsx
$ sudo ufw enable
```

ポートを開けていく

```jsx
$ sudo ufw allow 9345/tcp
```

これだけで事前準備は完了です！

ちなみに 22/tcp を開け忘れてログアウトすると ssh が出来なくなってしまうので忘れずに開けましょう。
もしやってしまった時は[こちらの記事](https://qiita.com/yamagai/items/3c4b7b1727efb7bf1534)を参考にしてください！


### インストール
基本的にドキュメント通りです。
そういえば、RKE では(というか k3s, k0s でも) masterノードのことを `server`、workerノードのことを `agent` と呼ぶので適宜読み替えてください。

1. 直接 rke2 コマンドを叩く方法
2. service から起動する方法(←オススメ)

- 準備(master, worker共に)

まずは `curl -sfL https://get.rke2.io | sh -` で rke2 コマンドを打てるようにしましょう

#### 1. 直接 rke2 コマンドを叩く方法(この次の「2.service から起動する方法」での構築をお勧めします)
##### master ノード起動

公式には、`rke2 server` で server として起動させると書いていますが、こうするとシェルからログアウトした際にプロセスが kill されてしまい正常に動きません。

ですので、バックグラウンドかつログアウトしてもプロセスがkillされないように `nohup` と `&` を使って

```jsx
$ nohup rke2 server &
``` 
としましょう。

自分は、起動時のログがみたかったので

```jsx
$ nohup rke2 server > rke2-server-log.txt &
```

で別ファイルに書き出して、別タブで `tail -f` してログを見ていました。


起動時のログを見ていると etcd や api-server のログで以下のエラーが出ましたが、
これは起動を待ってるだけで後から両方とも`etcd data store connection OK`や`Kube API server is now running`と出て正常動作するのでご安心ください。

```jsx
ERRO[0035] Failed to check local etcd status for learner management: context deadline exceeded
{"level":"warn","ts":"2020-12-13T08:39:05.726+0200","caller":"clientv3/retry_interceptor.go:62","msg":"retrying of unary invoker failed","target":"passthrough:///https://127.0.0.1:2379","attempt":0,"error":"rpc error: code = DeadlineExceeded desc = latest balancer error: connection error: desc = \"transport: Error while dialing dial tcp 127.0.0.1:2379: connect: connection refused\""}
```

また、起動時のログ内に、以下のようにクラスタに agent を追加する際に必要な token の場所、 agent登録時のコマンド、kubeconfig の場所が載ってるので注意してください。

```jsx
INFO[0000] Node token is available at /var/lib/rancher/rke2/server/token
INFO[0000] To join node to cluster: rke2 agent -s https://(serverノードのIP):9345 -t ${NODE_TOKEN}
INFO[0000] Wrote kubeconfig /etc/rancher/rke2/rke2.yaml
```

##### worker ノードをクラスタに追加

まずは`/var/lib/rancher/rke2/server/token`に置いてある`NODE_TOKEN`をコピーしましょう。

次にノードを agent としてクラスタに参加させます。
その際、上記のログでは`rke2 agent -s https://(serverノードのIP):9345 -t ${NODE_TOKEN}` で worker として起動させると書いていますが、
これも master 側と同じくバックグラウンドかつログアウトしてもプロセスがkillされないように `nohup` と `&` を使って

```jsx
$ nohup rke2 agent -s https://(serverノードのIP):9345 -t ${NODE_TOKEN} &
```

としましょう。

#### 2. service から起動する方法
後になって気付いたのですが、rke2 のインストール(`curl -sfL https://get.rke2.io | sh -`)により service に `rke2-server` と `rke2-agent` が登録されます。

これを使って起動していきましょう。

##### master ノード起動

`rke2-server`サービスの自動起動を有効化

```jsx
$ systemctl enable rke2-server.service
```

`rke2-server`サービスを起動

```jsx
$ systemctl start rke2-server.service
```

`rke2-server`サービスのログを追うなら

```jsx
$ journalctl -u rke2-server -f
```


##### worker ノードをクラスタに追加
`rke2-agent`サービスの自動起動を有効化

```jsx
$ systemctl enable rke2-agent.service
```

server ノードを識別するための`rke2-agent`サービスの設定

```jsx
# 設定用のファイルを作成
$ mkdir -p /etc/rancher/rke2/
$ vim /etc/rancher/rke2/config.yaml

# 作成した config.yaml に設定を記入
server: https://(serverノードのIP):9345
token: (NODE_TOKEN)
```


`rke2-agent`サービスを起動

```jsx
$ systemctl start rke2-agent.service
```

`rke2-agent`サービスのログを追うなら

```jsx
$ journalctl -u rke2-agent -f
```


これだけでクラスタは完成です！！！簡単ですね！！

#### kubectl を叩こう

`/etc/rancher/rke2/rke2.yaml` を見て, `~/.kube/config` に追記しましょう。
クラスタの名前は密林をイメージして`jukai`にしました。ダサくてごめんなさい。


```jsx
$ kubectl get node
NAME                    STATUS   ROLES         AGE     VERSION
23qa.l.time4vps.cloud   Ready    <none>        4d21h   v1.18.12+rke2r1
23qb.l.time4vps.cloud   Ready    etcd,master   4d21h   v1.18.12+rke2r1
```

無事、クラスタとして動いていますね。感無量です。

## アプリを立ててみる

### アプリの helm chart をデプロイしてみる

[こちらの記事](https://qiita.com/yamagai/items/4344875ef479a664f9ff)で GitOps を導入している対象の`alcatraz`という自作アプリをデプロイしてみましょう。

使用するマニフェストリポジトリは以下です。Helm で管理しています。

- https://github.com/bamboooo-dev/alcatraz-manifests

さて、デプロイですが、RKE2 では helm を使用する際も特に設定は不要です。
カスタマイズする時はこちら→ [https://docs.rke2.io/helm/](https://docs.rke2.io/helm/)

今回使う helm のバージョンは以下の通り。

```jsx
(⎈ |jukai:alcatraz)❯ helm version
version.BuildInfo{Version:"v3.3.4", GitCommit:"a61ce5633af99708171414353ed49547cf05013d", GitTreeState:"clean", GoVersion:"go1.14.9"}
```


**DB デプロイ**

```jsx
~/bamboooo/alcatraz-manifests main
(⎈ |jukai:alcatraz)❯ helm install alcatraz-db ./alcatraz-db
```

**API デプロイ**

```jsx
~/bamboooo/alcatraz-manifests main
(⎈ |jukai:alcatraz)❯ helm install alcatraz-api ./alcatraz-api
```

**UI デプロイ**

```jsx
~/bamboooo/alcatraz-manifests main
(⎈ |jukai:alcatraz)❯ helm install alcatraz-ui ./alcatraz-ui
```

**ingress 設定**

RKE2 では `nginx-ingress` が kube-system namespace に `rke2-nginx-ingress` という名前でデフォルトで入っており、使い方は `nginx-ingress` と同じになります。

詳しくは [公式](https://docs.rke2.io/networking/#nginx-ingress-controller) を参照してください。

```jsx
~/bamboooo/alcatraz-manifests main
(⎈ |jukai:alcatraz)❯ kubectl apply -f ingress.yaml
```

IP がふられたか確認してみましょう。

```jsx
(⎈ |jukai:alcatraz)❯ kubectl get ingress
NAME   CLASS    HOSTS   ADDRESS          PORTS   AGE
ui     <none>   *       80.209.234.201   80      4d21h
```

おお〜いけてそう

### ブラウザで確認

可愛いたこ焼きが跳ねてますね。成功です！！！
![スクリーンショット 2020-12-18 21.52.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640444/5952ca0d-d68a-078a-a94b-77c25f8d6d59.png)

# 最後に
無事に TIME4VPS と RKE2 で簡単でお安くクラスタ構築出来ました！

「金かかんのかよ！」と思ったそこの苦学生たち、近々【完全無料】編も書くから待っておきなさい...


# 参考リンク
- RKE2公式
    - https://docs.rke2.io/
- kubernetes 1.20 の docker非推奨問題について
    - https://blog.inductor.me/entry/2020/12/03/061329
    - https://jaco.udcp.info/entry/2020/12/03/172843

- Linuxコマンド(Bash)でバックグラウンド実行する方法のまとめメモ
    - https://qiita.com/inosy22/items/341cfc589494b8211844


※この記事は [WESEEK Tips wiki](https://tips.weseek.co.jp/) に 2020/12/18 に投稿された記事の転載です。
Tips wiki では、IT企業の技術的な情報やプロジェクトの情報を公開可能な範囲で公開しています。