ぶていのログでぶログ

思い出したが吉日

RaspberryPi4とNanoPi R4Sとmicrok8sでKubernetesクラスタを組む

この記事は🎄GMOペパボエンジニア Advent Calendar 2022の12/5の記事です。 昨日はbobの2022年に統計学や機械学習について学習したことまとめでした。統計学や機械学習のキャッチアップをするためのどのように勉強を行ったかというのがまとめられて参考になりますね。 明日は@genkiroidです!


そろそろKubernetes(k8s)のキャッチアップしたいと最近思っていて、先日入門Kubernetesを読んだ。 本を読んだだけでは完全に理解するのは難しいと思っていて実機が欲しくなっていた。 PCに環境を作ってもいいのだろうけど、そういえば…っと自宅のロストマウンテン*1をRaspberry Pi 4とNano Pi R4Sが発掘されたのでせっかくならこれで作ろうとなった。

Raspberry Pi 4

Raspberry Pi 4(RPi4)は有名なので説明は不要でしょう。 RPi4が発売された当初に入手したのですが、今は半導体不足の影響で入手が困難になっているのが残念ですね。 このモデルは4GBだが、Nano Pi R4Sも4GBなので十分かなって思っている。 基板がむき出しの状態で運用しているが、AmazonのBlack Fridayでケースを買ったのでそのうちかっこよくなっているはず。

Nano Pi R4S

NanoPiはFriendlyElecが開発・販売している小型シングルボードコンピュータだ。 名前から察するにRPi4の対抗馬として出た感じなのだろうか。

RPi4と違い標準でイーサネットポートが2つある。 当初はYAMAHA RTX810のリプレイス先として使おうと思っていたのだが、なんやかんやあって結局ホームゲートウェイを使うことになったので、ロストマウンテンに押し込められていた…。 WANとLANっと書いてあるので、おそらくルーターとして使うことを想定しているのだろう。 ちなみに、WANがeth0、LANがeth1になっている。 USB-Cポートは電源のみに使用する。

USB3.0が2ポートとMicroSDカードスロットがある。 1/4インチのねじ穴がありカメラなどのスタンドが使えるようになっている、…が私は使っていない。

メモリは4GBでRPi4と同じ容量を積んでいる。 CPUはRockchip RK3399というARM系CPUを積んでいる。 2.0GHz Cortex-A72を2コア、1.5GHzのCortex-A52を4コア積んでいる。 そのため、RPi4よりCPU性能が高いらしい。 実際、私の手元にあるNanoPi R4S2台とRPi4でGeekbench 5で比較したら確かに少し性能がよさそうだった。

Single-Core Score Multi-Core Score
NanoPi R4S 1台目 293 793
NanoPi R4S 2台目 294 795
Raspberry Pi4 233 701

RPi4とくらべて若干使いづらいのはHDMIポートが存在しないことだと思う。 ルーターやサーバ使用を想定していそうなので、そういうのはオミットしているのかもしれない*2。 シリアルコンソールも存在するのだが、私のようにメタルケースを使用する場合はアクセスできなくなるので注意が必要。

購入に関してはFrindlyElecの公式ショップから行えるのだが、私はAliExpressで購入した。 RK3399

使用するMicroSDカードは速いほうがよい

RPi4やNanoPi R4Sに使用するMicroSDカードは高速なほうがよい。 っと当たり前のことを書いているが、遅いメディアを使ってしまうとCreateContainerErrorが頻発してかなり微妙な感じになる…。 一応私は、AmazonのBlack Fridayで安かったSanDisk Ultra microSD 128GB UHS-I Class10を使っている。 もっと高速なメディアもあるがコストパフォーマンスを鑑みるとこれでよさそうだなぁってのと、実際使ってみたところこれで問題なかった。

使用するOSはUbuntu 20.04(Friendly Core)

筐体は決まったので次は使用するOSを決める。 RPi4は複数のOSが選択できるが、NanoPi R4SはOpenWrtベースのFirndlyWrtかUbuntu Coreベース*3のFrindlyCoreしか選択できない。 それぞれFrindlyElecがポーティングしているのだが、ゴリゴリ改造してあるわけじゃなくて、NanoPi R4S専用にチューニングされたカーネルとブートイメージが載っているだけで他のパッケージはそのままっぽい。 詳しくは NanoPi R4Sを参照のこと。

