Raspberry pi 4におけるK3Sクラスタ構築

#はじめに
Raspberry piにk3sを構築するのは従来からあるが、Raspberry pi「4」に構築した記事はあまりなかったのでメモ。
よって新規性は「4」であることだけなのであまり記事としての価値は正直ないです。
当例ではRaspberry pi 4を3台用意し、Master1台、Node2台で構築します。
RaspbianのInstallは解説しません。ddコマンドやrufusを使ってMicro SDにOSを焼いてください。

##おやくそく
 - FirewallやSELinuxは無効にしてあります。各自セキュリティ対策を実行してください。
 - 当例ではすべてRoot権限を持ったアカウントで実行しています。適宜sudoする等してください。

##環境
　- Raspbery pi 4 3台(Master, Node, Node)
　- Rasbian OS (Debian 10.1)

#準備(全ノード)
##hostsファイル編集
[報告されているバグ][link-1]を回避するため、hostsファイルに全ノードを登録します。
[link-1]:https://github.com/rancher/k3s/issues/60

本環境では
Master: 192.168.1.221 pi4-master
Node1: 192.168.1.222 pi4-node1
Node2: 192.168.1.223 pi4-node2
としました。よってhostファイル編集後は

```
root@pi4-master:/home/pi# cat /etc/hosts
127.0.0.1       localhost
::1             localhost ip6-localhost ip6-loopback
ff02::1         ip6-allnodes
ff02::2         ip6-allrouters

192.168.1.221 pi4-master
192.168.1.222 pi4-node1
192.168.1.223 pi4-node2
```

となります。

##Cgroup有効化
参考 https://kenfdev.hateblo.jp/entry/2019/03/11/193034
当環境ではこれを行わないとのちのコンテナ作成においてContainer Creatingから先に進みませんでした。
/boot/cmdline.txtの**行末**に
`cgroup_enable=cpuset`
を追記します。**改行して追記ではありません。**
つまり編集後にcatすると

```
root@pi4-master:/home/pi# cat /boot/cmdline.txt
１：console=serial0,115200 console=tty1 root=PARTUUID=d9b3f436-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles
cgroup_memory=1 cgroup_enable=memory cgroup_enable=cpuset 
```
となります。(`１：`は行番号。筆者追記)

下記例のように**改行して追記ではありません**

```
#下記例は間違い
root@pi4-master:/home/pi# cat /boot/cmdline.txt
１：console=serial0,115200 console=tty1 root=PARTUUID=d9b3f436-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles
cgroup_memory=1 cgroup_enable=memory 
２：cgroup_enable=cpuset 
```
当環境では`cgroup_memory=1 cgroup_enable=memory`は存在したので追記しませんでしたが、
なかった場合は同様に1行目の行末に追記してください。

##Firewall, SELinux無効
執筆時点(2020/02/27)で最新のRaspbian利用時は下記コマンドでSwapを無効化しました。
バージョンによってコマンドが異なるようですので、古いバージョン利用時は[ここ][link-2]を参考に各自行ってください。
[link-2]:https://www.angelcurio.com/raspberrypi/?Raspberry%20Pi/%E5%90%84%E7%A8%AE%E8%A8%AD%E5%AE%9A/swap%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E5%BB%83%E6%AD%A2

```
systemctl stop firewalld
systemctl disable firewalld
systemctl stop dphys-swapfile
systemctl disable dphys-swapfile
```


#K3S Install(Master)
https://k3s.io/
むずかしいことはありません。公式通り。

```
curl -sfL https://get.k3s.io | sh -
```
正常に終了後、
`kubectl get pods --all-namespaces`を実行すると、Podが立ち上がっているのが確認できます。
※構築時にここのメモ取り忘れたので、構築完了時から編集して下記に載せています。存在するPod間違えているかもしれません。

