ラズパイでKubernetesを動かして見たら、驚いた！


ラズパイで、Kubernetesが動くという記事を見つけて、自分でも試して見ました。 HyperiotOSというDockerコンテナエンジンが、プレインストールされたラズパイのOSを使って、簡単にできるかと思ったのですが、結構、苦労したので、記録として残しておきたいと思います。 しかし、苦労に見合う大きな発見があり、最後の部分に書いておきました。


![IMG_4629.jpg](https://qiita-image-store.s3.amazonaws.com/0/42829/0285eeac-7f0c-41a9-e50f-f5893c78f629.jpeg "IMG_4629.jpg")



## 構成

* ラズパイ:  Raspberry Pi 3 Model B V1.2  x4 
* ラズパイのOS: HyperiotOS Version 1.7.1 https://blog.hypriot.com/, Docker-CE 17.10
* K8s: バージョン 1.8.14 
* ポッドネットワーク: Flannel 0.9.1


## Hyperiot OSのSDカードの作成

HyperiotOSの[ダウンロードページ](https://blog.hypriot.com/downloads/) で、hypriotos-rpi-v1.7.1.img.zip をクリックして、ダウンロードします。　それから、[MacOSを利用したSDカード作成のガイド](https://blog.hypriot.com/getting-started-with-docker-and-mac-on-the-raspberry-pi/)を参考にして、SDカードを作成します。

## IPアドレスとホスト名の設定

作成したSDカードをセットして、モニターとキーボードを接続して、ラズパイを起動したら、/boot/user-dataを編集して、ホスト名を設定します。

ログインは、HyperiotOS ユーザーIDとパスワードは、[FAQ](https://blog.hypriot.com/faq/)に書いてあります。 ログインできたら、'sudo -s' してルートユーザーで作業します。


~~~file:/boot/user-data
#cloud-config
# vim: syntax=yaml
#

# The current version of cloud-init in the Hypriot rpi-64 is 0.7.9
# When dealing with cloud-init, it is SUPER important to know the version
# I have wasted many hours creating servers to find out the module I was trying to use wasn't in the cloud-init version I had
# Documentation: http://cloudinit.readthedocs.io/en/0.7.9/index.html

# Set your hostname here, the manage_etc_hosts will update the hosts file entries as well
hostname: master1       <--- ホスト名をセット
manage_etc_hosts: true

# You could modify this for your own user information
users:
以下省略
~~~


デフォルトでは、DHCPでIPアドレスを取得する設定なのですが、k8sクラスタを構成するために、エディタで/etc/network/interface.d/eth0を編集して、静的なIPアドレスを設定します。

~~~/etc/network/interface.d/eth0
allow-hotplug eth0
iface eth0 inet static
    address 192.168.1.201
    netmask 255.255.255.0
    gateway 192.168.1.1
    nameserver 192.168.1.1
~~~

以上の設定が完了したら、rebootコマンドで再起動します。

## 認証鍵の設定

sshでパスワード無しでログインできる様に、公開鍵を設定して、ログインできる様にします。

pirate でログインして、エディタでauthorized_keysに公開鍵を設定ます。 sshの鍵生成方法は、https://webkaru.net/linux/ssh-keygen-command/ を参考にすると良いと思います。

~~~
$ mkdir .ssh
$ vi authorized_keys
~~~


それから、ルートになって、sshd設定ファイルを編集します。 33行目がコメントになっているので、#を削除して、有効化します。

~~~file:/etc/ssh/sshd-config
     26 # Authentication:
     27 LoginGraceTime 120
     28 PermitRootLogin without-password
     29 StrictModes yes
     30 
     31 RSAAuthentication yes
     32 PubkeyAuthentication yes
     33 AuthorizedKeysFile      %h/.ssh/authorized_keys
     34 
~~~

設定が有効になる様に、以下でsshdを再起動して、設定を有効化します。

~~~
sudo systemctl restart ssh.service
~~~


## Kubernetesのインストール

Kuberentesのバイナリが登録されたリポジトリを追加して、aptパッケージ・マネージャーでインストールできる様にします。

~~~
apt-get update && apt-get install -y apt-transport-https
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF
apt-get update
~~~

ARMプロセッサ用にビルドされたKubernetesは、どれでも利用できるという訳ではなさそうです。 筆者が試したところ、1.9.0 以降では、apiserverが安定に動作せず、利用できませんでした。原因を詳しく調べていませんが、apiserverが起動して、１分くらい経過したところで、コンテナが止まってしまいます。 そして、停止と起動を繰り返すといった症状がおきました。　

* 1.10.5 apiserver NG
* 1.9.8  apiserver NG
* 1.9.0  apiserver NG
* 1.8.14 apiserver OK
* 1.8.0  apiserver OK
* 1.7.16 apiserver OK


とりあえず、動作する 1.8.14 を利用する事にして、セットアップを進めます。　次のコマンドで、バージョンを指定して、インストールします。

~~~
# apt-get update
# apt-get install kubelet=1.8.14-00 kubeadm=1.8.14-00 kubectl=1.8.14-00 kubernetes-cni=0.5.1-00
~~~

利用可能なバージョン確認するには、`apt-cache madison kubeadm` を利用します。 

もし、インストールしたバージョンが不安定だったり、問題があったら、次のコマンドで、アンインストールして、他のバージョンを試すことができます。

~~~
apt-get remvoe --purge kubelet=1.8.14-00 kubeadm=1.8.14-00 kubectl=1.8.14-00 kubernetes-cni=0.5.1-00
~~~


## 設定の修正とマスターの初期化

インストールが終わったら、次のファイルを編集します。

~~~
vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
~~~

修正部分は、KUBELET_DNS_ARGSのdnsのアドレスです。 Flannel のポッド・ネットワークは、固定アドレスなので、合わせて修正して、--cluster-dns=10.244.0.10 とします。

~~~
      5 Environment="KUBELET_DNS_ARGS=--cluster-dns=10.244.0.10 --cluster-domain=cluster.local"
~~~

終わったら、kubeletを再起動して、設定を反映させます。

~~~
systemctl daemon-reload && systemctl restart kubelet
~~~


次のコマンドで、マスターノードの初期化を実行します。

~~~
kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.1.201 --service-cidr=10.244.0.0/16
~~~

もし、間違えたり、途中で、問題が発生して、最初からやり直したい場合は、以下の様にリセットできます。リセット後に、前述の`kubeadm init`から、やり直し事ができます。

~~~
kubeadm reset 
~~~

このリセットは、マスターを初期化で、動作を開始した Dockerコンテナを終了と削除、設定を消去するものです。 マスターだけでなく、ノードでも利用する事ができます。

## kubectlの実行環境作成

マスターの設定が完了したら、次の表な表示が出ますから、ルートをexitして、一般ユーザーになって、下記のmkdir からの３行を実行して、kubectl が動作する様にします。

~~~
$ kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.1.201 --service-cidr=10.244.0.0/16

<途中省略>

Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run (as a regular user):

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  http://kubernetes.io/docs/admin/addons/

You can now join any number of machines by running the following on each node
as root:

  kubeadm join --token 9d3ca7.4b675998b35c75bc 192.168.1.201:6443 --discovery-token-ca-cert-hash sha256:c2530cd4440c04a9b463d046ef27f6a6edf30b304aad8b196cee90a12c32d47e
~~~

設定が完了したら、`kubectl get node` を実行して、以下の様に表示されたら、成功です。

~~~
$ kubectl get node
NAME      STATUS     ROLES     AGE       VERSION
master1   NotReady   master    2m        v1.8.14
~~~

上記の様に、設定直後は、NotReadyの状態ですが、ポッド・ネットワークを設定することで、Readyの状態へ変わります。



## ポッド・ネットワークの作成

最後に、ノードを跨って構成するポッド・ネットワークを設定します。  Flannelは、デーモン・セットとして、稼働するので、YAMLを使って設定します。

~~~
curl -O https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml
~~~

ダウンロードしたYAMLファイルを編集します。変更箇所は、以下の３箇所で、amd64 を armに修正します。

~~~
$ diff kube-flannel.yml kube-flannel-arm.yml 
88c88
<         beta.kubernetes.io/arch: amd64
---
>         beta.kubernetes.io/arch: arm
96c96
<         image: quay.io/coreos/flannel:v0.9.1-amd64
---
>         image: quay.io/coreos/flannel:v0.9.1-arm
110c110
<         image: quay.io/coreos/flannel:v0.9.1-amd64
---
>         image: quay.io/coreos/flannel:v0.9.1-arm
~~~


ファイルを修正したら、次の様に適用します。

~~~
kubectl apply -f kube-flannel-arm.yml
~~~

コマンドを実行して、1〜2分で、ネットワークが立ち上がり、マスターが Ready状態になります。

~~~
$ kubectl get po -n kube-system
NAME                              READY     STATUS    RESTARTS   AGE
etcd-master1                      1/1       Running   0          2m
kube-apiserver-master1            1/1       Running   1          2m
kube-controller-manager-master1   1/1       Running   0          45s
kube-dns-66ffd5c588-4r78m         2/3       Running   0          1m
kube-flannel-ds-52827             1/1       Running   0          25s
kube-proxy-rvpn6                  1/1       Running   0          1m
kube-scheduler-master1            1/1       Running   0          2m
HypriotOS/armv7: pirate@master1 in ~
$ kubectl get node
NAME      STATUS    ROLES     AGE       VERSION
master1   Ready     master    3m        v1.8.14
~~~

これで、マスタの設定が完了しました。

## ノードの追加

コンテナの実行環境であるノードは、マスターと同じ様に、下記の作業を実施した後に、マスターの初期化が完了した時に表示される `kubeadm join --token ... ` を実行して、クラスタに追加します。


* /boot/user-data を編集して、ホスト名を設定 node1,node2...
* /etc/network/interface.d/eth0 を変数して、静的IPアドレスを付与
* ssh鍵の設定
* apt-get update && apt-get install kubelet=1.8.14-00 kubeadm=1.8.14-00 kubectl=1.8.14-00 kubernetes-cni=0.5.1-00 でインストール
* vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf の--cluster-dns=10.244.0.10 の変更
* systemctl daemon-reload && systemctl restart kubelet

上記の設定が終わったら、`kubeadm init ...`の完了時の表示をコピペして、各ノードで実行します。

~~~
kubeadm join --token 9d3ca7.4b675998b35c75bc 192.168.1.201:6443 --discovery-token-ca-cert-hash sha256:c2530cd4440c04a9b463d046ef27f6a6edf30b304aad8b196cee90a12c32d47e
~~~

これが完了すると、マスターで確認できる様になります。デーモンセットは、自動的に展開されるので、追加したノードは順次 Ready状態になります。

~~~
$ kubectl get node
NAME      STATUS     ROLES     AGE       VERSION
master1   Ready      master    7m        v1.8.14
node1     Ready      <none>    49s       v1.8.14
node2     Ready      <none>    39s       v1.8.14
node3     NotReady   <none>    17s       v1.8.14
~~~


## kuebctl の .kube/configへの追加

マスタの.kube/configから、clustersの-cluster, contextsの-context, usersの-name の３箇所のデータをピックアップして、管理者クライアントの.kube/configへ追加する事で、`kubectl config get-contexts`でリストを表示して、`kubectl config set-context rasp-k8s` で表示できる様になります。

~~~.kube/config
clusters:
- cluster:
    certificate-authority-data: XXXX     
    server: https://192.168.1.201:6443
  name: rasp-k8s
<省略>  
contexts:
- context:
    cluster: rasp-k8s
    namespace: default
    user: rasp-admin
  name: rasp-k8s        
<省略>
users:
- name: rasp-admin
  user:
    client-certificate-data: XXXX
    client-key-data: XXXX 
~~~

                     
## デプロイなど

IAサーバーやクラウドで利用しているYAMLファイルを適用して見ます。 armプロセッサ用のコンテナでなければ、起動しないはずですが、指定をしなくても nginxのコンテナが起動しました。

~~~
imac:k8s_l2_pod maho$ cat pod-nginx.yml 
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.15

imac:k8s_l2_pod maho$ kubectl apply -f pod-nginx.yml 
pod "nginx" created
imac:k8s_l2_pod maho$ kubectl get pod
NAME      READY     STATUS              RESTARTS   AGE
nginx     0/1       ContainerCreating   0          38s
imac:k8s_l2_pod maho$ kubectl get pod
NAME      READY     STATUS    RESTARTS   AGE
nginx     1/1       Running   0          1m
~~~                                  

確かに起動しています。                 
                                                   
~~~
imac:~ maho$ kubectl get po -o wide
NAME      READY     STATUS    RESTARTS   AGE       IP            NODE
nginx     1/1       Running   0          4m        10.244.3.2    node3
~~~

次に、対話型のシェルのポッドを起動して見ます。

~~~
imac:k8s_l2_pod maho$ kubectl run  -it ubuntu --rm --restart=Never --image=ubuntu -- bash 
~~~

もう一つターミナルを開いて、動作状態を確認します。 armプロセッサ上ですが、普通にコンテナが上がってきました。

~~~
imac:~ maho$ kubectl get po -o wide
NAME      READY     STATUS    RESTARTS   AGE       IP            NODE
nginx     1/1       Running   0          14m       10.244.3.2    node3
ubuntu    1/1       Running   0          8s        10.244.2.11   node2
~~~

ubuntu対話型シェルから、nginxサーバへアクセスして、動作を確認して見ます。下記の様に問題なく、利用できます。

~~~
root@ubuntu:/# apt-get update && apt-get install -y curl
<省略>
root@ubuntu:/# curl http://10.244.3.2/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<以下省略>
~~~

どっか間違えて、IAサーバーのパブリッククラウドへデプロイを掛けたのでしょうか？心配になってきたので、CPUの情報を確かめてみます。 しかし、確かに、ARMです。 INTEL CPUとバイナリ互換ではないので、ARM用に作られたコンテナがあるという事ですね。

~~~
root@ubuntu:/proc# cat cpuinfo 
processor	: 0
model name	: ARMv7 Processor rev 4 (v7l)
BogoMIPS	: 38.40
Features	: half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 4

processor	: 1
model name	: ARMv7 Processor rev 4 (v7l)
BogoMIPS	: 38.40
Features	: half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 4

processor	: 2
model name	: ARMv7 Processor rev 4 (v7l)
BogoMIPS	: 38.40
Features	: half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 4

processor	: 3
model name	: ARMv7 Processor rev 4 (v7l)
BogoMIPS	: 38.40
Features	: half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 4

Hardware	: BCM2709
Revision	: a32082
Serial		: 0000000006a75704
~~~


[参照先](https://github.com/docker-library/official-images#architectures-other-than-amd64)によると、DockerHubのオフィシャルのコンテナ・イメージは、AMD64以外のアーキテクチャのCPUへも移植され、利用できる様になっていました。 

これは、使い慣れないCPUアーキテクチャのマシンでも、DockerコンテナとKuberentesを利用する事で、敷居が下がり、利用しやすくなったと言えると思います。 メインフレームとかPOWER System でも、同じ様に簡単に利用できることを意味しているんですね。 コンテナには、ビックリしですね。



## 参考資料

* Kubernetes 1.8 のドキュメント https://v1-8.docs.kubernetes.io/docs/setup/independent/install-kubeadm/
* Setup Kubernetes on a Raspberry Pi Cluster easily the official way! https://blog.hypriot.com/post/setup-kubernetes-raspberry-pi-cluster/
* Installing kubeadm https://v1-8.docs.kubernetes.io/docs/setup/independent/install-kubeadm/
* https://github.com/coreos/flannel
* https://github.com/docker-library/official-images#architectures-other-than-amd64