LoginSignup
21
10

More than 3 years have passed since last update.

RaspberryPi ZeroでおうちkubernetesしようとしたらARMがつらかった話

Last updated at Posted at 2019-04-30

ClusterHATを使って世界最小Kubernetesクラスタを構築する - サーバー構築編という記事を見て、できるんだ、自分もやってみようかなとやってみたら、なかなか出来なかったのでまとめます。

kubeletを動かしたというところが載ってなかったので同じところで詰まってしまったのかな・・・?
それか私の技術力が不足しているせいなのか。
いずれにせよ死ぬほど大変でした。

もし、こんなやり方なら普通にできるよというやり方があったら教えてください......

やったことはキレイにまとまってませんがこちらにまとめています。

この中で、ARM起因で詰まったところだけピックアップしています。
ラインナップは以下のとおりです。

  • Etcd(パッケージ版)の罠
  • Dockerイメージの罠
  • 動かない公式Kubernetes実行ファイル
  • 存在しないカーネル機能
  • 勝手に立ち上がるコンテナの罠

ハードウェアの構成

ClusterHAT上の役割 kubernetes上の役割 機器
master Raspberry Pi 3b+
本体 ClusterHAT
controller Raspberry Pi b+
node node Raspberry Pi zero WH * 2
node node Raspberry Pi zero

ClusterHATを使ってますがこれはRaspberry Pi Zeroを使ってクラスタが組めるものです。

要はサーバが3つ使えるというだけです。

クラスター...かっこいいじゃん、というモチベーションで購入しました。

なのでkubernetesのホストとしてはZeroシリーズ3台とRaspberryPi3が1台の計4台です。

Etcd(パッケージ版)の罠

etcdですが...

# apt install etcd

するとインストールできるくせに起動できません

# systemctl edit --full  etcd.service
Environment=ETCD_UNSUPPORTED_ARCH=arm

こいつを設定すると動きます。

エラーにこの環境変数がないぞと出るので設定しました。

UNSUPPORTED_ARCHという不吉なワードが並んでいますが、とりあえず動いてるので見て見ぬふりをしました。

Dockerイメージの罠

dockerをインストールして、動作確認がてらnginxを落としてきましたが、docker runすると一瞬にして落ちます。

ログも一瞬で落ちてるのが分かるだけで、何も残っていませんでした。

2,3時間考えてたら思いつきました、armじゃんと。

# docker run -d -P --name ngx arm32v6/nginx:1.15.10-alpine
# docker inspect -f '{{ .NetworkSettings.IPAddress }}' ngx
# docker run --rm -it arm32v6/bash bash
   $ wget http://{{ inspectで出たIP }}/ -S -O - (コンテナ名)

で動きました。
ちゃんと動いてたのに、無駄に調べてました。

世の中で出回っているバイナリはだいたいx86_64のものっぽいですね・・・

ちなみにdocker自体も古いバージョンにダウングレードしないと動きませんでした。

動かない公式Kubernetes実行ファイル

当初はapiserverもzeroで動かすつもりでしたが、apiserver試してる時に全く動いてくれませんでした。

原因はarm v6アーキテクチャのCPU上で、arm v7の命令を動かしているからのようです。

この方の記事 を参考にやりました。

kubernetes(1.14),go(1.12.4)でビルドしました。

# go get k8s.io/kubernetes
# cd ${GOPATH}/src/k8s.io/kubernetes
# dd if=/dev/zero of=/swapfile2 bs=1M count=8192 (もともとswap4GBあったけど足りなくてエラーになったから8GB追加(合計12GB)のswap)
# makeswap -f /swapfile2
# swapon /swapfile2
# apt install gcc-arm-linux-gnueabi
# git diff
diff --git a/hack/lib/golang.sh b/hack/lib/golang.sh
index 36f9f437e8..4472ab5bdb 100755
--- a/hack/lib/golang.sh
+++ b/hack/lib/golang.sh
@@ -308,6 +308,7 @@ kube::golang::set_platform_envs() {

   export GOOS=${platform%/*}
   export GOARCH=${platform##*/}
+  export GOARM=5

   # Do not set CC when building natively on a platform, only if cross-compiling from linux/amd64
   if [[ $(kube::golang::host_platform) == "linux/amd64" ]]; then
@@ -316,7 +317,7 @@ kube::golang::set_platform_envs() {
     case "${platform}" in
       "linux/arm")
         export CGO_ENABLED=1
-        export CC=arm-linux-gnueabihf-gcc
+        export CC=arm-linux-gnueabi-gcc
         ;;
       "linux/arm64")
         export CGO_ENABLED=1
