ラズパイでk8s構築する

ラズパイでk8s構築する

April 30, 2019
Kubernetes
kubernetes, RaspberryPi

k8s検証環境が欲しいけども、クラウドだとついつい停止し忘れて課金が悲惨なことになるので自宅に欲しいなぁ GWだしせっかくなのでラズパイでk8sクラスタを構築してしまおう

材料

全部Amazonで購入しました。

物理的な組み立て

3日間クッキング【Kubernetes のラズペリーパイ包み “サイバーエージェント風”】 を参考に、まずケースにラズパイを詰めていきます

参考ページにも書かれてますが、ケースのフィルムを剥がすのがとても大変です。 ぬるま湯に10分くらいつけておくと剥がしやすくなります。

無線ルーターの設定

PCから無線ルーターの設定を行います。

Wi-FiをBuffalo-x-xxxxに繋げてください。 パスワードは, 機器の裏に書かれている数字です

Wi-Fiを繋げた後に、 http://192.168.13.1/ へアクセスします

ユーザー名がadmin, パスワードはWi-Fi接続時と同じデータです。

設定ウィザードで次へを押下します 次の動作モード選択でワイヤレスランモードを選択して次へ 検索ボタンを押して、自宅のWi-Fiなどインターネットに繋がるWi-Fiを選択して、繋げてください。

設定が終わって、無線ルーターに繋げているPCからインターネットにアクセスできれば完了です

HypriotOSの用意

ラズパイで利用するOSをSDカードに書き込みます 今回はHypriotOSを使用します。 ラズパイ向けかつDockerに特化したOSらしいです。

imageのダウンロード

ここから最新のstable版をダウンロードします 今回はVersion 1.10.0を落としました。

MicroSDカードへ書き込み

こちらにmacでの手順が乗っています。

以下の作業を3つのMicroSDカードに対して実行してください

$ cd ~/Downloads
// ダウンロードしたzipファイルを解凍します
$ unzip hypriotos-rpi-v1.10.0.img.zip
Archive:  hypriotos-rpi-v1.10.0.img.zip
  inflating: hypriotos-rpi-v1.10.0.img

// SDカードが認識されているか確認
$ diskutil list
/dev/disk0 (internal):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                         251.0 GB   disk0
   1:                        EFI EFI                     314.6 MB   disk0s1
   2:                 Apple_APFS Container disk1         250.7 GB   disk0s2

/dev/disk1 (synthesized):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      APFS Container Scheme -                      +250.7 GB   disk1
                                 Physical Store disk0s2
   1:                APFS Volume Macintosh HD            155.3 GB   disk1s1
   2:                APFS Volume Preboot                 45.1 MB    disk1s2
   3:                APFS Volume Recovery                522.7 MB   disk1s3
   4:                APFS Volume VM                      3.2 GB     disk1s4

/dev/disk2 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *31.1 GB    disk2
   1:             Windows_FAT_32                         31.1 GB    disk2s1

// /dev/disk2がSDカードのようです
// SDカードをアンマウントします
$ diskutil unmountdisk /dev/disk2
Unmount of all volumes on disk2 was successful

// imageをSDに書き込みます, だいたい1分くらいかかります
$ sudo dd if=hypriotos-rpi-v1.10.0.img of=/dev/rdisk2 bs=1m
Password:
1000+0 records in
1000+0 records out
1048576000 bytes transferred in 60.963957 secs (17199933 bytes/sec)

ラズパイの初期設定

上記の無線ルーターが正常に動作している状態で, ラズパイの電源を入れてください
少し待てば、ルーターからDHCPでIPアドレスが割り当てられます.

arpコマンドでIPアドレスを確認することができます

$ arp -a
? (192.168.13.1) at 60:84:bd:74:8a:ec on en0 ifscope [ethernet]
? (192.168.13.4) at b8:27:eb:18:c1:b0 on en0 ifscope [ethernet]
? (192.168.13.5) at dc:a9:4:72:55:b7 on en0 ifscope permanent [ethernet]
? (224.0.0.251) at 1:0:5e:0:0:fb on en0 ifscope permanent [ethernet]
? (239.255.255.250) at 1:0:5e:7f:ff:fa on en0 ifscope permanent [ethernet]

