この記事で利用しているrak8sは更新されていませんので、Raspberry Pi上にKubernetes環境を構築する場合には、k3s を利用してください。
はじめに
Raspberry Piを利用してKubernetesクラスターを構築してみようと調べたところ、rak8s.ioに行きあたったので試してみました。
結果としては、Kubernetesのバージョンが古かったり、微妙な不具合があったので、ansible taskを少し修正するなどしています。
環境
このセクションの作業は、ノートPCなどのコンピュータ側で行ないます。
ansible実行環境
- Ubuntu 18.04.2 LTS (amd64版) on VMware Workstation 15
- PPA - Ansible
deb http://ppa.launchpad.net/ansible/ansible/ubuntu bionic main
deb-src http://ppa.launchpad.net/ansible/ansible/ubuntu bionic main
Kubernetes用
- Raspberry Pi 3 and 3+ (IPv4 address: 192.168.1.77/24, 192.168.1.78/24)
- Raspbian Lite (2018-11-13版)
rak8s.ioのgithubリポジトリをforkしたリポジトリ(https://github.com/YasuhiroABE/rak8s)を公開しています。
次の要領で利用できます。
$ git clone https://github.com/YasuhiroABE/rak8s.git
$ cd rak8s
$ git checkout -b v1.13.5 origin/v1.13.5
ansible-playbookコマンドを実行するための準備
この作業はRaspberry Piの全台で実施してください。
静的IPアドレスの設定
手元の環境はdnsmasqを動かしているので、MACアドレスから固定IPを割り当てるようにしています。
DHCPサーバーに手を入れることができないのであれば、/etc/dhcpcd.confファイルを変更します。
# Example static IP configuration:
interface eth0
static ip_address=192.168.0.10/24
static routers=192.168.0.1
static domain_name_servers=192.168.0.1
$ ip addr
コマンドでIPアドレスが意図したアドレスに変更されていない場合は、確認を兼ねて再起動がお勧めです。
Locale等の変更
配布されているRaspbian Liteのイメージから起動すると、sshサーバーは無効化されています。
そのため、あらかじめsshサーバーを起動しておくか、初回はHDMIケーブルとキーボードを接続し、raspi-configコマンド等からsshを有効化しておきます。
$ sudo raspi-config
起動した後は、5.Interfacing Optionsを選択し、2番目のSSHからYESを選択し、サーバーを起動させます。
Raspbian Liteのデフォルトロケールはイギリス英語なので、en_US.UTF-8がデフォルトになるように、4. Localisation Optionsを選択します。
遷移した画面で、Localeを変更し、en_GB.UTF-8を外し、en_US.UTF-8がデフォルトになるように変更しています。
お勧め設定として、GPUメモリをデフォルトの64MBから16MBに変更することが https://rak8s.io/ に記載されています。
この設定をしないとansible-playbookを実行した時にエラーになるので、変更しておきます。
"7. Advanced Options"に進み、"A3. Memory Split"メニューから変更します。
Timezoneについては、ansibleがUTCに変更するので、何も設定していません。
authorized_keysファイルの配置
パスワードなしにsshで実行できるように、ansible実行環境のUbuntuにある公開鍵をコピーしています。
今回はed25519形式を利用したかったので、新たに鍵を生成しています。
Raspberry Pi側で何もしていないと~/.ssh/ディレクトリが存在していないので、作成し、パスワードを入力して公開鍵を適切な場所にコピーしています。
$ ssh-keygen -t ed25519
...
$ ssh pi@192.168.1.77 mkdir --mode=700 .ssh
$ scp ~/.ssh/id_ed25519.pub pi@192.168.1.77:.ssh/authorized_keys
$ ssh pi@192.168.1.78 mkdir --mode=700 .ssh
$ scp ~/.ssh/id_ed25519.pub pi@192.168.1.78:.ssh/authorized_keys
kernelのアップデート
デフォルトの4.14.98-v7+から、4.19.30-v7+に変更しています。
$ sudo rpi-update
ansibleによるkubernetes環境の構築
ここからの作業はノートPCなどのコンピュータ側で行ないます。
あらかじめrak8sのディレクトリに移動しておきます。
inventoryファイルの変更
まず、inventoryファイルを変更します。
[prod]
rak8s002 ansible_host=192.168.1.79
rak8s003 ansible_host=192.168.1.80
[master]
rak8s002
group_vars/all.yml ファイルの修正
バージョンの指定を最新のv1.13.4に合わせます。
docker-ceは18.09でも動作確認はしているはずですが、ここでは18.06を指定しています。
all.ymlファイルにはpodnet変数が設定されていますが、途中で変更するとcleanup.ymlを使用しても、正常に変更が反映されずに残るようなので気をつけてください。
diff --git a/group_vars/all.yml b/group_vars/all.yml
index a4eb864..54053ff 100644
--- a/group_vars/all.yml
+++ b/group_vars/all.yml
@@ -1,7 +1,7 @@
token: udy29x.ugyyk3tumg27atmr
podnet: 10.244.0.0/16
-kubernetes_package_version: "1.11.4-00"
+kubernetes_package_version: "1.13.4-00"
# Available versions:
# 1.10.3-00
# 1.10.2-00
@@ -12,7 +12,7 @@ kubernetes_package_version: "1.11.4-00"
# 1.9.6-00
# 1.9.5-00
-kubernetes_version: "v1.11.4"
+kubernetes_version: "v1.13.4"
# Available versions:
# v1.10.3
# v1.10.2
@@ -23,7 +23,7 @@ kubernetes_version: "v1.11.4"
# v1.9.6
# v1.9.5
-docker_ce_version: "18.04.0~ce~3-0~raspbian"
+docker_ce_version: "18.06.3~ce~3-0~raspbian"
# Available versions:
# 18.05.0~ce~3-0~raspbian
# 18.04.0~ce~3-0~raspbian
roles/master/tasks/main.ymlファイルの修正
v1.13.4を前提にtask/main.ymlファイルを修正しています。
diff --git a/roles/master/tasks/main.yml b/roles/master/tasks/main.yml
index 99124bc..e957007 100644
--- a/roles/master/tasks/main.yml
+++ b/roles/master/tasks/main.yml
@@ -5,7 +5,7 @@
register: kubeadm_reset
- name: "Initialize Master {{ kubernetes_version }}"
- shell: kubeadm init --apiserver-advertise-address={{ ansible_default_ipv4.address }} --token={{ token }} --kubernetes-version={{ kubernetes_version }} --pod-network-cidr={{ podnet }}
+ shell: kubeadm init --apiserver-advertise-address={{ ansible_default_ipv4.address }} --token={{ token }} --kubernetes-version={{ kubernetes_version }} --pod-network-cidr={{ podnet }} --ignore-preflight-errors=SystemVerification
register: kubeadm_init
when: kubeadm_reset is succeeded
@@ -34,7 +34,7 @@
register: kubeadm_join
- name: Install Flannel (Networking)
- shell: "curl -sSL https://rawgit.com/coreos/flannel/{{ flannel_version }}/Documentation/kube-flannel.yml | sed 's/amd64/arm/g' | kubectl create -f -"
+ shell: "curl -sSL https://raw.githubusercontent.com/coreos/flannel/{{ flannel_version }}/Documentation/kube-flannel.yml | sed 's/amd64/arm/g' | kubectl create -f -"
- name: Poke kubelet
systemd:
ansible-playbookコマンドの実行の前に
これまで試したところ、直接 ansible-playbook コマンドを実行すると、python-aptのタスクで停止しました。
そのため事前にapt-getコマンドを実行しています。
rak8sディレクトリの中にいる状態(カレントディレクトリにansible.cfgファイルがる状態)で、次のコマンドを実行します。
$ ansible all -m command -b -a "apt-get update"
$ ansible all -m command -b -a "apt-get -y dist-upgrade"
いろいろ変更されていると思うので、ここで再起動しておきます。
$ ansible all -m command -b -a "shutdown -r now"
ansible-playbookコマンドの実行
引き続き、ansible実行側の環境で作業を進めます。
$ ansible-playbook cluster.yml
初回の起動は次のようなメッセージがでて、いくつかTaskが実行されて失敗するので、ノードを再起動してから続けます。
TASK [common : Reboot Message] *******************************************************************************
ok: [rak8s002] => {
"msg": "A reboot is required but the reboot module is a little wonky. Hopefully someone fixes this soon."
}
...
TASK [kubeadm : Run Docker 18.06.3~ce~3-0~raspbian Install Script] *******************************************
fatal: [rak8s002]: FAILED! => ...
... OR ...
TASK [master : Initialize Master v1.13.5] ********************************************************************
fatal: [rak8s000]: FAILED! => ...
最初のapt dist-upgradeの後で再起動する・しない、といった状況によって停止する場所は違いました。
再起動はansibleコマンドを使っています。
$ ansible all -m command -b -a "shutdown -r now"
しばらく待ってノードの稼動を$ ansible all -m ping
などで確認しつつ、再度ansible-playbookコマンドを実行します。
$ ansible-playbook cluster.yml
確認した環境では、これでエラーなく完了しています。
稼動確認
podはあっても起動しているか、再起動を繰り返していないか、きちんと確認しておく必要があります。
最初にrak8sを利用した時は、いろいろ動かないところがあって苦労しました。
ただ全体が起動するまでに8分前後の時間がかかっています。
作業を簡単にするために、以下のようなaliasを利用しています。
alias kc="sudo kubectl -n kube-system"
alias kcg="kc get"
alias kcga="kcg all"
alias kcgan="kcga --all-namespaces"
alias kcd="kc describe"
2回目のansible-playbook cluster.yml
が終わった後で、5分程度経過してからkubectlコマンドを実行した結果は次のようになりました。
$ . ~/k8s.envrc
$ kcg pod ## "sudo kubectl -n kube-system get pod" のエイリアス
NAME READY STATUS RESTARTS AGE
coredns-86c58d9df4-7bkfz 1/1 Running 0 3m51s
coredns-86c58d9df4-jzggq 1/1 Running 0 3m51s
etcd-rak8s000 1/1 Running 0 3m44s
kube-apiserver-rak8s000 1/1 Running 1 4m7s
kube-controller-manager-rak8s000 1/1 Running 0 3m44s
kube-flannel-ds-arm-5hss9 1/1 Running 1 3m15s
kube-flannel-ds-arm-7vxwg 1/1 Running 0 3m40s
kube-proxy-frfhx 1/1 Running 0 3m51s
kube-proxy-v9fgh 1/1 Running 0 3m15s
kube-scheduler-rak8s000 1/1 Running 0 3m44s
ここでRESTARTSのカウントが上がり続けないか、2〜3分間隔を開けて確認します。
問題がなければ、coreDNSを中心に、きちんとコンテナが稼動することを確認するために、metallbとnginxをデプロイしてみます。
metaillbのデプロイメント
あらかじめaliasを設定するためのファイルを準備しておきます。
test -f ~/k8s.envrc && . ~/k8s.envrc
alias kc="sudo kubectl -n metallb-system"
実際の作業は次のようになっています。
$ . ~/k8s.envrc.metallb
$ kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.7.3/manifests/metallb.yaml
## 適当なエディタで次のような内容のlayer2-config.yamlファイルを作成する
$ cat layer2-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.1.83-192.168.1.84
$ kc apply -f layer2-config.yaml
$ kcga
NAME READY STATUS RESTARTS AGE
pod/controller-7cc9c87cfb-wgn82 1/1 Running 0 12m
pod/speaker-5zp6x 1/1 Running 0 12m
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/speaker 1 1 1 1 1 <none> 12m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/controller 1/1 1 1 12m
NAME DESIRED CURRENT READY AGE
replicaset.apps/controller-7cc9c87cfb 1 1 1 12m
nginxによるmetallbの稼動確認
metallbと同様にaliasを設定するためのファイルを準備します。
test -f ~/k8s.envrc && . ~/k8s.envrc
alias kc="sudo kubectl -n nginx"
実際の作業は次のようになっています。
$ . ~/k8s.envrc.nginx
$ kc create ns nginx
$ cat deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: nginx
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.15
ports:
- containerPort: 80
$ kc apply -f deployment.yaml
$ cat service.yaml
---
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: nginx
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: LoadBalancer
$ kc apply -f service.yaml
$ kcga
NAME READY STATUS RESTARTS AGE
pod/nginx-deployment-5fc86c987f-xcl8c 1/1 Running 0 13m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx LoadBalancer 10.105.192.198 192.168.1.83 80:30043/TCP 13m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 1/1 1 1 13m
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-deployment-5fc86c987f 1 1 1 13m
EXTERNAL-IPがきちんとConfigMAPで指定したレンジから割り振られていれば完了です。
この他のTips
rak8sでは毎回$ sudo kubectl ...
とsudoで指定していますが、/root/.kube/config には必要なファイルはansibleで配置されています。
piユーザーのままでkubectlをsudoなしに実行したければ、同様にファイルをコピーすれば可能です。
$ mkdir -m 700 ~/.kube/
$ sudo cp /root/.kube/config ~/.kube/config
$ sudo chown -R pi:pi ~/.kube/.
以上