# make(正直なんで1回makeするのか分かってない)
# make all WHAT=cmd/kube-proxy KUBE_VERBOSE=5 KUBE_BUILD_PLATFORMS=linux/arm
# make all WHAT=cmd/kubelet KUBE_VERBOSE=5 KUBE_BUILD_PLATFORMS=linux/arm
# make all WHAT=cmd/kubectl KUBE_VERBOSE=5 KUBE_BUILD_PLATFORMS=linux/arm
# make all WHAT=cmd/kube-controller-manager KUBE_VERBOSE=5 KUBE_BUILD_PLATFORMS=linux/arm
# make all WHAT=cmd/kube-apiserver KUBE_VERBOSE=5 KUBE_BUILD_PLATFORMS=linux/arm
# make all WHAT=cmd/kube-scheduler KUBE_VERBOSE=5 KUBE_BUILD_PLATFORMS=linux/arm

kubernetes1.15でmakeしたときはGOARM=6で行けました。
kubernetes1.14でmakeしたときはmake cleanしてなかったせいかGOARM=5じゃないと参考にした記事の通りリンカでエラーが出ました。

存在しないカーネル機能

kubeletを実行すると、cpusetが無いと言われます。

cpusetはカーネルの機能ですね。

そのcpusetが最新版Pi Zero用Raspbianのカーネルに含まれてませんでした。

# cat /proc/cgroups
#subsys_name    hierarchy       num_cgroups     enabled
cpu     6       51      1
cpuacct 6       51      1
blkio   5       51      1
memory  3       81      1
...

raspberrypi zero cpusetで検索すると皆さん苦労してるんだなというのが分かります。

結果的にやったことをリストにすると

  • Raspbian-2017-11-29の古いイメージを落とす
  • clusterhat用のイメージに変換するツールを使う
  • カーネルアップグレードしないようにツールを修正する

です。

だいたいここで言われていることを自分なりに解釈してやってみました。

注意したいのがClusterHAT用イメージ作成ツールは、chrootでapt updateしようとしており、そのaptがarmなので、arm以外のcpu上でイメージを作成しようと思っても動きません。

armのPC持ってないのでcontroller上でイメージを作成しました。

# wget http://ftp.jaist.ac.jp/pub/raspberrypi/raspbian_lite/images/raspbian_lite-2017-12-01/2017-11-29-raspbian-stretch-lite.zip
# unzip 2017-11-29-raspbian-stretch-lite.zip
# apt install kpartx
# git clone https://github.com/burtyb/clusterhat-image
# cd clusterhat-image/build
# mkdir {img,mnt,dest,mnt2}
# mv /tmp/2017-11-29-raspbian-stretch-lite.img img/
# vim create.sh # 以下みたいな感じで修正
@@ -64,8 +64,10 @@ if [ "$LITE" = "y" ];then
   mount `echo $LOOP|sed s#dev#dev/mapper#`p1 $MNT/boot

   # Get any updates / install and remove pacakges
+  chroot $MNT apt-mark hold raspberrypi-kernel raspberrypi-kernel-headers
   chroot $MNT apt-get update
   chroot $MNT /bin/bash -c 'APT_LISTCHANGES_FRONTEND=none apt-get -y dist-upgrade'
+  chroot $MNT apt-mark unhold raspberrypi-kernel raspberrypi-kernel-headers
   chroot $MNT apt-get -y install bridge-utils wiringpi screen minicom python-smbus

   # Setup ready for iptables for NAT for NAT/WiFi use
