Raspberry Pi 4にKubernetesを入れる

こちらの記事にしたがってK8sをRaspberry Piに入れようとしていたのですが、一部情報が古くなっているのでメモを残しておきます。

https://qiita.com/kentarok/items/6e818c2e6cf66c55f19a

とくにDockerは今は非推奨となっており、cri runtimeとしてcrioを使うのが普通だそうです。[こちら](https://blog.uyorum.net/post/k8s-on-rpi4/)が非常に参考になりました、ありがとうございます。

## 前提

- Raspberry Pi ImagerでOSが書き込めたと仮定して進めます。
- M1 Macbook Airで動作確認しております。

以下はMaster nodeだけをとりあえず対象にして行っています。

## IPの固定

```
sudo raspi-config nonint do_hostname raspi001
sudo raspi-config nonint do_change_timezone Asia/Tokyo
sudo raspi-config  --expand-rootfs

sudo apt-get update && sudo apt-get dist-upgrade -y
sudo apt-get update && sudo apt-get upgrade -y

cat <<EOF >> /etc/dhcpcd.conf
static ip_address=192.168.13.101/24
static routers=192.168.13.1
static domain_name_servers=192.168.13.1 8.8.8.8
EOF


sudo reboot
```

## cgroups周りの設定

```
sudo sed -i 's/$/ cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory/g' /boot/cmdline.txt
sudo reboot
```

## スワップの無効化

```
sudo swapoff --all
sudo systemctl stop dphys-swapfile
sudo systemctl disable dphys-swapfile
systemctl status dphys-swapfile
```

## iptables周りの設定

```
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

sudo apt-get install -y iptables arptables ebtables
sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
sudo update-alternatives --set arptables /usr/sbin/arptables-legacy
sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy

sudo sysctl --system
```

## crioのインストール

まずはcrioの設定です。

```
cat <<EOF | sudo tee /etc/modules-load.d/crio.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables  = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sudo sysctl --system
```

インストールは以下のようになります。

```
OS=Raspbian_10
VERSION=1.20
cat <<EOF | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/ /
EOF
cat <<EOF | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:$VERSION.list
deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$VERSION/$OS/ /
EOF
curl -L https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:$VERSION/$OS/Release.key | sudo apt-key --keyring /etc/apt/trusted.gpg.d/libcontainers.gpg add -
curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/Release.key | sudo apt-key --keyring /etc/apt/trusted.gpg.d/libcontainers.gpg add -
sudo apt update
sudo apt install -y cri-o cri-o-runc
sudo systemctl daemon-reload
sudo systemctl enable crio
sudo systemctl start crio
```

## k8s install

```
sudo apt-get update && sudo apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
cat <<EOF | sudo tee /etc/default/kubelet
KUBELET_EXTRA_ARGS=--container-runtime-endpoint='unix:///var/run/crio/crio.sock'
EOF
```

## reloadする

```
systemctl daemon-reload
systemctl restart kubelet
```

## kubeadmでクラスター初期化

```
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
...
Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.13.101:6443 --token 9zztqc.nlw3opu0n21vx31j \
	--discovery-token-ca-cert-hash sha256:45da3c0977e3108401591ff2ed23a89180a485f1ad14b7e7fd5ac8bafd82b945
```

コマンドで確認してみましょう。

```
pi@raspi001:~ $ mkdir -p $HOME/.kube
pi@raspi001:~ $ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
pi@raspi001:~ $ sudo chown $(id -u):$(id -g) $HOME/.kube/config
pi@raspi001:~ $ kubectl get node
NAME       STATUS   ROLES           AGE   VERSION
raspi001   Ready    control-plane   18m   v1.24.1
```

## 最後に

Dockerが非推奨になっていたのを知らず、無駄な時間を溶かしてしまいました。
ここらへんは移り変わりが速いので、2年後にはまた別のランタイムやらを使っている気がします、、、

## おまけ：worker nodeの参加とnginxの起動

最初の名前設定とipアドレスだけ変えたコマンドが以下です。それ以外はクラスターにjoinするところまでいっしょです。

```
sudo raspi-config nonint do_hostname k8s-node1
sudo raspi-config nonint do_change_timezone Asia/Tokyo
sudo raspi-config  --expand-rootfs

sudo apt-get update && sudo apt-get dist-upgrade -y
sudo apt-get update && sudo apt-get upgrade -y

cat <<EOF >> /etc/dhcpcd.conf
static ip_address=192.168.13.102/24
static routers=192.168.13.1
static domain_name_servers=192.168.13.1 8.8.8.8
EOF


sudo reboot
```

joinするときは先ほどちらっと出てきたコマンドを叩きます。

```
kubeadm join 192.168.13.101:6443 --token 9zztqc.nlw3opu0n21vx31j \
	--discovery-token-ca-cert-hash sha256:45da3c0977e3108401591ff2ed23a89180a485f1ad14b7e7fd5ac8bafd82b945
[preflight] Running pre-flight checks
	[WARNING SystemVerification]: missing optional cgroups: hugetlb blkio
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
```

マスターノードで走っているのが確認できます。

```
$ kubectl get node
NAME        STATUS   ROLES           AGE     VERSION
k8s-node1   Ready    <none>          7m19s   v1.24.1
raspi001    Ready    control-plane   44m     v1.24.1
```

flannelもインストールしておきます。

```
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
podsecuritypolicy.policy/psp.flannel.unprivileged configured
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel unchanged
configmap/kube-flannel-cfg unchanged
daemonset.apps/kube-flannel-ds configured
```

以下のpodを作成してみました。

```sample-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: static-web
  labels:
    role: myrole
spec:
  containers:
    - name: web
      image: nginx
      ports:
        - name: web
          containerPort: 80
          protocol: TCP
```

リソースを作成します。

```
$ kubectl create -f sample-pod.yaml
```

以下のように作成されました。

```
$ kubectl get pods
NAME         READY   STATUS    RESTARTS   AGE
static-web   1/1     Running   0          50s
```