ラズパイでk8s構築する
April 30, 2019
k8s検証環境が欲しいけども、クラウドだとついつい停止し忘れて課金が悲惨なことになるので自宅に欲しいなぁ GWだしせっかくなのでラズパイでk8sクラスタを構築してしまおう
材料
全部Amazonで購入しました。
- Raspberry Pi 3 Model B+ × 3
- 積層式ケース × 1
- microSDHCカード 32GB × 3
- LANケーブル 0.3m × 3
- LANケーブル 0.15m × 1
- Micro USB ケーブル 0.2m × 5
- USB充電器 × 1
- スイッチングハブ × 1
- 無線ルーター × 1
物理的な組み立て
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 |
---|---|
master | 192.168.13.101 |
node-1 | 192.168.13.102 |
node-2 | 192.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を適用した様子です
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の資格とか挑戦してみたいところ