```
root@pi4-master:/home/pi# kubectl get pods --all-namespaces
NAMESPACE     NAME                                      READY   STATUS      RESTARTS   AGE
kube-system   metrics-server-6d684c7b5-gw8bh            1/1     Running     1          24h
kube-system   coredns-d798c9dd-5rmc7                    1/1     Running     1          24h
kube-system   traefik-6787cddb4b-vnvcp                  1/1     Running     1          24h
```

同様に、Nodeも`kubeclt get nodes`で確認すると

```
root@pi4-master:/home/pi# kubectl get nodes
NAME         STATUS   ROLES    AGE   VERSION
pi4-master   Ready    master   24h   v1.17.2+k3s1
```
##Token取得
Masterの構築は上記でほぼ終わりですが、Node追加用にTokenを取得します。
Tokenは `/var/lib/rancher/k3s/server/node-token`にあります。

```
root@pi4-master:/home/pi# cat /var/lib/rancher/k3s/server/node-token
K1020893324feca9a5dd57f7b722dddfba8b777ebb212212426a1a234379508d::server:a53fba4b42707ee50aebfe325d6
```
上記の
`K1020893324feca9a5dd57f7b722dddfba8b777ebb212212426a1a234379508d::server:a53fba4b42707ee50aebfe325d6`
をメモしておきましょう。

#K3S Install(Node2台)
Node2台とも同じコマンドを実行します。
最初のインストールはMasterと同様です。


```
curl -sfL https://get.k3s.io | sh -
```
当方のやり方が悪いのか、出回っている方法だとNode追加できなかったので下記方法で期待する動作を実現しました。

##k3s無効化

このままだとMasterとして動作してしまうので一通り停止と無効化します。

```
#k3s stop
sh /usr/local/bin/k3s-killall.sh

#k3s disable
systemctl stop k3s
systemctl disable k3s
```

##k3s agentサービス登録・有効化

k3s-agnetをサービスとして登録してsystemdで管理している先人が[ここ][link-3]にいらしたので真似します。
[link-3]:https://sky-joker.tech/2019/05/04/%E3%83%A9%E3%82%B9%E3%82%99%E3%83%98%E3%82%99%E3%83%AA%E3%83%BC%E3%83%8F%E3%82%9A%E3%82%A4%E3%81%A6%E3%82%99k3s%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%BF/


```

cat <<EOF > /etc/systemd/system/k3s-agent.service
[Unit]
Description=Lightweight Kubernetes Agent
Documentation=https://k3s.io
After=network-online.target
 
[Service]
Type=simple
EnvironmentFile=/etc/sysconfig/k3s-agent
ExecStartPre=-/sbin/modprobe br_netfilter
ExecStartPre=-/sbin/modprobe nf_conntrack
ExecStartPre=-/sbin/modprobe overlay
ExecStartPre=-/sbin/modprobe vxlan
ExecStart=/usr/local/bin/k3s agent --server \$SERVER --token \$TOKEN
Delegate=yes
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
TimeoutStartSec=0


[Install]
WantedBy=multi-user.target

EOF
```
次に上記にあるEnvironmentFileを作成し、Tokenを読み込むようにします。

SERVERとTOKENは各自のServer機のIPとMaster機のTokenに置き換えてください。

```
#ディレクトリ作成(ない場合) 
mkdir /etc/sysconfig/

cat <<EOF > /etc/sysconfig/k3s-agent
SERVER=https://192.168.1.221:6443
TOKEN=K1020893324feca9a5dd57f7b722dddfba8b777ebb212212426a1a234379508d::server:a53fba4b42707ee50aebfe325d6

EOF

/* 2020/07/16 上記修正しました(sysco「ｍ」fig)
# 誤 cat <<EOF > /etc/syscomfig/k3s-agent
# 正 cat <<EOF > /etc/sysconfig/k3s-agent
*/
```

サービスを読み込み、有効化と起動をします。

```
systemctl daemon-reload
systemctl start k3s-agent
systemctl status k3s-agent
systemctl enable k3s-agent
```

#動作確認(Master)
この時点でMasterからNode1,2がWorkerとして認識されているはずです。