@@ -327,8 +329,10 @@ if [ "$DESKTOP" = "y" ];then
   mount `echo $LOOP|sed s#dev#dev/mapper#`p1 $MNT/boot

   # Get any updates / install and remove pacakges
+  chroot $MNT apt-mark hold raspberrypi-kernel raspberrypi-kernel-headers
   chroot $MNT apt-get update
   chroot $MNT /bin/bash -c 'APT_LISTCHANGES_FRONTEND=none apt-get -y dist-upgrade'
+  chroot $MNT apt-mark unhold raspberrypi-kernel raspberrypi-kernel-headers
   chroot $MNT apt-get -y install bridge-utils wiringpi screen minicom python-smbus
   chroot $MNT apt-get -y purge wolfram-engine 

@@ -510,8 +514,10 @@ if [ "$FULL" = "y" ];then
   mount `echo $LOOP|sed s#dev#dev/mapper#`p1 $MNT/boot

   # Get any updates / install and remove pacakges
+  chroot $MNT apt-mark hold raspberrypi-kernel raspberrypi-kernel-headers
   chroot $MNT apt-get update
   chroot $MNT /bin/bash -c 'APT_LISTCHANGES_FRONTEND=none apt-get -y dist-upgrade'
+  chroot $MNT apt-mark unhold raspberrypi-kernel raspberrypi-kernel-headers
   chroot $MNT apt-get -y install bridge-utils wiringpi screen minicom python-smbus
   chroot $MNT apt-get -y purge wolfram-engine

# create.sh 2017-11-29

勝手に立ち上がるコンテナの罠

やっとk8sのコンポーネント全部動いた!

と思ってpodを立ち上げたら立ち上がりませんでした。

kubeletのログが以下のようなやつで埋め尽くされてました。

Error response from daemon: OCI runtime create fa
iled: container_linux.go:341: creating new parent process caused \"container_linux.go:1713: running lstat on namespace path \\\"/proc/20093/ns
/ipc\\\" caused \\\"lstat /proc/20093/ns/ipc: no such file or directory\\\"\": unknown"

ipcはコンテナ間の通信の話らしいですが、1コンテナしか立ててないはずでした。
それでdocker ps -aするとpauseコンテナが出てきました。
しかもすぐexitしてしまっている模様。

このpauseってやつが原因かもなあと思って、pauseをarmv6用にビルドしてみました。

pauseコンテナのDockerfileなどはk8sのリポジトリ内にありました。

$ cd ~/go/src/k8s.io/kubernetes
$ git diff
diff --git a/build/pause/Makefile b/build/pause/Makefile                                                                                      
index 92b0f40b16..b718ef86a1 100644
--- a/build/pause/Makefile
+++ b/build/pause/Makefile
@@ -40,7 +40,7 @@ ifeq ($(ARCH),amd64)
 endif

 ifeq ($(ARCH),arm)
-       TRIPLE ?= arm-linux-gnueabihf
+       TRIPLE ?= arm-linux-gnueabi
 endif

# docker build . -t pause-arm 
# docker save pause-arm  > pause-arm.tar

感動の瞬間

$ kubectl get nodes
NAME            STATUS     ROLES    AGE    VERSION
p1              Ready      <none>   37h    v1.14.2-beta.0.9+3c949c7d419670-dirty
$ kubectl get pod  
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   16         36h

まとめ

だいたいZeroのCPUアーキテクチャで詰まってた気がするのでk8s用途でZeroシリーズを使うのはやめたほうが良さそうでした。

よくよく記事を見て回るとPi3×4台くらいで作ってる人がほとんどでした。(みんなお金持ち;;)

最終的に動いたには動きましたが、実際に運用となると、おいそれとアップデートができなさそうなのがちょっと辛いです。

カーネルがそもそも古いバージョン固定じゃないといけないですしね。

まあ趣味だから良いかという感覚です。

そもそも、カーネルコンフィグを正しく設定できる技術力があれば・・・

Pi3を4台くらい余裕で買えるくらいのところに転職したいので何か仕事があったら教えてください。

21
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
10