はじめに
「せっかく年末年始が休みなので、自宅k8sクラスタでも組んでみるか!」
・・・さて、自宅サーバとして既に稼働していたIntelのNUC6CAYHをmasterにするとして、workerとしてはraspberry pie4を使ってみてはどうだろうか・・・?
・・・と意気込んだは良いものの、NUCはintel製品なので、amd64アーキテクチャを採用、raspberry pie 4 model Bではarmのプロセッサを積んでいるため、素直にkubeadmを導入してinit, joinをしただけでは、うまく動いてくれません。
kubeadmを利用した自宅k8sクラスタの構築記事なんかは山ほどあるのですが、今回のように、異なる構成のマシンを組み合わせてk8sクラスタを作る、という記事はほぼなく、すこし情報が古いようだったので、アップデートと防備録を兼ねて記事にします。
環境
マシンの物理構成
- MELCHIOR(IntelNUC6CAYH) https://www.intel.com/content/www/us/en/products/boards-kits/nuc/kits/nuc6cayh.html
- RAM: 8GB
- OS: Ubuntu 20.04.1 LTS
- Internal-IP: 192.168.100.230
 
- BALTHASAR (Raspberry pie 4 model B)
- OS: Raspbian GNU/Linux 10 (buster)
- Internal-IP: 192.168.100.232
- Storage: ADDATA microSD 64GB
 
- CASPER (Raspberry pie 4 model B)
- OS: Raspbian GNU/Linux 10 (buster)
- Internal-IP: 192.168.100.234
- Storage: SanDisk microSD 64GB
 
kubernetes
- kubeadm
- v1.20.1
 
- flannel
- v0.13.1-rc1
 
事前準備
kubeadmを使ってとりあえず、kubectl get nodes辺りが動くところまで持っていきましょう。以下の記事の内容が割とそのまま参考になるのでそちらをご覧ください:
- 
2019年版・Kubernetesクラスタ構築入門 (https://knowledge.sakura.ad.jp/20955/) - kubeadmを用いてのk8sクラスタの構成に関する日本語記事としては、こちらが非常にわかりやすいです。
 
- 
Raspberry Pi 4 でおうちKubernetesを作ろう(Raspbian Buster Lite対応版)(https://qiita.com/kentarok/items/6e818c2e6cf66c55f19a) - Raspbianを使う場合の諸設定などこちらが参考になります。上記の記事を補間するような形で参考にすると良いと思います。
 
問題点
上述した事前準備のところまで完了すると、とりあえずkubectl get nodesが動くようになっているはずです。しかし、待てども待てども以下のような状況から進みません:
@MELCHIOR:~$ kubectl get nodes
NAME        STATUS   ROLES                  AGE   VERSION
balthasar   NotReady    <none>                 16h   v1.20.1
casper      NotReady    <none>                 16h   v1.20.1
melchior    Ready    control-plane,master   16h   v1.20.1
masterであるノードであるmelchiorだけは問題なくReadyに成りますが、workerノードで何かが起きているようです。
こういうときは、kubectl get pods --all-namespacesで具体的にどのpodで問題が起きているのかを確認したり、さらにkubectl describe podsで詳細を確認するのがセオリー(らしい)ということで調べてみると・・・
今回のケースではどうやら、kube-proxyがコンテナの起動段階でコケてしまっており、つられて対応するkube-flannel-dsがうまく動かない、ということが起きているようでした。
また、なぜコケてしまっていたのかを詳しく分析してみると、次の2つの問題が起きていたようでした:
- 
- kubeadmが設定してくれたkube-proxyのためのdaemonsetにnodeSelectorが設定されておらず、raspberry pie 4の上で動く二つのworker node (casper, balthasar)でもamd64用のimageをpullしてくるようになってしまっていた。
 
- 
- raspbianだと、dnsにsystemd-resolvedではなく、openresolvを用いているため、resolv.confの参照先が異なっている
 
解決策
1.については、こちらなどを参考にして、既存のdaemonsetの設定にamd64用のnodeSelectorを追加した上でarm用のdaemonsetを新たに追加すれば良いです。
ただし、上記情報は若干古く、nodeSelectorの部分でセットするkey beta.kubernetes.io/archは既にdeprecatedなので、代わりにkubernetes.io/archを使うと良いと思います。
2.についてですが、とりあえず私が試行錯誤したり情報を探したりした結果だと、根本的な解決策を探すことは出来ませんでした。(詳しい方がいらっしゃれば教えていただけると幸いです)
そこで、当座のworkaroundとして、kube-proxy内のコンテナが参照している先にシンボリックリンクを作成することで、問題を回避することが出来ました。
describeでkube-proxyの詳細を見ると、次のようなeventの後にこけてしまっていることがわかります:
 Failed to create pod sandbox: open /run/systemd/resolve/resolv.conf: no such file or directory
そこでシンプルに/etc/resolv.confからシンボリックリンクをはってあげました。
mkdir /run/systemd/resolve
ln -s /etc/resolv.conf /run/systemd/resolve/resolv.conf
そして、対応するpodsを削除して、recreationを待ってみます。
最後に、get nodesをして各nodeの状態を確認してみると・・・
@MELCHIOR:~$ kubectl get nodes
NAME        STATUS   ROLES                  AGE   VERSION
balthasar   Ready    <none>                 16h   v1.20.1
casper      Ready    <none>                 16h   v1.20.1
melchior    Ready    control-plane,master   16h   v1.20.1
無事全部Readyになりました。やったー!
おわりに
今回初めて実機でk8sクラスタを構築してみましたが、想像より遥かに大変で、試行錯誤を繰り返すうちに、少しk8sと仲良くなれたような気がします。記事にすると、これだけの内容ですが、実際自分で試して様々なエラーと戦ってみると、良い経験になるなあ、という事を改めて感じましたので、みなさんもお正月(といってもあと僅かですが・・・)挑戦してみてはいかがでしょうか?
ところで、問題2に関してはあくまで、シンボリックリンクを作ってみたら、動いた、というワークアラウンドなので、ノードを物理的に再起動したりすると壊れます。
どなたか、この辺り詳しい方がいらっしゃれば、正しい解決策を教えていただけるとたすかります。
ちなみに、各物理ノードのホスト名は、某国民的アニメに登場するコンピュータが元ネタです。
