以下の動機でKubernetes the hard way on Raspberry Piを実施。その際のメモ。
- CKAの勉強を兼ねて
- いざという時に備えて家に1つクラスタを持っておきたい
Key takeaway
- 事前に注意しておきたいハマる(かもしれない)ポイントが分かる(私の場合、環境設定で躓いた)
- 本記事内では公式ドキュメントにある手順について1つずつ追うことはしない
- Controller & Worker 両roleを持つノードをセットアップする方法が分かる(公式ドキュメント等に手順見当たらない)
- Metallbを使ってLoad Balancerを配備するところまでカバー
環境
- ハードウェア
- Controller & Worker
- Raspberry Pi 4 Model B 8GB RAM
- Worker
- Raspberry Pi 4 Model B 8GB RAM
- Raspberry Pi 3 Model B 1GB RAM
- Controller & Worker
- OS
- Ubuntu Server 20.04.2 LTS on Raspberry Pi
- 作業環境
- Ubuntu 18.04(WSL2) on Windows 10 21H1
連休前に思い立ち、フリマサイトで本体や冷却ファン等を買い足した。
参考にしたサイト
-
kelseyhightower/kubernetes-the-hard-way: Bootstrap Kubernetes the hard way on Google Cloud Platform. No scripts.
- 基本的には本家を読み進めるがGCP環境を前提とした手順になっているのでRaspberry Pi環境向けに読み替える必要がある箇所について、次のリファレンスを参考にさせてもらった。
- Nek0trkstr/Kubernetes-The-Hard-Way-Raspberry-Pi
- home-kubernetes-2020/how-to-create-cluster-logical-hardway at master · CyberAgentHack/home-kubernetes-2020
ハマったとこ
DHCP無効化
ノードのStatic IP設定方法がひと昔前の方法から変わっていてやや躓いたのでメモ。Netplanというものを初めて知る。各Worker上のPOD CIDRへのルーティングもNetplanを使って簡単に不揮発化できた。
Worker node上のswapの恒久的な無効化
swapが有効だとkubeletが動かない記載は何度か見ていたので、 sudo swapoff -a
で無効化はしていたが、これだと起動のたびに無効化が必要だったらしい。Worker node起動後、 kubectl get node
でノードが NotReady
だったので各種コンポーネントのログをチェックしていたら、kubeletのユニットログにswapをoffしろのメッセージを発見して気づく。以下のコマンドで恒久的にswapを無効化する。
sudo systemctl disable dphys-swapfile
Hostnameが重要
当初、controllerにraspberry piを1台、workerを残りの2台に割り当てたが、controllerもworkerとして動かしたく、controllerにworker向けのセットアップを追加で実施した。Controller向けに設定済みノードに対して、他のWorkerノードと同様のセットアップを施してみたがkubeletが動かない。kubeletのログを見ると、controller向けに命名していたhostnameをkubeletが混同して使っていて、API Serverにアクセスできないようで、エラーになっていた。そもそも、kube-controller
やkube-worker1
のようにノードのロールをHostnameに使っていたのが良くなかった(rpi1,rpi2みたいにしておいた方がいいと思われる)。
以下にWorker 3台構成が正しく動くまでの紆余曲折した構成の変遷を記載しておく。
Worker 2台構成(Hardway開始当初の構成)
HW | Node role | Hostname |
---|---|---|
Raspberry Pi 1 | Controller | kube-controller |
Raspberry Pi 2 | Worker | kube-worker1 |
Raspberry Pi 3 | Worker | kube-worker2 |
Worker 3台構成(3台目のWorker(kubelet)が動かなかった時の構成)
HW | Node role | Hostname |
---|---|---|
Raspberry Pi 1 | Controller & Worker | kube-controller |
Raspberry Pi 2 | Worker | kube-worker1 |
Raspberry Pi 3 | Worker | kube-worker2 |
Worker 3台構成(3台のWorker構成が無事動いた時の構成)
HW | Node role | Hostname |
---|---|---|
Raspberry Pi 1 | Controller & Worker | kube-worker0 |
Raspberry Pi 2 | Worker | kube-worker1 |
Raspberry Pi 3 | Worker | kube-worker2 |
読み替えた部分
Bootstrapping the etcd Cluster
etcdは、arm64版のバイナリがリリースされているのでそれを使う
Bootstrapping the Kubernetes Control Plane
- 各種コンポーネントのバイナリはarm64版に読み替え。wgetするURLはamd64をarm64に置換
- kube-apiserverの起動引数は本家サイトの最新版とRaspberry Pi向けサイトの情報をマージしたものを利用
cat <<EOF | sudo tee /etc/systemd/system/kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
[Service]
ExecStart=/usr/local/bin/kube-apiserver \\
--advertise-address=${INTERNAL_IP} \\
--allow-privileged=true \\
--apiserver-count=1 \\
--audit-log-maxage=30 \\
--audit-log-maxbackup=3 \\
--audit-log-maxsize=100 \\
--audit-log-path=/var/log/audit.log \\
--authorization-mode=Node,RBAC \\
--bind-address=0.0.0.0 \\
--client-ca-file=/var/lib/kubernetes/ca.pem \\
--enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\
--etcd-cafile=/var/lib/kubernetes/ca.pem \\
--etcd-certfile=/var/lib/kubernetes/kubernetes.pem \\
--etcd-keyfile=/var/lib/kubernetes/kubernetes-key.pem \\
--etcd-servers=https://${CONTROLLER0_IP}:2379 \\
--event-ttl=1h \\
--encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \\
--kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \\
--kubelet-client-certificate=/var/lib/kubernetes/kubernetes.pem \\
--kubelet-client-key=/var/lib/kubernetes/kubernetes-key.pem \\
--runtime-config='api/all=true' \\
--service-account-key-file=/var/lib/kubernetes/service-account.pem \\
--service-account-signing-key-file=/var/lib/kubernetes/service-account-key.pem \\
--service-account-issuer=https://${INTERNAL_IP}:6443 \\
--service-cluster-ip-range=10.32.0.0/24 \\
--service-node-port-range=30000-32767 \\
--tls-cert-file=/var/lib/kubernetes/kubernetes.pem \\
--tls-private-key-file=/var/lib/kubernetes/kubernetes-key.pem \\
--v=2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
Bootstrapping the Kubernetes Worker Nodes
runcとcontainerdは本家にarm64版のリリースがなかったのでLaunchpadからarm64版debパッケージをダウンロードして利用した。
- Download runc package for arm64 from launchpad
- Download containerd package for arm64 from launchpad
metallbのセットアップ
公式ドキュメントに従って特に躓くこともなく設定できた。
まず以下のマニフェストをインストールする。
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.10.2/manifests/namespace.yaml
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.10.2/manifests/metallb.yaml
metallb用のconfigmapをapplyするまでアイドル状態とのこと。以下のconfigmapをapplyする。あらかじめホームネットワークのDHCPリース範囲を確認し、使われる予定のないIPアドレスの範囲を確認しておく(私の場合、192.168.2.100-192.168.2.200がDHCPのリース範囲内だった)。
{
cat << EOF | kubectl apply -n metallb-system -f -
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.2.2-192.168.2.99
EOF
}
以上である。以下のサンプルdeploymentで動作確認だけしておく。
{
kubectl create namespace kube-verify
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: kube-verify
namespace: kube-verify
labels:
app: kube-verify
spec:
replicas: 3
selector:
matchLabels:
app: kube-verify
template:
metadata:
labels:
app: kube-verify
spec:
containers:
- name: nginx
image: nginx:1.21.1
EOF
}
このdeploymentをLoadBalancer typeでサービス公開する。
kubectl expose deployment kube-verify -n kube-verify --type=LoadBalancer --target-port=80 --port=80
service/kube-verify exposed
kubectl get service kube-verify -n kube-verify
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-verify LoadBalancer 10.32.0.205 192.168.2.2 80:31865/TCP 4s
configで指定した範囲内のIPアドレスがさっそく払い出されていることがわかる。 http://192.168.2.2 にアクセスするとクラスタ上のサービスにアクセスできることがわかる。
最後に
Kubernetes the hard wayは思っていたより、簡単にできた。オリンピックを観ながら設定していたのでミスが色々あったが、そのおかげでトラブルシューティングをやるチャンスができ、理解をより深めることができたと思う。物理的な構築にはあまりこだわらなかったが、あまり広げて散らかすと家庭内からクレームがでる恐れがあるので、それなりにこじんまりさせることも重要と思う。