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台くらい余裕で買えるくらいのところに転職したいので何か仕事があったら教えてください。