在 RaspberryPi 上建一個比較健康的 k8s cluster

因為最近我購入了一台 Mikrotik hAP ac2 當作 router,我的 RaspberryPi 就從 Wireless AP 退役了。 正好一直想來架設一個 Nextcloud 來當作雲端備份,不過 Ubuntu 的 Nextcloud 是用 snap 安裝,非常難以維護,而且 snap 也是一個不怎麼自由的東東,是個非常不健康的玩意。所以就只好來用 container 了。

本文的作法大量參考了 xdavidwu 的文章:建一個比較健康的 k8s

因為最近我購入了一台 Mikrotik hAP ac2 當作 router,我的 RaspberryPi 就從 Wireless AP 退役了。

正好一直想來架設一個 Nextcloud 來當作雲端備份,不過 Ubuntu 的 Nextcloud 是用 snap 安裝,非常難以維護,而且 snap 也是一個不怎麼自由的東東,是個非常不健康的玩意。所以就只好來用 container 了。

目標

  • CRI-O + crun
  • 啟用 user namespace
  • Container 的 storage 要在外接硬碟上
  • Single node cluster 就好

環境

  • Raspberry Pi 4 Model B
  • Ubuntu 20.04
  • 網路:192.168.0.2/24

基本安裝

在這裡會需要安裝 kubelet, kubeadm, cri-o, crun,需要拉 Google & CRI-O 的 Apt source 跟 key

deb https://packages.cloud.google.com/apt kubernetes-xenial main

deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_20.04/ /
deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/1.20/xUbuntu_20.04/ /

詳細步驟就請跟官網走

CRI-O 設定

這邊大部分都跟 xdavidwu 的設定差不多,差別是我沒像他一樣 drop capabilities 就是,有需要的再自行服用


default_runtime = "crun"

...

apparmor_profile = "crio-default"

...

# uid 跟 gid 的 mapping 可以自行調整
uid_mappings = "0:100000:65536"
gid_mappings = "0:100000:65536"

...

# 記得要把crun的註解拿掉
[crio.runtime.runtimes.crun]
/etc/crio/crio.conf
remap-uids = "0:100000:65536"
remap-gids = "0:100000:65536"
/etc/containers/storage.conf

除了這個 configuration 之外,要記得刪除 `etc/crio/crio.conf.d/01-crio-runc.conf
不然待會兒 CRI-O 啟動時會抱怨 crio-runc 沒安裝

設定完之後,先不要啟動 CRI-O

目錄權限

為了方便等會兒設定,這邊可以先建立一個使用者對到裡面的 root

sudo useradd -u 100000 -g 100000 -M -s /usr/sbin/nologin kubernetes

接著要建立一些目錄並設定權限:

sudo mkdir /var/lib/kubelet
sudo chown root:kubernetes /var/lib/kubelet
sudo chmod g+rx /var/lib/kubelet

sudo mkdir /var/lib/kubelet/pods
sudo chown root:kubernetes /var/lib/kubelet/pods -R
sudo chmod g+s /var/lib/kubelet/pods

sudo mkdir /var/lib/etcd
sudo chown kubernetes:kubernetes -R /var/lib/etcd

/var/lib/containers 是 CRI-O 存放 images 的地方,mkdir 之後設定 automount 把它掛載到外接硬碟上,之後再啟動 CRI-O 的 service

設定 CNI 網路

這邊需要依照各位的網路設定去調整,我會開一個新的 bridge 並跟 192.168.0.0/24 網段切開,所以會需要開啟 masquerade

{
    "cniVersion": "0.3.1",
    "name": "crio",
    "type": "bridge",
    "bridge": "cni0",
    "isDefaultGateway": true,
    "ipMasq": true,
    "hairpinMode": true,
    "ipam": {
        "type": "host-local",
        "routes": [
            { "dst": "0.0.0.0/0" }
        ],
        "ranges": [
            [{ "subnet": "10.85.0.0/16" }]
        ]
    }
}
/etc/cni/net.d/100-crio-bridge.conf

啟用 memory cgroup

Raspberry Pi 上的 Ubuntu 預設是沒有 memory cgroup 的,需要手動開啟

修改 /boot/firmware/nobtcmd.txt ,在裡面加入以下 kernel cmdline:

cgroup_enable=memory cgroup_memory=1

修改完後需要重新開機!

kubeadm 設定檔

在這裡先寫好設定檔,方便等一下不用打一長串指令

apiVersion: kubeadm.k8s.io/v1beta2
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: 192.168.0.2
  bindPort: 6443
nodeRegistration:
  criSocket: "unix:///var/run/crio/crio.sock"
  name: rpi4
---
apiVersion: kubeadm.k8s.io/v1beta2
apiServer:
  certSANs:
  - "<other domains here>"
  - "192.168.0.2"
  - "10.85.0.1"
  extraArgs:
    authorization-mode: Node,RBAC
  timeoutForControlPlane: 5m0s
imageRepository: k8s.gcr.io
kind: ClusterConfiguration
kubernetesVersion: v1.20.4
networking:
  dnsDomain: cluster.local
  serviceSubnet: 10.96.0.0/12
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
kubeadm.yml

部屬 kubelet

用剛才的設定檔讓 kubeadm 幫我們初始化 kubelet

sudo kubeadm init --config kubeadm.yml

修正 kube-proxy

由於現在 container 內不具有 kube-proxy 所需要的權限了,因此需要把它拉出來,直接在外面執行。

因此需要把以下東西拉到外面執行:

  • 程式本體
  • 設定檔
  • 憑證
  • Token

程式可以考慮從 /var/lib/containers/storage 中找到對應的 CRI-O container 目錄,或是從 Image 中解出來。

然後在 /var/lib/kubelet/pods 裡會有 pod 用的 configmap & secret,一樣從裡面複製出設定檔、憑證、Token,全部放到 /var/lib/kube-proxy 目錄中。記得權限要弄好。

總共應該要有5個檔案

  • config.conf
  • kubeconfig.conf
  • ca.crt
  • token
  • namespace

最後要把 kubeconfig.conf 裡面的憑證及 token 路徑修正,設定就完成了。

Systemd 設定檔

寫一個 Systemd 的 Service 讓它可以自動啟動:

[Unit]
Description=kube-proxy
Wants=network-online.target
Before=multi-user.target

[Service]
Type=simple
ExecStart=/usr/local/bin/kube-proxy --config=/var/lib/kube-proxy/config.conf

[Install]
WantedBy=multi-user.target

start & enable 測試一下,看該有的 iptables 有沒有長出來

最後把裡面的 daemonset 拔掉就大功告成了

kubectl -n kube-system delete daemonset kube-proxy

Cluster 設定

若要串多個 node 的方式就一模一樣了。

若是 single node 的話要記得把 noderole 拔掉,不然 master 是不會跑 worker 的工作的。

kubectl taint nodes --all node-role.kubernetes.io/master-