こんにちは、クラウドソリューション開発チーム・リーダーの山澤です。今回はkubernetesのPoCの一環として、Raspberry Pi3 Model BRKE(Rancher Kubernetes Engine) を使って、Kubernetes をデプロイしましたのでご紹介いたします。

Raspberry Pi3 Model B

Raspberry Pi3 Model BはARMプロセッサを搭載したシングルボードコンピュータです。
今回はこちらにARMv8(64bit)で動作可能なOSを使い、Dockerをインストールして、Kubernetesのノードとして利用できるかを検証しました。Raspberry Pi3の主なスペックはこのようになっています。

  • 4 ARM 64bit Cores (1.2 GHz)
  • 1GB Memory
  • 8GB SDカード

https://www.raspberrypi.org/products/raspberry-pi-3-model-b/

Raspberry-Pi-3-Model-B

HypriotOS

Raspberry Piの公式OSはdebianをベースとしたRaspbian(https://www.raspberrypi.org/downloads/raspbian/)になりますが、64bitモードで動作可能なHypriotOSを利用しました。

HypriotOSもまた、公式ページでは64bit対応のものは配布されていないため、HypriotのメンバーであるDieter Reuter氏のGithubリポジトリにある下記を利用させてもらいました。

https://github.com/DieterReuter/image-builder-rpi64/releases/

Releases-DieterReuter-image-builder-rpi64

Rancher2.0

Rancher2.0は米Rancher LabsがオープンソースとしてGithubで公開しているkubernetes管理ツールです。複数のKubernetesクラスタをWebUIなどから一元管理することができます。
本日時点(2018年3月X日時点)ではTechPreviewですが、すでにKubernetesのインポート機能を使うことができますので、今回は作成したRaspberry Pi3のKubernetesをその機能でWebUIから管理できるようにしました。

Rancher-2-Rancher-Labs

rancher/rke

rancher/rkeは上記のRancher2.0の内部でKubernetesエンジンとして使用されている、kubernetesデプロイツールです。こちらも現段階での最新版であるv0.1.1を利用させてもらいました。
※ ただし、ラズパイのスペック的な問題でデプロイにおいてタイムアウトが発生するので、リトライ回数を調整したカスタムビルド版を使用しています。

rancher-rke--Rancher-Kubernetes-Engine--an-extremely-simple--lightning-fast-Kubernetes-installer-that-works-everywhere-

検証

それでは早速とりかかりたいと思います。おおまかな流れは下記になります。

  1. Rancher2.0をローカルPCで動作させる。
  2. SDカードにOSのイメージを焼く。
  3. Raspberry Pi3でHypriotOSを起動する。
  4. HypriotOSをセットアップする。
  5. ローカルPCからKubernetesをデプロイする。
  6. 出来上がったクラスタをRancher2.0にインポートする。

1. Rancher2.0をローカルPCで動作させる。

rancher2.0はコンテナ化されていますので、起動はすごく簡単です。dockerが動いているローカルPCで下記のコマンドを実行したら立ち上がります。

docker run -d -p 80:80 -p 443:443 rancher/server:preview

2018年3月6日更新

docker run -d -p 80:80 -p 443:443 rancher/server:v2.0.0-alpha16

ブラウザでhttp://localhost を開くと最初のログイン画面が表示されます。(デフォルトはadmin/adminです)

Rancher

次にパスワード変更画面が表示されますので、任意のパスワードを設定すれば、Globalメニューのトップが表示されます。

1-top

ステップ6でAdd Clusterから作成したクラスタを追加しますので、ステップ1はここまでです。

2. SDカードにOSのイメージを焼く。

次に、64bit版HypirotOSのイメージを取得し、SDカードに焼きます。
今回はこちらのイメージを利用しました。

hypriotos-rpi64-v20180211-133408.img.zip

zipを展開してhypriotos-rpi64-v20180211-133408.imgをSDカードに焼くのですが、resin.ioが提供しているEtcherが便利です。3ステップでSDカードへの書き込みができます。

Etcher

3. Raspberry Pi3でHypriotOSを起動する。

SDカードの準備ができましたのでRaspberry Pi3に差し込んで起動します。(デフォルトのユーザとパスワードはpirate/hypriotになっています)
HDMIで接続可能なディスプレイとUSBキーボードをお持ちの方は、ログインしてifconfigでIPアドレスを確認しておきます。
お持ちでない方はこちらにあるようにnmapを駆使してIPアドレスを見つけてください。

https://blog.hypriot.com/getting-started-with-docker-and-linux-on-the-raspberry-pi/

IPアドレスがわかればSSHで接続することができます。

hypriot

4. HypriotOSをセットアップする。

HypriotOSは最初からdockerがインストールされているのですが、kubernetesをデプロイするためのバージョンに合わないのでアンイストールします。

hypriot-default-docker
hypriot-purge-docker

sudo apt-get purge -y docker-ce

ARMv64のdocker-1.12.6を使いたいのですが、残念ながらリポジトリには存在しません。

株式会社あっとBSD様が非常にレアなARMv64のdocker-1.12.6を公開してくださってますので、こちらを利用させてもらいました。

http://docker.atbsd.com/builds/Linux/arm64/docker-1.12.6.tgz

こちらをダウンロード&展開して、/usr/binに配置して起動します。
(※動作検証用なのでサービス化していないです。)

$ wget http://docker.atbsd.com/builds/Linux/arm64/docker-1.12.6.tgz
$ tar -zxvf docker-1.12.6.tgz
$ sudo cp docker/* /usr/bin/
$ sudo rm /etc/docker/daemon.json
$ sudo docker daemon &
$ docker version
Client:
 Version:      1.12.6
 API version:  1.24
 Go version:   go1.6.4
 Git commit:   78d1802-unsupported
 Built:        Wed Jan 11 07:09:31 2017
 OS/Arch:      linux/arm64

Server:
 Version:      1.12.6
 API version:  1.24
 Go version:   go1.6.4
 Git commit:   78d1802-unsupported
 Built:        Wed Jan 11 07:09:31 2017
 OS/Arch:      linux/arm64

sudo docker daemon &を実行時にgraphdriver関連のエラーが表示されますが、今回の検証には影響がないので進行可能です。

最後にローカルPCの公開鍵(~/.ssh/id_rsa.pub)をpirateユーザに渡しておきます。

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

5. ローカルPCからKubernetesをデプロイする。

Raspberry Piの準備が整いましたので、ローカルPCからrancher/rkeを使って、kubernetesをデプロイします。

rancher/rkeはv0.1.1にリトライ回数を増やしたバージョンが必要です。
ビルドしたものをDocker Hubにアップしていますので、よろしければお使いください。

$ docker pull kutil/kutil:edge

上記のコンテナからRaspberry Pi3に鍵でSSH接続できるように、先ほどの鍵をボリュームとしてマウントして起動します。
また、カレントディレクトも同様にマウントして、RKEの設定ファイルやKUBECONFIGファイルが残るようにしています。

$ docker run -it -v ~/.ssh/id_rsa:/root/.ssh/id_rsa -v $PWD:/work kutil/kutil:edge

rancher/rkeではconfigコマンドにより、インタラクティブにkubernetesのデプロイ構成を定義していくことができます。
定義内容はサーバにSSH接続するための情報や、各サーバの役割、クラスタの基本情報、各システムコンテナのオプションなど多岐に渡りますが、今回は1台のRaspberry Pi3にcontroleplane worker etcdの役割を詰め込みます。
また、デフォルトではx86_64用のイメージが設定に書き込まれるので、意図的に書き出されるファイル名をcluster.yaml.x86_64としています。

# rke config --name cluster.yaml.x86_64

ARM64v8対応

ARM64環境にデプロイできるように、上記をcluster.yamlにコピーして編集します。
また、v0.1.1だとingress-nginxのイメージの指定ができないのでprovider: noneにしています。

# cp cluster.yaml.x86_64 cluster.yaml
# vi cluster.yaml

ARM64サーバで動作させるためには、すべてのイメージをarm64ビルドしたものに変更する必要があります。
以下のイメージをarm64版として作成しました。(※あくまでPoC用のイメージです。ご注意ください)

  • infra_container_image: supersoftware/pause-arm64:3.0
  • etcd: mirrorgooglecontainers/etcd-arm64:3.2.14
  • alpine: arm64v8/alpine
  • nginx_proxy: supersoftware/rke-nginx-proxy-arm64:v0.1.1
  • cert_downloader: supersoftware/rke-cert-deployer-arm64:0.1.1
  • kubernetes_services_sidecar: supersoftware/rke-service-sidekick:v0.1.0
  • kubedns: supersoftware/k8s-dns-kube-dns-arm64:1.14.5
  • dnsmasq: supersoftware/k8s-dns-dnsmasq-nanny-arm64:1.14.5
  • kubedns_sidecar: supersoftware/k8s-dns-sidecar-arm64:1.14.5
  • kubedns_autoscaler: supersoftware/cluster-proportional-autoscaler-arm64:1.0.0
  • kubernetes: supersoftware/k8s-arm64:v1.8.7-rancher1-1
  • flannel: supersoftware/flannel-arm64:v0.9.1
  • flannel_cni: supersoftware/flannel-cni-arm64:v0.2.0

上記のイメージに差し替えた設定ファイルは以下になります。

cluster.yaml

nodes:
- address: <PUT_YOUR_NODES_IP_ADDRESS_HERE>
  internal_address: ""
  role:
  - controlplane
  - worker
  - etcd
  hostname_override: ""
  user: root
  docker_socket: /var/run/docker.sock
  ssh_key: ""
  ssh_key_path: ~/.ssh/id_rsa
  labels: {}
services:
  etcd:
    image: mirrorgooglecontainers/etcd-arm64:3.2.14
    extra_args: {}
  kube-api:
    image: supersoftware/k8s-arm64:v1.8.7-rancher1-1
    extra_args: {}
    service_cluster_ip_range: 10.233.0.0/18
    pod_security_policy: false
  kube-controller:
    image: supersoftware/k8s-arm64:v1.8.7-rancher1-1
    extra_args: {}
    cluster_cidr: 10.233.64.0/18
    service_cluster_ip_range: 10.233.0.0/18
  scheduler:
    image: supersoftware/k8s-arm64:v1.8.7-rancher1-1
    extra_args: {}
  kubelet:
    image: supersoftware/k8s-arm64:v1.8.7-rancher1-1
    extra_args: {}
    cluster_domain: cluster.local
    infra_container_image: supersoftware/pause-arm64:3.0
    cluster_dns_server: 10.233.0.3
    fail_swap_on: false
  kubeproxy:
    image: supersoftware/k8s-arm64:v1.8.7-rancher1-1
    extra_args: {}
network:
  plugin: flannel
  options: {}
authentication:
  strategy: x509
  options: {}
addons: ""
system_images:
  etcd: mirrorgooglecontainers/etcd-arm64:3.2.14
  alpine: arm64v8/alpine
  nginx_proxy: supersoftware/rke-nginx-proxy-arm64:v0.1.1
  cert_downloader: supersoftware/rke-cert-deployer-arm64:0.1.1
  kubernetes_services_sidecar: supersoftware/rke-service-sidekick:v0.1.0
  kubedns: supersoftware/k8s-dns-kube-dns-arm64:1.14.5
  dnsmasq: supersoftware/k8s-dns-dnsmasq-nanny-arm64:1.14.5
  kubedns_sidecar: supersoftware/k8s-dns-sidecar-arm64:1.14.5
  kubedns_autoscaler: supersoftware/cluster-proportional-autoscaler-arm64:1.0.0
  kubernetes: supersoftware/k8s-arm64:v1.8.7-rancher1-1
  flannel: supersoftware/flannel-arm64:v0.9.1
  flannel_cni: supersoftware/flannel-cni-arm64:v0.2.0
  calico_node: ""
  calico_cni: ""
  calico_controllers: ""
  calico_ctl: ""
  canal_node: ""
  canal_cni: ""
  canal_flannel: ""
  wave_node: ""
  weave_cni: ""
  pod_infra_container: ""
ssh_key_path: ~/.ssh/id_rsa
authorization:
  mode: rbac
  options: {}
ignore_docker_version: false
kubernetes_version: ""
private_registries: []
ingress:
  provider: none
  options: {}
  node_selector: {}

以上で準備が整いましたので、早速Kubernetesのデプロイを開始します!

# rke up

およそ30分ほどでデプロイが完了します。

デプロイが完了すると、kubectrl用のkube_config_cluster.ymlがカレントディレクトリに出力されます。
(※設定ファイル名によってファイル名が変わります。cluster.yml.bkの場合kube_config_cluster.yml.bk)
こちらを環境変数KUBECONFIGに指定してやれば、kubectlが読み込みます。
kutil/kutil:edgeにはkubectlをインストール済み&KUBECONFIG設定済みですので、そのまま使えます。

# kubectl get node,all --all-namespaces
NAME                STATUS    ROLES                AGE       VERSION
no/192.168.253.65   Ready     etcd,master,worker   8m        v1.8.7-rancher1-dirty

NAMESPACE     NAME              DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
kube-system   ds/kube-flannel   1         1         1         1            1           <none>          7m

NAMESPACE     NAME                         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kube-system   deploy/kube-dns              1         1         1            1           7m
kube-system   deploy/kube-dns-autoscaler   1         1         1            1           7m

NAMESPACE     NAME                                DESIRED   CURRENT   READY     AGE
kube-system   rs/kube-dns-6ffd65c86b              1         1         1         7m
kube-system   rs/kube-dns-autoscaler-65bbcbdc69   1         1         1         7m

NAMESPACE     NAME                         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kube-system   deploy/kube-dns              1         1         1            1           7m
kube-system   deploy/kube-dns-autoscaler   1         1         1            1           7m

NAMESPACE     NAME              DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
kube-system   ds/kube-flannel   1         1         1         1            1           <none>          7m

NAMESPACE     NAME                                DESIRED   CURRENT   READY     AGE
kube-system   rs/kube-dns-6ffd65c86b              1         1         1         7m
kube-system   rs/kube-dns-autoscaler-65bbcbdc69   1         1         1         7m

NAMESPACE     NAME                                 DESIRED   SUCCESSFUL   AGE
kube-system   jobs/rke-kubedns-addon-deploy-job    1         1            7m
kube-system   jobs/rke-network-plugin-deploy-job   1         1            8m

NAMESPACE     NAME                                      READY     STATUS    RESTARTS   AGE
kube-system   po/kube-dns-6ffd65c86b-vlr5f              3/3       Running   0          7m
kube-system   po/kube-dns-autoscaler-65bbcbdc69-l9vh6   1/1       Running   0          7m
kube-system   po/kube-flannel-d5wpp                     2/2       Running   0          7m

NAMESPACE     NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
default       svc/kubernetes   ClusterIP   10.233.0.1   <none>        443/TCP         9m
kube-system   svc/kube-dns     ClusterIP   10.233.0.3   <none>        53/UDP,53/TCP   7m

最後にingress-nginxのarm64版をkubectlでデプロイしておきます。
(※rkeの次のリリースではcluster.yamlでのイメージ指定だけで良くなっているはずです。)

# kubectl apply -f https://github.com/kyamazawa/kubernetes/releases/download/v1.8.7-rancher1/ingress-nginx-arm64.yaml
namespace "ingress-nginx" created
deployment "default-http-backend" created
service "default-http-backend" created
configmap "nginx-configuration" created
configmap "tcp-services" created
configmap "udp-services" created
deployment "nginx-ingress-controller" created
serviceaccount "nginx-ingress-serviceaccount" created
clusterrole "nginx-ingress-clusterrole" created
role "nginx-ingress-role" created
rolebinding "nginx-ingress-role-nisa-binding" created
clusterrolebinding "nginx-ingress-clusterrole-nisa-binding" created
# kubectl -n ingress-nginx get all
NAME                              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/default-http-backend       1         1         1            1           7m
deploy/nginx-ingress-controller   1         1         1            1           7m

NAME                                     DESIRED   CURRENT   READY     AGE
rs/default-http-backend-659bdd9f56       1         1         1         7m
rs/nginx-ingress-controller-7f7d4d7775   1         1         1         7m

NAME                              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/default-http-backend       1         1         1            1           7m
deploy/nginx-ingress-controller   1         1         1            1           7m

NAME                                     DESIRED   CURRENT   READY     AGE
rs/default-http-backend-659bdd9f56       1         1         1         7m
rs/nginx-ingress-controller-7f7d4d7775   1         1         1         7m

NAME                                           READY     STATUS    RESTARTS   AGE
po/default-http-backend-659bdd9f56-xstgf       1/1       Running   0          7m
po/nginx-ingress-controller-7f7d4d7775-pbn7z   1/1       Running   0          7m

NAME                       TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
svc/default-http-backend   ClusterIP   10.233.20.5   <none>        80/TCP    7m

上記のnginx-ingress-controllerはhostNetwork: trueに設定していますので、ブラウザでアクセスしてdefault-http-backendにルーティングされたら正常に動作しています。

ingress-nginx-defaultbackend

6. 出来上がったクラスタをRancher2.0にインポートする。

Raspberry Pi3にKubernetesがデプロイできましたので、Rancher2.0にインポートしてみます。
Add ClusterよりImport an Existing Clusterを選択します。

(いずれCreate a Clusterからラズパイにデプロイできる日がくると信じています...)

2-add-cluster

Read from a filekube_config_cluster.ymlを選択して、Importします。

3-import-existing-cluster

4-imported-cluster-list

4-imported-cluster-rpi

このようにkubernetesのコンフィグファイルを読み込ませることで、簡単に既存のクラスタをインポートすることができました! ちなみにworkloadsはこのようになっています。

7-arm64-rpi

まとめ

今回、rancher kubernetes用のdockerイメージやデプロイツールなど、いろいろなものを自前でカスタマイズして用意する必要がありましたが、なんとかRaspberry Pi3でARM64対応のKubernetesを立ち上げ、Rancher2.0にインポートすることができました。将来的にはARM64環境でももっと簡単にkubernetesが使えるようになったら、IoTなどでも利用されるようになったりして盛り上がっていくことを期待しています。

© 2018. SuperSoftware Co., Ltd. All Rights Reserved.