Raspberry PI で Kubernetes クラスタを構築しました。
会社の「テックサポート制度」により、 Raspberry PI がサポート対象となり、遺憾無くお金を使わせてもらうことができる様になりました。 その甲斐あって、兼ねてより家で k8s cluster を飼う、というエンジニア冥利に尽きる所作を味わいたくチャレンジさせていただきました。
普段は AWS で Fargate を利用するケースが多く、 kubernetes を利用するシーンがなく、興味もあり、学びを広げたい意図があります。
目的 Raspberry PI のようなベアメタル環境で OS をインストールし k8s 関連パッケージを構築し、構築に必要な大まかな流れを理解する、 です。
以下を実施していきます。
Raspberry PI OS インストール
kubernetes cluster 構築
Metal LB 構築
非常に学びとハマりポイントが多かったので、以下に記していきたいと思います。
購入したものリスト 2022 年 4 月下旬、Raspberry PI 単体でなく、スターターキットでの取り扱いが多かったです。 スターターキットは単体に比べやや高くなりますが、その辺は会社 のサポート制度の力を存分にお借りしました ♪
Raspberry Pi Imager で OS 書き込み
2022-04-26 時点で最新の Raspberry PI OS Lite (32-bit) Bullseye を選択しました。 今回の要件に GUI は不要で極力軽めのイメージを利用したかった為です。
設定でホスト名や Wifi の設定をしておくと後が楽です。
SD カードにイメージを書き込みし、 Raspberry PI に差し込み、起動します。
cgroup の有効化 Docker を利用すべく、 cgroup を有効化します。
1 2 3 $ sudo nano /boot/cmdline.txt cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1
nano
or vi
が入ってたのですが、 vim
使いたい場合はこちら 参考
cgroup への理解は以下参考になりました。
swap 無効化 1 2 3 $ sudo swapoff --all $ sudo systemctl stop dphys-swapfile $ sudo systemctl disable dphys-swapfile
swap を無効化する理由は公式ドキュメントで言及されています。
https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/_print/#始める前に
Swap がオフであること。kubelet が正常に動作するためには swap は必ずオフでなければなりません。
IP 固定 ルーターで IP 固定しておくと再起動時に変更なく楽です。
ルーターでなく、 Raspberry PI 側で /etc/dhcpcd.conf
を編集し固定する方法は以下参考になります。Raspberry Pi の IP アドレスを固定にするには?
ここで一旦 reboot し諸々を反映しておきます。
Docker インストール CRI のインストール を参考にインストールしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // パッケージのリポジトリ情報更新時に必要な公開鍵を取得。ないと GPG error が発生 $ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg // armhf debian 用の docker 安定版のリポジトリを登録 $ echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list// リポジトリ更新 $ sudo apt-get update // docker インストール $ sudo apt-get -y install docker-ce docker-ce-cli containerd.io $ sudo systemctl enable docker // pi ユーザを docker グループに追加し docker を操作できる様にする $ sudo usermod pi -aG docker
Cgroup Driver が systemd を使用するように設定されていることを確認 1 2 3 4 $ sudo docker info | grep Cgroup Cgroup Driver: systemd Cgroup Version: 2
以下公式 で systemd を使用を推奨しています。
コンテナランタイムと kubelet が cgroup ドライバーとして systemd を使用するように設定を変更することでシステムは安定します。 以下の Docker 設定の native.cgroupdriver=systemd オプションに注意してください。
kubeadm インストール 公式 に沿って以下実行していきます。
iptables がブリッジを通過するトラフィックを処理できるようにする 公式ドキュメント参考
br_netfilter
がロードされているか確認する
1 2 3 4 5 $ lsmod | grep br_netfilter br_netfilter 32768 0 bridge 180224 1 br_netfilter ipv6 520192 28 br_netfilter,bridge
何も表示されない場合、 br_netfilter
がロードされていない為、以下実行し明示的にロードしておきます。
1 2 3 4 5 $ 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 sysctl --system
iptables が nftables バックエンドを使用しないようにする 公式
nftables バックエンドは現在の kubeadm パッケージと互換性がありません。(ファイアウォールルールが重複し、kube-proxy を破壊するためです。)
公式の説明にある通り、 iptables が nftables を使うことで kubernetes が正常に動作しないことがある為、 iptables をレガシーバージョンに切り替えます。
1 2 3 4 5 6 7 8 // レガシーバイナリがインストールされていることを確認してください $ 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
参考: nftables 入門
kubeadm、kubelet、kubectl のインストール いよいよ kubeadm インストールです。
公式
2022/04/30 時点の最新バージョン 1.23.6 では kubelet が起動失敗するエラーが発生した為、 バージョンは 1.22 系を選択します。
1 2 3 4 5 6 7 8 9 10 11 12 $ 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-get update // 1.22 系をインストール $ sudo apt-get install -y kubelet=1.22.7-00 kubeadm=1.22.7-00 kubectl=1.22.7-00 // バージョン固定 $ sudo apt-mark hold kubelet kubeadm kubectl
Kubernetes クラスター構築 公式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 // flannel をクラスター初期化処理を実装すべく 10.244.0.0/16 を指定している // see: https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml $ sudo kubeadm init --pod-network-cidr=10.244.0.0/16 ... // 最後の1行をコピーしておく kubeadm join <master node ip>:6443 --token yyyy \ --discovery-token-ca-cert-hash sha256:xxxxxxxx // 上記作成時に出力されるクラスター開始時の設定 $ mkdir -p $HOME /.kube$ sudo cp -i /etc/kubernetes/admin.conf $HOME /.kube/config $ sudo chown $(id -u):$(id -g) $HOME /.kube/config // flannel 構築 $ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml // 起動してく様子がわかる♪ $ kubectl get pod --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-78fcd69978-sv52p 0/1 Pending 0 111s kube-system coredns-78fcd69978-t5glm 0/1 Pending 0 111s kube-system etcd-pikube01 1/1 Running 0 2m kube-system kube-apiserver-pikube01 1/1 Running 0 2m4s kube-system kube-controller-manager-pikube01 1/1 Running 0 2m kube-system kube-flannel-ds-w2bqt 0/1 Init:0/2 0 9s kube-system kube-proxy-kpm8w 1/1 Running 0 111s kube-system kube-scheduler-pikube01 1/1 Running 0 2m
flannel はコンテナの相互疎通等ネットワーク構築に有用で k8s との相性がよいです。
worker ノード登録 master node でクラスター作成時に出力されたコマンドを実行します。 以下 worker node で実施します。
1 2 3 4 5 6 7 8 9 $ sudo kubeadm join <master node ip>:6443 --token xxx \ --discovery-token-ca-cert-hash sha256:yyy ... 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.
token は期限付きなのでご注意ください。
token の期限は master node で確認できます。
1 2 3 4 master$ kubeadm token list TOKEN TTL EXPIRES USAGES DESCRIPTION EXTRA GROUPS xxx 23h 2022-04-28T13:12:39Z authentication,signing <none> system:bootstrappers:kubeadm:default-node-token
token の期限が切れたら master node で再発行してください。 トークンを再発行し、且つ、worker node で kubeadm join
する為のコマンドを出力してくれます。
1 master$ kubeadm token create --print-join-command
クラスタで worker node が登録されてるか確認する 1 2 3 4 5 6 master$ kubectl get nodes NAME STATUS ROLES AGE VERSION pikube01 Ready control-plane,master 32h v1.22.7 pikube02 Ready <none> 32m v1.22.7 pikube03 NotReady <none> 18s v1.22.7
label 付けする 1 2 master$ kubectl label node pikube02 node-role.kubernetes.io/worker= master$ kubectl label node pikube03 node-role.kubernetes.io/worker=
再度 node 一覧を表示すると ROLES にラベル付けされているのが確認できます。
1 2 3 4 5 6 $ kubectl get nodes NAME STATUS ROLES AGE VERSION pikube01 Ready control-plane,master 32h v1.22.7 pikube02 Ready worker 39m v1.22.7 pikube03 Ready worker 7m8s v1.22.7
ローカルのマシンで kubectl で操作できる様にする 1 2 3 4 5 6 7 // 出力結果をコピーする master$ kubectl config view --raw macOS$ vi ~/.kube/config // 上記コピーを貼り付け保存 macOS$ kubectl get nodes
Metal LB インストール 参考:
1 2 3 // 全インターフェースで IPv4 パケットの転送が有効化する $ sudo sysctl net.ipv4.conf.all.forwarding=1 $ sudo iptables -P FORWARD ACCEPT
MetalLB > Installation にある設定通りに進めます。
1 2 3 4 5 6 7 8 9 10 11 $ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/namespace.yaml $ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/metallb.yaml // metallb 関連の pod の起動確認 $ kubectl get -n metallb-system pods NAME READY STATUS RESTARTS AGE controller-66445f859d-qg8cz 1/1 Running 0 30s speaker-bzzzc 1/1 Running 0 30s speaker-vbhdf 1/1 Running 0 30s speaker-vslj8 1/1 Running 0 30s
addresses: 192.168.11.200-192.168.11.220
は DHCP で取得可能なレンジを指定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // metallb を layer2 モードで起動 $ cat <EOF> metallb-config.yamlapiVersion: v1 kind: ConfigMap metadata: namespace: metallb-system name: config data: config: | address-pools: - name: default protocol: layer2 addresses: - 192.168.11.200-192.168.11.220 EOF $ kubectl apply -f metallb-config.yaml
nginx を type: LoadBalancer
でデプロイし、 metallb が IP を割り当てていることを確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 $ cat <EOF> nginx.deployment.yamlapiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 1 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app: nginx ports: - port: 80 targetPort: 80 type: LoadBalancer EOF $ kubectl apply -f nginx.deployment.yml
割り当てられた IP が 192.168.11.200
となっており、外部からアクセスできることを確認します。
1 2 3 4 5 $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 148m nginx-service LoadBalancer 10.109.50.62 192.168.11.200 80:31270/TCP 57m
1 2 3 $ curl 192.168.11.200 // Welcome to nginx! が表示される
Raspberry PI 以外のマシンからアクセスできない場合 自身の環境で Raspberry PI の設定をしている MacOS から EXTERNAL-IP
に nginx 起動直後はアクセスできましたが、 数分後、アクセスできなくなる事象が発生しました。
以下参考に解決しました。
参考: LoadBalancer using Metallb on bare metal RPI cluster not working after installation
MetalLB layer2 モードは、プロミスキャスモードが有効でない限り、ブロードキャストパケットを受信しません。 そのため、以下ブロードキャストパケットを受信できる様にすることで macOS –> metalLB の疎通が確認できました。
1 $ sudo ifconfig wlan0 promisc
promisc: “promiscuous” で「見境のない」という意味で全ての通信を読み込むモードにする、という意味です。
サーバ再起動で消えてしまう設定なので crontab に設定しておくと良い。
1 2 3 4 $ sudo crontab -e // 以下最終行に追記 @reboot sudo ifconfig wlan0 promisc
総評 ハマりポイントは以下でした。
kubeadm, kubelet が最新 1.23 系で動作せず
MetalLB の吐き出す External IP に接続できなかった
今後は以下に焦点を当てつつ、実際にサービスを作っていきます ♪
以上 参考になれば幸いです。
参考 https://qiita.com/reireias/items/0d87de18f43f27a8ed9b