FrindlyCoreといえどUbuntuがそのまま使えるのと、RPi4でもUbuntuが使えること、そしてUbuntuならmicrok8sのインストールが簡単なのでUbuntu20.04/FrindlyCoreにすることにした。

OSイメージをMicroSDカードに書き込む。

RPi4のOSイメージ書き込みは公式が出しているRaspberry Pi Imagerを使うと便利。 この時に注意があり、必ず64bit版を選択する必要がある。 なぜかというとFrindlyCoreは64bit OSになっていて、OS上ではCPUアーキテクチャがaarch64として見えている。 クラスタを組む場合CPUアーキテクチャを合わせておいたほうが後々はまらないのでよい。

NanoPi R4SにはRaspberry Pi Imagerのような便利なツールはない。 なので、NanoPi R4SのWikiサイトを開き4.2.2 Flash to TFにあるdownload linkからFrindlyCoreのイメージをダウンロードしてくる。 その後、Windows Disk Imager Renewalを使ってMicroSDカードに書き込むと良い*4

microk8sのインストール

RPi4ならRaspberry Pi ImagerのオプションでSSHを有効化しておけばSSHできるはず。 NanoPi R4SはデフォルトでSSHが有効になっているので、Wikiにある情報でログインできるはず。

microk8sはsnapパッケージでインストールすることになるので事前にsnapをインストールしておく。

sudo apt update
sudo apt install -y snapd

microk8sのインストールはについては公式がドキュメントを用意している。 これを参考に以下を実行するだけである。簡単!

sudo snap install microk8s --classic --channel=1.25

RPi4のみ: cgroupを有効にする

microk8s(というかcontainerd?)がcgroupを使用している。 RPi4のUbuntu 20.04ではcgroupが有効化されていないので変更する必要がある。

$ sudo vim /boot/firmware/cmdline.txt
elevator=deadline net.ifnames=0 console=serial0,115200 dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash
↓
cgroup_enable=memory cgroup_memory=1 elevator=deadline net.ifnames=0 console=serial0,115200 dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash

ファイルを変更後、設定を適用するためにリブートする必要がある。

$ sudo shutdown -r now

FrindlyCoreのみ: containerd snapshotterのファイルシステムをnativeに変更する

FrindlyCoreをインストールしたNanoPi R4Sでは、いつまでたっても microks status --wait-ready が返ってこないと思う。 いろいろ原因を調査したところ、dmesgに以下のメッセージが出ていることがわかった。

$ dmesg -T
[Mon Nov 28 19:08:58 2022] overlayfs: filesystem on '/var/snap/microk8s/common/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/1109/fs' not supported as upperdir
[Mon Nov 28 19:09:59 2022] overlayfs: filesystem on '/var/snap/microk8s/common/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/1110/fs' not supported as upperdir

実はFrindlyCoreはrootファイルシステムをovelayfsで保護している構成になっている。

$ df -hT /
Filesystem     Type     Size  Used Avail Use% Mounted on
overlay        overlay  116G  4.0G  112G   4% /

その上に、containerdのsnapshotterがoverlayfsでオーバレイを作ろうとしているのでエラーになっているという感じだった。 幸いsnapshotterが利用するファイルシステムをoverlayfsからnativeに変更できるのでそうする。

$ sudo vim /var/snap/microk8s/current/args/containerd-template.toml
-- snip --
    # snapshotter is the snapshotter used by containerd.
    snapshotter = "${SNAPSHOTTER}"
      ↓
    snapshotter = "native"
-- snip --

設定変更を反映するためにmicrok8sを再起動する。

sudo microk8s stop
sudo microk8s start

ホスト名を名前解決できるようにする

microk8sでクラスタを組む場合、クラスタ内の各サーバのホスト名を名前解決できる必要がある(そうしないとmicrok8s joinするときにエラーになる)。 hostsに書いてもいいのだが、それはそれで管理が面倒なのでLLMNRを有効にして名前解決できるようにしておく。

$ sudo vim /etc/systemd/resolved.conf
-- snip --
#LLMNR=noLLMNR=yes
-- snip
$ sudo systemctl restart systemd-resolved

