K8s on ラズパイ(ARM64)にcri-o+podmanでworkerを追加してみた


はじめに

2020/12末時点で、kubernetesのworkerノードを「cri-o」と「podman」で構築してみた結果です。

環境

  • Raspberry Pi 4B
    • OS: RaspiOS Lite(ARM64) beta 2020/8/24 (※本手順では最終的にkernelの入れ替えをしています)
    • kubernetes: v1.20.1 (※タイミング悪くv1.20.0のマスターにv1.20.1を追加してます)

構築した手順

メモ書き程度ですが、ご参考まで。
RaspiOS Lite(ARM64)をインストール後、ホスト名やネットワークの設定後の状態からです。
https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2020-08-24/

1. スワップ無効化

swap有効化のままでもkubeletがインストールできる方法もあるようですが、私は無効化してます。

dphys-swapfile swapoff
systemctl disable dphys-swapfile

2. cgroupsの設定

cgroups...kernelによってsysfs上の階層構成が変わったりしてたり、よくわかってません。

perl -pi -e "s/^/cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1 /" /boot/cmdline.txt 

3. cri-o,podmanのインストール

arm64のパッケージが含まれる最新バージョンが「1.18.3」のようです。コンパイルすればもっと最新が使えそうですがやってません。

OS=Debian_Unstable
VERSION=1.18:1.18.3

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 -

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

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

ここまでは公式の手順で良いのですが、1.18.3は「1.18:/1.18.3」という階層にあるので修正します。
一旦「apt update」してエラーが出ないか確認しましょう。

perl -pi -e 's/18:/18:\//' /etc/apt/sources.list.d/devel\:kubic\:libcontainers\:stable\:cri-o\:1.18\:1.18.3.list
apt update

Debian10用にバックポートされた「libseccomp2」パッケージ用のリポジトリ追加です。

echo 'deb http://deb.debian.org/debian buster-backports main' >> /etc/apt/sources.list
apt update

パッケージのインストールをします。
dockerコマンドの代替えである「podman」はkubernetes関連のパッケージと競合しているようなので、「podman-rootless」をインストールします。
https://github.com/containers/podman/issues/5296

apt -t buster-backports install libseccomp2
apt install cri-o cri-o-runc podman-rootless

インストールできたらcrioサービスを有効化します。

systemctl enable crio

4. kubelet,kubeadm,kubectlのインストール

定番なので説明は書きませんが、「apt update」が失敗する事があるので、成功するまで「apt update」してます。

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" >> /etc/apt/sources.list.d/kubernetes.list
apt update
apt install kubelet kubeadm kubectl

バージョン指定でインストールする場合は以下のように指定できます。
apt install kubelet=1.20.0-00 kubeadm=1.20.0-00 kubectl=1.20.0-00

5. その他設定

必要なカーネルパラメータを追加しておきます。

cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF

必要な前提カーネルモジュールを定義しておきます。

echo "br_netfilter" >> /etc/modules

一応、nftablesを使わないようにします。詳しく調べてませんが、kube-proxyはnftablesで良くても、flannelがiptablesじゃなきゃダメなのかなと思ってます。

update-alternatives --set iptables /usr/sbin/iptables-legacy

dockerを利用しないので、iptablesのFORWARDのデフォルトポリシーはACCEPTのままなはずですが、DROPの場合はACCEPTに変更してください。

kubeletがcgroup-driverとしてsystemdを使うように設定します。

echo "KUBELET_EXTRA_ARGS=--cgroup-driver=systemd" >> /etc/default/kubelet

本記事前提OSのカーネル以外の場合は、この状態でrebootして「kubeadm join」すればうまく動くかもしれません。

6. kernelの入れ替え

RaspiOS Lite(ARM64)の場合は、カーネルがよろしくないようでkubernetesクラスタに参加できてもPodがデプロイされませんでした。
具体的には「〜cpu.cfs_period_us: permission denied」と出てPodのデプロイに失敗します。
調べると以下の記事を発見しました。(最初からここ見ればよかったなぁ〜)
https://gist.github.com/dmesser/b110d717b372e36607b3fb75b52ab0ca