```
kubectl get nodes
NAME         STATUS   ROLES    AGE   VERSION
pi4-node1    Ready    <none>   24h   v1.17.2+k3s1
pi4-node2    Ready    <none>   24h   v1.17.2+k3s1
pi4-master   Ready    master   24h   v1.17.2+k3s1
``` 
node1,2が増えています。ReadyになっていればOKです。

せっかくなのでRoleも追加します。

###Role追加

```
kubectl label node pi4-node1 node-role.kubernetes.io/worker=worker
kubectl label node pi4-node2 node-role.kubernetes.io/worker=worker
```
すると、ROLESの欄がWorkerになります。

```
kubectl get nodes
NAME         STATUS   ROLES    AGE   VERSION
pi4-node1    Ready    worker   24h   v1.17.2+k3s1
pi4-node2    Ready    worker   24h   v1.17.2+k3s1
pi4-master   Ready    master   24h   v1.17.2+k3s1
``` 
なお、ラベルでの管理については触れません。

##nginx deploy
[すばらしいYaml][link-4]があったのでお借りします。
[link-4]:https://tech-lab.sios.jp/archives/15620

```
cat <<EOF > nginx.yaml                                             
apiVersion: v1
kind: Service
metadata:
  name: nginx-test-service
  labels:
    app: nginx-test
spec:
  selector:
    app: nginx-test
  type: NodePort
  ports:
  - protocol: TCP 
    port: 80
    targetPort: 80
    nodePort: 30080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-test-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-test
  template:
    metadata:
      labels:
        app: nginx-test
    spec:
      containers:
      - name: nginx-container
        image: nginx:1.15.9
        ports:
        - containerPort: 80
        lifecycle:
          postStart:
            exec:
              command:
                - /bin/sh
                - -c
                - echo "NGINX container!" > /usr/share/nginx/html/index.html && echo "Pod name is \$(hostname) ." >> usr/share/nginx/html/index.html
EOF
```
これをDeployします。

```
#deploy
kubectl apply -f nginx.yaml

#確認。すべてRunningになればOK
kubectl get pods
#NAME                                     READY   STATUS    RESTARTS   AGE
#nginx-test-deployment-745b8d55dc-q2qn2   1/1     Running   0          22h
#nginx-test-deployment-745b8d55dc-tdn8w   1/1     Running   0          22h
#nginx-test-deployment-745b8d55dc-zpz84   1/1     Running   0          22h

#access(IP addressはMasterでもNodeでもOK)
curl -XGET http://192.168.1.221:30080/
<!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>
```
[先程参考にしたページ][link-4]ではPod名だけ返ってくるはずが、なぜかHTML全部返ってきました。
動作の確認はできたので詳しくは追求しません。バージョンの問題かもしれません。
わかった方教えてください。

#おわりに
先人の知恵だけで一通り構築ができました。
特段Raspberry pi 4だからといって違いはないです(当たりまえ)
一式3万円くらいで揃えられるので、オンプレK8s(K3s)練習環境としては最適ではないでしょうか。![IMG_20190927_003955.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356296/0fc38de9-7c87-6e92-5b25-109fa70ad57c.jpeg)
(Raspberry pi 3の写真しかなかった・・・)


#参考文献
[1] Rancher公式 https://k3s.io/
[2] 既存バグ回避 https://github.com/rancher/k3s/issues/60
[3] k3sをラズパイで起動するまでにやったこと https://kenfdev.hateblo.jp/entry/2019/03/11/193034
[4]ラズベリーパイでK3Sクラスタを作ってみた https://sky-joker.tech/2019/05/04/%E3%83%A9%E3%82%B9%E3%82%99%E3%83%98%E3%82%99%E3%83%AA%E3%83%BC%E3%83%8F%E3%82%9A%E3%82%A4%E3%81%A6%E3%82%99k3s%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%BF/
[5] SIOS k3s でクラスタ (Master 1台/Worker 2台) を構築してみた https://tech-lab.sios.jp/archives/15620