この設定をクラスタの各ノードに行い、それぞれのホスト名が名前解決できたら成功。

ping node1
ping node2
ping node3

kubectlをエイリアスする

kubectlを実行する場合、microk8s kubectl としてmicrok8sコマンドのサブコマンドとして実行する必要がある。 これを毎回打つのはだるいので以下のようにエイリアスしておくとよい。

$ sudo snap alias microk8s.kubectl kubectl
$ which kubectl
/snap/bin/kubectl

microk8sクラスタを組む

microk8sが起動したら、どれか一台のノードでmiccrok8s add-nodeを実行する。 すると、joinするためのコマンドがサジェストされる。

$ microk8s add-node
From the node you wish to join to this cluster, run the following:
microk8s join 192.168.XX.YYY:25000/********/******

Use the '--worker' flag to join a node as a worker not running the control plane, eg:
microk8s join 192.168.XX.YYY:25000/********/****** --worker

If the node you are adding is not reachable through the default interface you can use one of the following:
microk8s join 192.168.XX.YYY:25000/********/******

サジェストされたコマンドを別のノードで実行するとクラスタに組み込まれる。

$ microk8s join 192.168.XX.YYY:25000/********/******

microk8s status すると組み込まれているのがわかる。 ここで注意が必要なのは、3台以上でクラスタを組まないとhigh-availabilityがyesにならない。 これはRaft分散合意アルゴリズムを使っているからだと思う。

$ microk8s status
microk8s is running
high-availability: yes
  datastore master nodes: 192.168.XX.WWW:19001 192.168.XX.ZZZ:19001 192.168.XX.YYY:19001
  datastore standby nodes: none
-- snip --

kubectl get node でも各ノードが追加されていることがわかる。

❯ kubectl get node
NAME        STATUS   ROLES    AGE     VERSION
node2        Ready    <none>   18h     v1.25.4
node3        Ready    <none>   2d19h   v1.25.4
node1        Ready    <none>   6d23h   v1.25.4

課題: ストレージをどうするか

k8sではCSIを介して様々なストレージを利用することができる*5。 せっかくクラスタを組んでいるので、冗長性が高いストレージを選択したいと思っている。 NFSやSMBも対応しているのだがこれだとどこか1台ノードにデータが存在することになるし、クラスタ外で用意するの家庭内決裁が通る気がしない😅 そこで分散ストレージを利用したい。 いくつか候補はあるのだが、microk8sがアドオンとして提供しているmyastorを最初に試そうとした。 しかしながら、mayastorはnvme_tcpカーネルモジュールを必要としていて、これがFriendlyCoreに含まれていないので使えないことがわかった…。 次にLonghornを試したのだが、microk8sではサポートされていない(?)PodSecurityPolicyを利用しようとしてエラーになってしまった…。 GlusterFSは過去の経験上あんまり選択したくないし、JuiceFSはaarch64には対応していないし…うーん。

今のところPersistentDiskが必要なワークロードを想定していないので、一旦保留!というステータスになっている。 また、何かいい案がいつかったら紹介したいと思う。

まとめ

自宅に余っていたRassberry Pi 4とNanoPi R4SにUbuntu 20.04/FrindlyCoreを入れて、microk8sでクラスタを組むことができた。 microk8sはCanonicalがとてもプッシュしていることもあって、かなり簡単に導入できるのは魅力的だと思う。 少し手を加える必要があるが、IntelアーキテクチャだけではなくARMアーキテクチャでも動くのはすごく便利だと思う。

k8sクラスタを作ったばかりでまだ本格的にワークロードを入れていないので今後悩むことが出てくるかもしれないが、なんとかなるとおもう!(根拠なし 自宅k8sクラスタで運用ノウハウを貯めたら、こっしょり仕事の環境に入れて本番環境に入れてみたりしたい😏

*1:禁断の地になっているマウンテンサイクル。別名:おもちゃ箱

*2:といいつつ後継機のNanoPi R5Sには搭載されているんだよなぁ

*3:っと書かれているがos-releaseとか見るとUbuntuっぽいんだよなぁ

*4:Linuxならddを使うとよい。macOSは・・・わからん

*5:https://kubernetes-csi.github.io/docs/drivers.html