$ ifconfig | grep 192
	inet 192.168.13.5 netmask 0xffffff00 broadcast 192.168.13.255
// 自分のアドレスが192.168.13.5なので、ラズパイは192.168.13.4のようです

DHCPのため、どのラズパイがどのIPに割り当てられたのかわからないので 1つづつやって行くのが良いかと思います

hypriotOSへの初期ログインはユーザー名:pirate, パスワード:hypriotです

ssh pirate@192.168.13.4
pirate@192.168.13.4's password:
Linux black-pearl 4.14.98-v7+ #1200 SMP Tue Feb 12 20:27:48 GMT 2019 armv7l

HypriotOS (Debian GNU/Linux 9)

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Mon Apr 29 17:08:06 2019 from 192.168.13.5
HypriotOS/armv7: pirate@black-pearl in ~
$

パッケージの更新を行います

$ sudo apt-get update \
  && sudo apt-get -y dist-upgrade \
  && sudo apt-get -y autoremove \
  && sudo apt-get autoclean

vimも矢印とか使えるように設定しておきます
一般ユーザーとrootユーザー両方で設定しておきます

$ echo "set nocompatible" > ~/.vimrc
$ sudo -i
# echo "set nocompatible" > ~/.vimrc

IPアドレスを固定します

sudo vi /etc/dhcpcd.conf

以下を末尾に追加します

interface eth0
static ip_address=192.168.13.10x/24
static routers=192.168.13.1
static domain_name_servers=192.168.13.1 8.8.8.8

static ip_address=192.168.13.10x/24の箇所は以下に合わせてください

ホスト名IP
master192.168.13.101
node-1192.168.13.102
node-2192.168.13.103

ホスト名もこの名前に変更します

/etc/hosts/etc/hostsを編集します

/etc/hostnameに自分のホスト名を入力してください /etc/hostsは3台とも共通で以下を,,127.0.0.1の行だけは自分のホスト名を入れてください

127.0.1.1 black-pearl.localdomain black-pearl
- 127.0.0.1 localhost
+ 127.0.0.1 localhost master
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts

+ 192.168.13.101 master
+ 192.168.13.102 node-1
+ 192.168.13.103 node-2

/etc/hostsはcloud-initの設定で毎回初期化されるので、/etc/cloud/cloud.cfgをいじって、- update_etc_hostsの行を削除します

-  - update_etc_hosts

ここまでやったら、各ラズパイを一度再起動させてください

$ sudo reboot

最後にSSH鍵を作成して、各ラズパイに配布しておきます

$ ssh-keygen -t rsa -f ~/.ssh/k8s-raspberry
$ ssh-copy-id -i ~/.ssh/k8s-raspberry pirate@192.168.13.101
$ ssh-copy-id -i ~/.ssh/k8s-raspberry pirate@192.168.13.102
$ ssh-copy-id -i ~/.ssh/k8s-raspberry pirate@192.168.13.103

~/.ssh/configに以下を追記しておくssh node-1というコマンドだけでログインできるようになります

Host master
    HostName 192.168.13.101
    User pirate
    Port 22
    IdentityFile ~/.ssh/k8s-raspberry
Host node-1
    HostName 192.168.13.102
    User pirate
    Port 22
    IdentityFile ~/.ssh/k8s-raspberry
Host node-2
    HostName 192.168.13.103
    User pirate
    Port 22
    IdentityFile ~/.ssh/k8s-raspberry

各サーバにもう一度ログインして、パスワードでのログインを無効化しておきましょう

$ sudo vi /etc/ssh/sshd_config
- #PasswordAuthentication yes
+ PasswordAuthentication no
$ sudo systemctl restart ssh

kubeletはswapが有効だと動かないそうです。 HypriotOSでは初期状態でSwapが無効だったので、特に作業はしていません. もし、Swapが有効化されていた場合は, /etc/fstabでswapと記載された行をコメントアウトして、再起動してください