ただ、実際カーネルを調べると「CONFIG_CFS_BANDWIDTH」は有効になっているはずなのですが、/procや/sysの階層がkubeletが想定しているような階層になっていないように思えました。
どちらにせよ、おそらくカーネルを再コンパイルしなくてはならないと思われますが、Cephを導入した際にrbdを有効にしたカスタムカーネルがあったので、試しに入れ替えたらうまく動きました。

カーネルをコンパイルした手順は以下になります。
https://qiita.com/Y-Shikase/items/c6fffbbd11a3d039b380#kernel%E3%83%93%E3%83%AB%E3%83%89

上記手順でカーネルを入れ替えて再起動後、「kubeadm join」でうまくクラスタへの参加とPodのデプロイができました。

masterノードでの作業
# kubeadm token create
cbrhlc.ckwxfkt2zfv75b4b
# openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
9dce510f61f13a841dc94fd43d27c5264a76c671deae350363069c6e43c04879

tokenとhashをメモる。

workerノードでの作業
kubeadm join 10.0.0.1:6443 --token cbrhlc.ckwxfkt2zfv75b4b --discovery-token-ca-cert-hash sha256:9dce510f61f13a841dc94fd43d27c5264a76c671deae350363069c6e43c04879

注意点

自炊イメージを使用している場合

dockerでコンテナイメージを自炊して、kubernetes上で使っている場合には少し注意が必要です。
dockerではリポジトリに階層を付けない事ができるのですが、podmanでは「localhost/」という階層が必ず付きますので、dockerとpodmanで混在している場合はpodman側に合わせなければなりません。
dockerのworkerでは自炊のイメージにエイリアスで「localhost/」のリポジトリ名を付けて、kubernetesのyamlも「image: lcoalhost/〜」としました。

kubeletのエラー

動いてはいますが、kubeletがエラーを吐いてます。

コンテナのデバッグ

cri-oの不具合でexecが実行できません。
https://github.com/cri-o/cri-o/issues/4280

ARM64向けの新バージョンのcri-oが出るのを待つか、最新を自分でコンパイルする必要がありそうです。

[追記]
再構築する前にダメ元でxUbuntu用のcri-o 1.20.0をインストールしたらデバッグできるようになりました。

wget http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/1.20/xUbuntu_20.04/arm64/cri-o_1.20.0~0_arm64.deb
dpkg -i cri-o_1.20.0~0_arm64.deb

また、1.20.0にしたからかはちゃんと確認してませんが、イベントに以下のWarningが定期的に出るようになりました。

$ kubectl get events -A -w
       :
default          0s          Warning   ImageGCFailed             node/maya                                       failed to get imageFs info: non-existent label "crio-images"

kubeletのログは以下のようなログが出ます。

Jan  3 00:00:52 maya kubelet[455]: E0103 00:00:52.287957     455 log_metrics.go:66] failed to get pod stats: failed to get imageFs info: non-existent label "crio-images"

対策は、以下のissuesに書かれているように、サービス起動ファイルの[Unit]に「Before=kubelet.service」を追記しましょう。
https://github.com/cri-o/cri-o/issues/4437

dockershim vs cri-o

大袈裟な書き方ですが、同じHW構成でコンテナランタイムだけ違うworkerの無風状態での負荷を比較しました。
Loadがdockerは1.2くらいの平均で、cri-oは0.6くらいの平均になりました。cri-oは100MBくらいメモリ使用量も少なかったです。


podmanについて

まだ使い込んでないのですが、Dockerfileからのbuildができたので、docker互換として使えるようです。

「images」はそのまま使えましたし、「load/save」も使えそうです。
ただ、「ps」は表示されません。

CRI経由で作成されたものだからかもしれませんが、crictlコマンドで表示できました。

おわりに

色々試しましたが以下のような結果になりました。

・podmanでDockerfileからのbuild、イメージのsave/loadはできたので、脱dockerはできそう
・負荷はdockerより軽い
・cri-o 1.18.3では不具合によりコンテナのデバッグできない→xUbuntu用のcri-o 1.20.0で動いた!?

新バージョンのcri-oをコンパイルする元気が無かったので、個人的には「もう少し待った方がいいかなー」という感じでした。