RaspberryPiでKubernetesクラスタを構築
1.はじめに
今回はラズパイでKubernetesクラスタを構築してみたいと思います。
OSはUBUNTU SERVER 22.04.2 LTS(64-BIT)を使用し、全てのラズパイにインストール済みのところから始めます。OSのインストール方法は他の記事を参照してください。
また、クラスタの構成は以下のようになります。
2.k8sクラスタを構築するための予備知識
まず、クラスタ構築の前に必要となる知識を整理していきたいと思います。
- kubectl
KubernetesのCLIです。 - kube-apiserver
Kubernetes APIを提供するコンポーネントです。kubectlを使ってkube-apiserverに対してリクエストを送り、Kubernetesのリソースを管理します。 - kubelet
コンテナランタイムと連携し、コンテナを管理します。 - コンテナランタイム
高レベルコンテナランタイムと低レベルコンテナランタイムがあります。- 高レベルコンテナランタイム
CRI(Container Runtime Interface)と呼ばれる規格でkubeletとのやり取りが行われます。また、高レベルコンテナランタイムは、コンテナイメージを管理する役割を持ちます。さらに、低レベルコンテナランタイムに対してコンテナ作成の命令を送ります。代表的な高レベルコンテナランタイムにcontainerdやcri-oがあります。 - 低レベルコンテナランタイム
OCI(Open Container Initiative)と呼ばれる規格で高レベルコンテナランタイムとのやり取りが行われます。また、低レベルコンテナランタイムは、実際にコンテナの生成、実行、停止、削除の役割を担います。代表的な低レベルコンテナランタイムにrunCやgVisorがあります。
- 高レベルコンテナランタイム
今回のKubernetesクラスタの構築では、高レベルコンテナランタイムにcontainerd、低レベルコンテナランタイムにrunCを使用します。
3.事前準備
3.1.秘密鍵と公開鍵を作成
$ ssh-keygen -t rsa -b 4096
~/.ssh
├ id_rsa # 秘密鍵
└ id_rsa.pub # 公開鍵
3.2.公開鍵を送る
ラズパイの方に公開鍵を送ります。
$ ssh-copy-id -i ~/.ssh/id_rsa.pub user-name@XXX.XXX.XXX.XXX # XXX.XXX.XXX.XXXはラズパイのIPアドレス
ラズパイのIPアドレスが見当たらない場合は、こちらの方法で送ります。
$ ssh-copy-id -i ~/.ssh/id_rsa.pub user-name@host-name.local
3.3.接続
秘密鍵を指定してラズパイに接続します。
$ ssh -i id_rsa user-name@XXX.XXX.XXX.XXX
同じくラズパイのIPアドレスが見当たらない場合は、こちらの方法で接続します。
$ ssh -i id_rsa user-name@host-name.local
3.4.IPアドレスを固定
/etc/netplan/99_config.yamlを作成しIPアドレスを固定します。
$ vi /etc/netplan/99_config.yaml
network:
version: 2
ethernets:
eth0:
dhcp4: false
addresses:
- 192.168.13.101/24
nameservers:
addresses:
- 8.8.8.8
編集したらnetplanコマンドで反映させます。
$ sudo netplan apply
他のNodeにも接続してIPアドレスを以下のように設定しておきます。
ホスト名 | IPアドレス | 役割 |
---|---|---|
k8s-master | 192.168.13.101 | Master Node |
k8s-node1 | 192.168.13.102 | Worker Node |
k8s-node2 | 192.168.13.103 | Worker Node |
3.5.エイリアス作成
簡単にssh接続できるようにエイリアスを作成しておきます。
$ vi ~/.ssh/config
Host k8s-master # エイリアス名
HostName 192.168.13.101 # 接続先IPアドレス
User user-name # 接続先ユーザ名
IdentityFile ~/.ssh/id_ras # 秘密鍵
Host k8s-node1
HostName 192.168.13.102
User user-name
IdentityFile ~/.ssh/id_ras
Host k8s-node2
HostName 192.168.13.103
User user-name
IdentityFile ~/.ssh/id_ras
$ ssh k8s-master
4.k8sクラスタの構築
4.1.linux-modules-extra-raspiのインストール
複数のKubernetes Node上に跨るPod間のネットワーク通信を機能させるために、CNI(Container Network Interface)Pluginと呼ばれるものがあります。linux-modules-extra-raspiをインストールすることで、CNIが正常に働きPod間のネットワーク通信が上手く機能するそうです。因みに今回はCNIにFlannelを使います。(後述)
$ apt install -y linux-modules-extra-raspi
$ reboot now
4.2.カーネル
Kubernetesコンテナランタイムを参照しながら進めていきます。
4.2.1.カーネルモジュールのロード
カーネルモジュールは小さなコードの集まりであり、必要に応じてカーネルにロード・アンロードすることができます。カーネルモジュールはシステムを再起動する必要なくカーネルの機能を拡張します。
overlay、br_netfilterモジュールが起動時に読み込まれるようにします。
# confファイルに読み込みたいモジュールを明示
$ cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
# カーネルモジュールをロード
$ sudo modprobe overlay
$ sudo modprobe br_netfilter
4.2.2.カーネルパラメータの設定
カーネルにパラメータを渡して、カーネルの動作を制御します。
# カーネルパラメーターを永続的に設定
$ cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
# カーネルパラメーターを適用
$ sudo sysctl --system
4.2.3.確認
br_netfilter、overlayモジュールがロードされていることを確認します。
$ lsmod | grep br_netfilter
br_netfilter 32768 0
bridge 319488 1 br_netfilter
$ lsmod | grep overlay
overlay 155648 0
カーネルパラメーターの設定が1であることを確認します。
$ sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
4.3.コンテナランタイムのインストール
4.3.1.containerdのインストール(高レベルコンテナランタイム)
containerd step1を参照しながら進めていきます。
# containerdのバイナリをダウンロード
$ curl -LO https://github.com/containerd/containerd/releases/download/v1.7.2/containerd-1.7.2-linux-arm64.tar.gz
# 展開
$ tar Cxzvf /usr/local containerd-1.7.2-linux-arm64.tar.gz
# バイナリが置かれていることを確認
$ ls -la /usr/local/bin/containerd*
/usr/local/bin/containerd
/usr/local/bin/containerd-shim
/usr/local/bin/containerd-shim-runc-v1
/usr/local/bin/containerd-shim-runc-v2
/usr/local/bin/containerd-stress
$ mkdir -p /usr/local/lib/systemd/system
# unitファイルをダウンロード
$ curl -L https://raw.githubusercontent.com/containerd/containerd/main/containerd.service > /usr/local/lib/systemd/system/containerd.service
# unitファイルが置かれていることを確認
$ ls -la /usr/local/lib/systemd/system/containerd.service
/usr/local/lib/systemd/system/containerd.service
# unitファイルを再読み込み
$ systemctl daemon-reload
# unitファイルを反映
$ systemctl enable --now containerd
# 反映できているか確認
$ systemctl is-active containerd
active
4.3.2.runcのインストール(低レベルコンテナランタイム)
containerd step2を参照しながら進めていきます。
# runcのバイナリをダウンロード
$ curl -LO https://github.com/opencontainers/runc/releases/download/v1.1.7/runc.arm64
# インストール
$ install -m 755 runc.arm64 /usr/local/sbin/runc
# バイナリが置かれていることを確認
$ ls -la /usr/local/sbin/runc
/usr/local/sbin/runc
4.4.CNI pluginsのインストール
containerd step3を参照しながら進めていきます。
$ mkdir -p /opt/cni/bin
# CNI pluginsのバイナリをダウンロード
$ curl -LO https://github.com/containernetworking/plugins/releases/download/v1.3.0/cni-plugins-linux-arm-v1.3.0.tgz
# インストール
$ tar Cxzvf /opt/cni/bin cni-plugins-linux-arm-v1.3.0.tgz
# バイナリが置かれていることを確認
$ ls /opt/cni/bin
bandwidth bridge dhcp dummy firewall flannel host-device host-local ipvlan loopback macvlan portmap ptp sbr static tap tuning vlan vrf
4.5.systemd cgroupドライバーの設定
Kubernetesコンテナランタイムを参照しながら進めていきます。
$ mkdir /etc/containerd
# containerdにデフォルトの設定を適用
$ containerd config default > /etc/containerd/config.toml
# containerdにsystemd cgroupドライバーを適用
# /etc/containerd/config.tomlのSystemdCgroupをtrueに変更
vi /etc/containerd/config.toml
...
SystemdCgroup = true
...
# containerdを再起動
$ systemctl restart containerd
# 動いているか確認
$ systemctl is-active containerd
active
4.5.kubectl、kubelet、kubeadmのインストール
kubeadm、kubelet、kubectlのインストールを参照しながら進めていきます。
$ sudo apt update
$ sudo apt install -y apt-transport-https ca-certificates curl
$ curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-archive-keyring.gpg
$ echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
$ sudo apt update
$ sudo apt install -y kubelet kubeadm kubectl
$ sudo apt-mark hold kubelet kubeadm kubectl
kubeletによって使用されるcgroupドライバーを設定します。
$ vi /etc/default/kubelet
# 以下のように設定
KUBELET_EXTRA_ARGS=--cgroup-driver=systemd
4.6.k8sクラスタ構築
$ kubeadm init --node-name master --apiserver-advertise-address=192.168.13.101 --pod-network-cidr=10.244.0.0/16
# 以下のように出力される
Your Kubernetes control-plane has initialized successfully!
...
kubeadm join ...
kubectlをrootユーザ以外でも使えるようにしておきます。
$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config
Worker Nodeに必要なものをインストールした後にkubeadm join ...をWorker Nodeで実行します。
$ kubeadm join 192.168.13.101:6443 --node-name XXXXX --token XXXXX --discovery-token-ca-cert-hash XXXXX
今回のクラスタ構築では、flannelを使いPod間のネットワークが機能するようにします。
$ kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
4.7.kubeconfigの設定
手元にあるPCからkubectlでMaster Nodeと通信できるようにします。kubectlは$HOME/.kube/configに書かれている情報を用いて接続が行われます。
# 手元のPCで実行
$ mkdir -p $HOME/.kube
$ scp k8s-master:/etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config
5.完成
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready control-plane 15d v1.27.3
node1 Ready <none> 14d v1.27.3
node2 Ready <none> 14d v1.27.3
$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
master Ready control-plane 15d v1.27.3 192.168.13.101 <none> Ubuntu 22.04.2 LTS 5.15.0-1033-raspi containerd://1.7.2
node1 Ready <none> 14d v1.27.3 192.168.13.102 <none> Ubuntu 22.04.2 LTS 5.15.0-1033-raspi containerd://1.7.2
node2 Ready <none> 14d v1.27.3 192.168.13.103 <none> Ubuntu 22.04.2 LTS 5.15.0-1033-raspi containerd://1.7.2
6.Nodeをクラスタから除外
Nodeを除外したいときは、Nodeを削除した後にkubeadm resetを実行してください。
# Master Nodeで指定するNodeを削除
$ kubectl delete node <node name>
# 削除したNodeで実行
$ kubeadm reset
7.おわりに
クラウドを使えばクラスタは一瞬で構築できると思いますが、自前のクラスタを構築することでKubernetesやコンテナの理解が深まりました。今後もKubernetesは最強のコンテナオーケストレーションであり続けると思うので、積極的に触れていきたいです。
また、Kubernetesやコンテナの活躍の場は非常に幅広く、F16戦闘機や自動車にも搭載されるほどです。これからの技術的発展が非常に楽しみです。
参考
https://github.com/containerd/containerd
https://github.com/cri-o/cri-o
https://github.com/opencontainers/runc
https://github.com/google/gvisor
https://ubuntu.com/server/docs/network-configuration
https://qiita.com/yoshimi0227/items/3cac86915d47a2b788cd
https://manpages.ubuntu.com/manpages/jammy/man5/modules-load.d.5.html
https://manpages.ubuntu.com/manpages/jammy/man5/sysctl.d.5.html
https://wiki.archlinux.jp/index.php/カーネルモジュール
https://zenn.dev/ttnt_1013/articles/f36e251a0cd24e
https://qiita.com/kentarok/items/6e818c2e6cf66c55f19a
https://skanehira.github.io/this-week-in-gorilla/articles/raspberry-pi-cluster.html
https://kubernetes.io/docs/reference/setup-tools/kubeadm/
https://github.com/flannel-io/flannel
Discussion