k8sのインストール

下準備が済んだので、kubernetesを構築していきます 構築方法は色々あるのですが、ここではお手軽かつ、最新バージョンに追従しているkubeadmを使って構築します

ちなみにk8sのバージョンは1.14になります

kubeadmのインストール

https://kubernetes.io/docs/setup/independent/install-kubeadm/

HypriotOSを使っているので、すでにDockerはインストールされています. kubeadm関連のパッケージだけのインストールだけで構いません 3台すべてで以下を実行します

sudo -i
apt-get update && apt-get install -y apt-transport-https curl
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 https://apt.kubernetes.io/ kubernetes-xenial main
EOF
apt-get update
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl

インストールされたの確認

# kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.1", GitCommit:"b7394102d6ef778017f2ca4046abbaa23b88c290", GitTreeState:"clean", BuildDate:"2019-04-08T17:08:49Z", GoVersion:"go1.12.1", Compiler:"gc", Platform:"linux/arm"}

# kubectl version --client
Client Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.1", GitCommit:"b7394102d6ef778017f2ca4046abbaa23b88c290", GitTreeState:"clean", BuildDate:"2019-04-08T17:11:31Z", GoVersion:"go1.12.1", Compiler:"gc", Platform:"linux/arm"}

masterの起動

masterサーバにログインして、以下のコマンドを実行してください

$ sudo kubeadm init --pod-network-cidr 10.244.0.0/16

...
[bootstrap-token] creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following 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:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.13.101:6443 --token 9uijxn.efjcexvomvetwosk \
    --discovery-token-ca-cert-hash sha256:df59f36b7ad9c1816a01e3347dcaa14be2442df1ba2c76c58859c2e3b4d13a9e

表示されたコマンドでpirateユーザーからもクラスタに対して、kubectlを打てるようにしましょう

$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config
// 認識されているか確認, この時点ではSTATUSがNotReadyで構わない
$ kubectl get no
NAME     STATUS     ROLES    AGE    VERSION
master   NotReady   master   114s   v1.14.1

nodeの参加

最後に表示された、以下のコマンドをnodeのサーバで打つことで、クラスタに参加できます sudo kubeadm join 192.168.13.101:6443 --token 9uijxn.efjcexvomvetwosk \ --discovery-token-ca-cert-hash sha256:df59f36b7ad9c1816a01e3347dcaa14be2442df1ba2c76c58859c2e3b4d13a9e

打った後にmasterサーバでもう一度確認してみましょう

$ kubectl get no
NAME     STATUS     ROLES    AGE     VERSION
master   NotReady   master   4m20s   v1.14.1
node-1   NotReady   <none>   18s     v1.14.1
node-2   NotReady   <none>   18s     v1.14.1

追加されました。

CNIプラグインのインストール

上記手順でnode間の通信は行えるようになりました。 が、コンテナ間の通信を行うためにContainer Network Interface(CNI)をインストールする必要があります。 候補はいくつかあるのですが、ここではflannelを使います

https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/#pod-network

// デフォルトだとamd64向けになっているので、ラズパイが使っているarmに変更する
curl https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml \
| sed "s/amd64/arm/g" | sed "s/vxlan/host-gw/g" > kube-flannel.yml

$ kubectl apply -f kube-flannel.yml

StatusがReadyになりました。

$ kubectl get no
NAME     STATUS   ROLES    AGE   VERSION
master   Ready    master   9h    v1.14.1
node-1   Ready    <none>   9h    v1.14.1
node-2   Ready    <none>   80s   v1.14.1

Dashboardのデプロイ

アプリケーションのデプロイ確認も含めて、dashboardをデプロイします.

https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/ https://github.com/kubernetes/dashboard/releases/tag/v1.10.1

github見る感じ、まだ1.13以上のバージョンには対応していないように見えますが、一応は使えます

// flannel同様、arm向けに変更します
$ curl https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended/kubernetes-dashboard.yaml \
| sed "s/amd64/arm/g" > kubernetes-dashboard.yaml

$ kubectl apply -f kubernetes-dashboard.yaml

