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


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

構成

  • ラズパイ: 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のダウンロードページ で、hypriotos-rpi-v1.7.1.img.zip をクリックして、ダウンロードします。 それから、MacOSを利用したSDカード作成のガイドを参考にして、SDカードを作成します。

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

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

ログインは、HyperiotOS ユーザーIDとパスワードは、FAQに書いてあります。 ログインできたら、'sudo -s' してルートユーザーで作業します。

/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行目がコメントになっているので、#を削除して、有効化します。

/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分くらい経過したところで、コンテナが止まってしまいます。 そして、停止と起動を繰り返すといった症状がおきました。 

  • 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 からの3行を実行して、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ファイルを編集します。変更箇所は、以下の3箇所で、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 の3箇所のデータをピックアップして、管理者クライアントの.kube/configへ追加する事で、kubectl config get-contextsでリストを表示して、kubectl config set-context rasp-k8s で表示できる様になります。

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

参照先によると、DockerHubのオフィシャルのコンテナ・イメージは、AMD64以外のアーキテクチャのCPUへも移植され、利用できる様になっていました。

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

参考資料