次にdashboardでの閲覧権限を持ったサービスアカウントを発行します

apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kube-system
$ kubectl apply -f service-account.yaml
// secret名を確認
$ kubectl -n kube-system get secret | grep admin-user
admin-user-token-g5vd9                           kubernetes.io/service-account-token   3      2m43s
// tokenの確認
$ kubectl -n kube-system describe secret admin-user-token-g5vd9
Name:         admin-user-token-g5vd9
Namespace:    kube-system
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: admin-user
              kubernetes.io/service-account.uid: 1e939d86-6b15-11e9-803e-b827eb18c1b0

Type:  kubernetes.io/service-account-token

Data
====
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLWc1dmQ5Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIxZTkzOWQ4Ni02YjE1LTExZTktODAzZS1iODI3ZWIxOGMxYjAiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06YWRtaW4tdXNlciJ9.Dpp_KZeTTPzHDAjYzuQr7PzxHLzJ1ViL_ct8cotBFBQOLwEAoVpESOiU_-SXvw9GPJSkhHLH6jv7iaKYC_LKtzg8Jj0C_jY3VuWMum7_Z_1RV9jjQcxuujLvKQZ4EZEixrf3GbMFFHfGYvVuC6Rx4EtFm-_CJ5iF07DRARoj2MoJoE86vQ31g64fFPFXwltMxMcIDpbsLzBIBOhdSDDBnprBJIyCj90UPfQgBssb-Y_PZKYoaVkb-tbG16tSJevvjpwgXilbH306XUZ6-CtzO2Bt85WdE5-14Qz-ySa2T0IPViwflac6b0_sR5aH_HAhmTQjUEve6cnyfxYvm-tkxQ
ca.crt:     1025 bytes
namespace:  11 bytes

ここで確認したtokenがdashboardのログイン時に必要になります

またこのままだと、localhostからしかアクセスできないので、serviceのtypeをNodePortに変更して、 外部からもアクセスできるようにします

https://github.com/kubernetes/dashboard/wiki/Accessing-Dashboard---1.7.X-and-above

$ kubectl -n kube-system edit service kubernetes-dashboard

このコマンドを打つとviでマニフェストの編集画面になるので、以下を変更します

spec:
  clusterIP: 10.100.124.90
  externalTrafficPolicy: Cluster
  ports:
  - port: 443
    protocol: TCP
    targetPort: 8443
  selector:
    k8s-app: kubernetes-dashboard
  sessionAffinity: None
-  type: ClusterIP
+  type: NodePort  
$ kubectl -n kube-system get service kubernetes-dashboard
NAME                   TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)         AGE
kubernetes-dashboard   NodePort   10.105.147.108   <none>        443:31888/TCP   16m

31888が割り当てられました。あとはmasterかnode-1,node-2のこのポートに対してhttpsでアクセスしてあげれば繋がります, サインインはTokenを選択して、上記手順で確認したadmin-userのTokenを入力してください

https://192.168.13.101:31888

上記の通り、まだk8s:1.14に対してdashboardは追いついていないせいか Chromeで見るとnamespaceの選択画面から次へいけませんでした。

Safariで見ると、それっぽく見えるようです

画像

上記は以下のnginx deploymentを適用した様子です

https://kubernetes.io/docs/tasks/run-application/run-stateless-application-deployment/#creating-and-exploring-an-nginx-deployment

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14
        ports:
        - containerPort: 80
kubectl apply -f nginx.yaml

構築失敗した場合

以下のコマンドでパッケージや設定を削除後、パッケージのインストール手順からやり直してください (kubeletの起動に失敗することがあります, 一度再起動を挟まないと設定が反映されないようなので、rebootコマンドとかを打ってみましょう)

$ sudo kubeadm reset
$ sudo apt-get purge kubeadm kubectl kubelet kubernetes-cni kube*   
$ sudo apt-get autoremove  
$ sudo rm -rf ~/.kube

最後に

自宅にk8s環境が構築できました! これで色々検証して、Certified Kubernetes Administratorの資格とか挑戦してみたいところ

参考にさせてもらったページ