はじめに
世の中ではGrafana Weekということで、Raspberry Pi 5複数台をクラスタリングしてKubernetesを作成し、Grafanaを載せてみたいと思います。
というのは冗談ですが、最近趣味で安価に常駐プロセスをデプロイできるホスティング環境に悩んでいました。常駐しないなら最近はゼロコールドスタートなV8 Isolateを使ったCloudflare WorkersやDeno Deployが無料枠が大きくいい感じです。 一方常駐プロセスはHerokuの無料プランがなくなりました。AWS AppRunnerは起動時間を人間が稼働している時間のみに絞っても10$はかかります。fly.ioは、Legacy hobby planでCPU-1x 256mb VM 3つと3 GB 永続ボリュームストレージは無料で扱えます。fly.ioはCLIもよくできているので、軽い検証の場合こちらで良さそうです。
より永続化領域とメモリが潤沢な環境を求めて、自前でコンテナオーケストレーション基盤を作ることにしました。
Kubernetesクラスタ自体は4年前くらいにRPi3で作ったことがあります。エコシステム的な難しさもあり、早々に飽きてしまったので今回はそこそこ真面目に構築しました。実務的で使ったことはないので、経験者からすると拙いのとN番煎じですが、ご参考にして頂けたら幸いです。
以下の点ご理解お願いします。
- クラウドでサーバレス開発をしているので、電気やハード的なところはあまり参考にならないと思います
- 本構成は動作を保証しません。特に電気的な部分は、他の記事と合わせて確認するのが良いと思います
以後Raspberry Pi 5は、RPi5と表記します。
構成
ゴール
Mac(Kubernetesクラスタ外)からkubectl port-forward
なしで、Grafanaが閲覧できるようになることを目指します。こんな形です。
注意点
本項以降の各手順は、最小2台で進めることを推奨します。理由は、一気に4台でやるよりは、2台でgrafana起動まで確認し、その後2ノード追加する方が何か手戻りがあった際にやり直しがしやすいためです。
前提
- PoEは利用しない(RPi5ではまだPoE HATがないため)
- RPi5のストレージには NVMe M.2 SSDを利用
- microSDで代用可能です、RPi5側で対応しているのと家に余っていたので活用しました
- RPi5のイメージには、Ubuntu Server 24.04 LTS(64bit)を利用
- 電源はスイッチング電源2台で、各スイッチング電源に2つのRPi5を接続
項 | 名称 | 個数 | 補足 | リンク |
---|---|---|---|---|
1 | Raspberry Pi 5 8GB | 4 | RPi5本体 | リンク |
2 | Raspberry Pi 5用アクティブクーラー | 4 | RPi5のクーラー | リンク |
3 | Geekworm X1001 V1.1 PCIe NVMe M.2 拡張ボード | 4 | RPi5とM.2.を接続するための拡張ボード | リンク |
4 | KIOXIA EXCERIA G2 SSD-CK1.0N3G2/N 1TB | 4 | M.2. M.2 NVMe SSD。正確には家に余っている別メーカーのものもありますが大きな差はないので割愛。 | リンク |
5 | ORICO M.2 SSD 外付けケース | 1 | NVMe SSDへイメージを書き込むための外付けケース | リンク |
6 | スイッチング電源 DC5V10A 出力50Wタイプ RWS50B-5 | 2 | RPi5は5V/5Aなので、1つあたりRPi52台接続想定。GPIO 5Vピン2つから電源供給 | リンク |
7 | Qiコネクタ | 1 | スイッチング電源からGPIO経由で電源供給するため利用 | リンク |
8 | コネクタ用ハウジング | 4 | 上記のQiコネクトを半分に切って、ケーブル4本を1つのハウジングに納めて計4本作ります | リンク |
9 | TP-Link スイッチングハブ 5ポート PoE+ | 1 | PoE+は利用しません | リンク |
10 | Type C ケーブル 0.5M 2本組 | 2 | 2本組を2つ購入。計4本 | リンク |
11 | LANケーブル 0.3m 5本 | 4 | 5本組を1つ購入。計5本 | リンク |
12 | microSD 32GB | 1 | ブート設定を変更するためだけに利用します。恒常的に利用しません。 | なし |
13 | microSD読めるカードリーダー | 1 | なし | |
14 | キッチンラック | 1 | ラズパイを積む用のラック。専用のものは高かったので、キッチンラックで代用しました...RPi5の裏面の導通ある部分がメタルラックに接触するのがリスクなので紙の上にRPi5を載せています | リンク |
15 | 片プラグ付耐熱コード | 2 | 丸端子圧着済みなので、スイッチング電源に接続しやすいです | リンク |
ブート設定
はじめに
SDカードにRasberry Pi OSを書き込む
書き込みには、Rasberry Pi Imagerを利用します。ダウンロードはこちらから可能です。私はWindows版を利用しました。
一時的にしか使わないので、OSはRASBERY PI OS LITE(64-BIT)を利用します
設定の編集をします
ユーザーとWi-Fi設定を入れたら、サービスタブに移動します
SSHを有効化します。書き込みの段階で指定できるの便利ですね!
書き込み開始します
書き込み完了まで待ちます。
M.2 SSDにUbuntu Serverを書き込む
先に恒常的に利用するM.2にもOSを書き込みます。6の外付けケースを利用して、PCに画像のような形で接続します。
OSはUbuntu Server 24.04 LTS
を利用します
初回はイメージダウンロードの時間がかかるため、通信環境によっては時間がかかります。一度内部でイメージをダウンロードすれば、2回目以降は数分もかからず終わります。
書き込みが終わったら、再度M.2をPCにマウントします。すると画像のようにブートパーティションを認識します。
ブートパーティションを開いたら、config.txtを見つけます。
config.txtに2行追記します
[all]
+dtparam=nvme
+dtparam=pciex1
RPi5にクーラーを取り付け
RPi5 M.2拡張
PCIe NVME M.2 拡張ボードを取り付けます
裏は家にあったネジで高さを出すようにしました。
電源の設定
電源はRPi5の2,4ピンに+5V、6,9ピンにGNDに繋ぎます。スイッチング電源1つに2つのRPi5を接続します。以下の図が分かりやすいです。下の直方体はコネクタのハウジングを示してます。
※ 弊社のこの領域に強い方に教えて頂きました。ありがとうございます;;
実際はこんな形です。
ブート設定(EEPROM書き込み)
microSDを入れて、RPi5の電源を入れます。ホスト名はraspberrypi.local
です。
$ ssh shuntaka@raspberrypi.local
(中略)
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
(中略)
Linux raspberrypi 6.6.20+rpt-rpi-2712 #1 SMP PREEMPT Debian 1:6.6.20-1+rpt1 (2024-03-07) aarch64
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.
shuntaka@raspberrypi:~ $
ブートローダーを更新します。
shuntaka@raspberrypi:~ $ sudo rpi-eeprom-update
*** UPDATE AVAILABLE ***
BOOTLOADER: update available
CURRENT: Wed Dec 6 18:29:25 UTC 2023 (1701887365)
LATEST: Fri Feb 16 15:28:41 UTC 2024 (1708097321)
RELEASE: default (/lib/firmware/raspberrypi/bootloader-2712/default)
Use raspi-config to change the release.
利用可能な更新がありそうなので、更新を実行します。
shuntaka@raspberrypi:~ $ sudo rpi-eeprom-update -d -a
*** PREPARING EEPROM UPDATES ***
BOOTLOADER: update available
CURRENT: Wed Dec 6 18:29:25 UTC 2023 (1701887365)
LATEST: Fri Feb 16 15:28:41 UTC 2024 (1708097321)
RELEASE: default (/lib/firmware/raspberrypi/bootloader-2712/default)
Use raspi-config to change the release.
CURRENT: Wed Dec 6 18:29:25 UTC 2023 (1701887365)
UPDATE: Fri Feb 16 15:28:41 UTC 2024 (1708097321)
BOOTFS: /boot/firmware
'/tmp/tmp.ov5cNFr05e' -> '/boot/firmware/pieeprom.upd'
UPDATING bootloader.
*** WARNING: Do not disconnect the power until the update is complete ***
If a problem occurs then the Raspberry Pi Imager may be used to create
a bootloader rescue SD card image which restores the default bootloader image.
flashrom -p linux_spi:dev=/dev/spidev10.0,spispeed=16000 -w /boot/firmware/pieeprom.upd
UPDATE SUCCESSFUL
変わっていないので、再起動する必要がありそう
shuntaka@raspberrypi:~ $ sudo rpi-eeprom-update
*** UPDATE AVAILABLE ***
BOOTLOADER: update available
CURRENT: Wed Dec 6 18:29:25 UTC 2023 (1701887365)
LATEST: Fri Feb 16 15:28:41 UTC 2024 (1708097321)
RELEASE: default (/lib/firmware/raspberrypi/bootloader-2712/default)
Use raspi-config to change the release.
sudo reboot
後再度実行したところ更新完了していることを確認
shuntaka@raspberrypi:~ $ sudo rpi-eeprom-update
BOOTLOADER: up to date
CURRENT: Fri Feb 16 15:28:41 UTC 2024 (1708097321)
LATEST: Fri Feb 16 15:28:41 UTC 2024 (1708097321)
RELEASE: default (/lib/firmware/raspberrypi/bootloader-2712/default)
Use raspi-config to change the release.
ブート設定を変更します。極度のnanoアレルギーなのでviを設定します。
env EDITOR=vi sudo -E rpi-eeprom-config --edit
変更前
[all]
BOOT_UART=1
POWER_OFF_ON_HALT=0
BOOT_ORDER=0xf461
以下のように設定変更します
変更後(diff)
[all]
BOOT_UART=1
POWER_OFF_ON_HALT=0
-BOOT_ORDER=0xf461
+BOOT_ORDER=0xf416
+PCIE_PROBE=1
実行結果
shuntaka@raspberrypi:~ $ env EDITOR=vi sudo -E rpi-eeprom-config --edit
Updating bootloader EEPROM
image: /lib/firmware/raspberrypi/bootloader-2712/default/pieeprom-2024-02-16.bin
config_src: blconfig device
config: /tmp/tmpp6gugkfa/boot.conf
################################################################################
[all]
BOOT_UART=1
POWER_OFF_ON_HALT=0
BOOT_ORDER=0xf416
PCIE_PROBE=1
################################################################################
*** To cancel this update run 'sudo rpi-eeprom-update -r' ***
*** CREATED UPDATE /tmp/tmpp6gugkfa/pieeprom.upd ***
CURRENT: Fri 16 Feb 15:28:41 UTC 2024 (1708097321)
UPDATE: Fri 16 Feb 15:28:41 UTC 2024 (1708097321)
BOOTFS: /boot/firmware
'/tmp/tmp.53DnGVNp9Z' -> '/boot/firmware/pieeprom.upd'
UPDATING bootloader.
*** WARNING: Do not disconnect the power until the update is complete ***
If a problem occurs then the Raspberry Pi Imager may be used to create
a bootloader rescue SD card image which restores the default bootloader image.
flashrom -p linux_spi:dev=/dev/spidev10.0,spispeed=16000 -w /boot/firmware/pieeprom.upd
UPDATE SUCCESSFUL
M.2からブート
一度RPi5の電源をきり、microSDを抜いて、M.2を挿します。Ubuntu 24.04 LTSの文字があるので、M.2から起動していることが確認できました。
$ ssh shuntaka@pi1.local
shuntaka@pi1.local's password:
Welcome to Ubuntu 24.04 LTS (GNU/Linux 6.8.0-1004-raspi aarch64)
shuntaka@pi4:~$ sudo df -h
Filesystem Size Used Avail Use% Mounted on
tmpfs 795M 3.2M 792M 1% /run
/dev/nvme0n1p2 917G 1.9G 878G 1% /
tmpfs 3.9G 0 3.9G 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
/dev/nvme0n1p1 505M 85M 420M 17% /boot/firmware
tmpfs 795M 12K 795M 1% /run/user/1000
カーネルの更新
Ubuntu Server 24.04 LTS
では更新はありません。
$ uname -r
6.8.0-1004-raspi
sudo apt update
sudo apt -y upgrade
$ uname -r
6.8.0-1004-raspi
Ubuntu Server 23.10
の場合、クーラーが回りっぱなしで音が大きいので確実にやるのが良いです。
参考
ネットワーク設定
はじめに
以下のような形でアドレスを固定します。
ホスト名 | eth0 IP |
---|---|
pi1 | 192.168.1.1 192.168.86.10 |
pi2 | 192.168.1.2 |
pi3 | 192.168.1.3 |
pi4 | 192.168.1.4 |
p1のnetplanの設定
cat <<EOF | sudo tee /etc/netplan/01-netcfg.yaml > /dev/null
network:
version: 2
ethernets:
eth0:
dhcp4: false
addresses:
- 192.168.1.1/24
- 192.168.86.10/24
nameservers:
addresses: [8.8.8.8]
EOF
sudo netplan apply
pi2以降のnetplanの設定
pi2~pi4のnetplanの設定をします
cat <<EOF | sudo tee /etc/netplan/01-netcfg.yaml > /dev/null
network:
version: 2
ethernets:
eth0:
dhcp4: false
addresses:
- 192.168.1.2/24
# pi3の場合
# - 192.168.1.3/24
# pi4の場合
# - 192.168.1.4/24
nameservers:
addresses:
- 8.8.8.8
EOF
sudo netplan apply
192.168.1.2
が割り当てられていることを確認
shuntaka@pi2:~$ ip a
(中略)
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
(中略)
inet 192.168.1.2/24 brd 192.168.1.255 scope global eth0
valid_lft forever preferred_lft forever
(中略)
Kubernetesクラスタの構築
kubelet kubeadm kubectlのインストール
モジュールのロード設定を実施します
全てのノード
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
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
読み込まれているか確認
全てのノード
lsmod | grep br_netfilter
lsmod | grep overlay
kubelet kubeadm kubectlをインストールします
全てのノード
sudo apt update
sudo apt install -y apt-transport-https ca-certificates curl # <--最初は apt-getでやっていたの確認
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
cri-oのインストール
download.opensuse.orgから要件にあったものを選択
全てのノード
export OS=xUbuntu_22.04
sudo echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/ /" | sudo tee -a /etc/apt/sources.list.d/cri-o-stable.list >/dev/null
sudo echo "deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/1.28/$OS/ /" | sudo tee -a /etc/apt/sources.list.d/cri-o.list >/dev/null
sudo curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/Release.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/libcontainers-crio-archive-keyring.gpg
sudo curl -L http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/1.28/$OS/Release.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/cri-o-apt-keyring.gpg
sudo apt update
sudo apt install cri-o cri-o-runc -y
sudo rm -rf /etc/cni/net.d/*
sudo systemctl daemon-reload
sudo systemctl enable crio
sudo systemctl start crio
cri-oの動作確認
全てのノード
shuntaka@pi1:~$ sudo crictl info
{
"status": {
"conditions": [
{
"type": "RuntimeReady",
"status": true,
"reason": "",
"message": ""
},
{
"type": "NetworkReady",
"status": false,
"reason": "NetworkPluginNotReady",
"message": "Network plugin returns error: No CNI configuration file in /etc/cni/net.d/. Has your network provider started?"
}
]
},
"config": {
"sandboxImage": "registry.k8s.io/pause:3.9"
}
Kubernetesクラスタの初期化を実行します。
マスターノード
$ sudo kubeadm init \
--pod-network-cidr=10.244.0.0/16 \
--control-plane-endpoint=192.168.1.1 \
--apiserver-advertise-address=192.168.1.1 \
--apiserver-cert-extra-sans=192.168.1.1
ワーカーノード側で実行する必要があるコマンドをメモします。
メモする出力結果
sudo kubeadm join 192.168.1.1:6443 --token rwmoll.7vfynzwt0wbparok \
--discovery-token-ca-cert-hash sha256:bf28ffd7b92f505b93409da63dacc610d05e4d3566e50b629738a6a4cf4b259f
kubectl
が利用できるように、設定を行います
マスターノード
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Kubernetesクラスタに参加するコマンドを実行します
ワーカーノード
sudo kubeadm join 192.168.1.1:6443 --token rwmoll.7vfynzwt0wbparok \
--discovery-token-ca-cert-hash sha256:bf28ffd7b92f505b93409da63dacc610d05e4d3566e50b629738a6a4cf4b259f
ノードを確認します。この段階ではNotReadyですが、CNIをインストールするとReady状態になります
マスターノード
$ kubectl get node
NAME STATUS ROLES AGE VERSION
pi1 NotReady control-plane 9m21s v1.28.9
pi2 NotReady <none> 9s v1.28.9
CNI(calico)のインストール
手順と異なり、Ubuntu Server 23.10の場合、以下の設定が必要です
全てのノード
# Ubuntu Server 23.10利用の場合のみ
sudo apt install linux-modules-extra-raspi -y
CNIをインストールします
マスターノード
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/tigera-operator.yaml
curl https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/custom-resources.yaml -O
# custom-resources.yamlのcidr: 10.244.0.0/16 に修正
kubectl create -f custom-resources.yaml
kubectl get pods -n calico-system
CNIがインストールされ、全てReadyになりました
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
pi1 Ready control-plane 17m v1.28.9
pi2 Ready <none> 8m16s v1.28.9
エイリアス設定
使いやすいように、エイリアスと補完設定を入れます。この設定で省略されたk
コマンドとtab
での補完が効くようになります。
cat <<EOF >> ~/.bashrc
# k8s
source <(kubectl completion bash)
alias k=kubectl
complete -o default -F __start_kubectl k
EOF
Helmの設定
はじめに
今回KubernetesリソースはHelm経由でデプロイすることにします。
Helmのインストール
マスターノード
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
sudo apt install apt-transport-https --yes
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt update
sudo apt install helm
Helmfileのインストール
マスターノード
export VERSION="0.163.1"
wget -O "/tmp/helmfile_${VERSION}_linux_arm64.tar.gz" "https://github.com/helmfile/helmfile/releases/download/v${VERSION}/helmfile_${VERSION}_linux_arm64.tar.gz"
tar -xzvf "/tmp/helmfile_${VERSION}_linux_arm64.tar.gz" -C /tmp
sudo mv /tmp/helmfile /usr/bin/helmfile
sudo chmod 755 /usr/bin/helmfile
MetalLBの設定
はじめに
MetalLBは、ベアメタルKubernetes環境向けのロードバランサーでKubernetesクラスタ外からアクセス出来るようにIPを割り当てます。
IPアドレスプールの定義
割り当てるIPプールを定義します。IPレンジはKubernetesの内部IPではなく、ルータで払い出されているものを利用してください。
ip-address-pool.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: default
namespace: metallb
spec:
addresses:
- 192.168.86.200-192.168.86.250
autoAssign: true
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: default
namespace: metallb
spec:
ipAddressPools:
- default
適用します
kubectl apply -f ip-address-pool.yaml
MetalLBのホスティング
こちら を参考に、以下のコマンドで、strictARP
をtrueにします
kubectl edit configmap -n kube-system kube-proxy
helmfile.yaml
repositories:
- name: metallb
url: https://metallb.github.io/metallb
releases:
- name: metallb
namespace: metallb
chart: metallb/metallb
version: 0.14.5
values:
- values.yaml
実行自体はこけますが、repositoriesは追加されます。
helmfile sync
以下のコマンドで変更可能箇所を出力します。出力しますが変更箇所はありません。
helm show values metallb/metallb>values.yaml
適用します。
helmfile sync
PromethuesとGrafanaの設定
はじめに
- PromethuesとGrafana間で通信させるために同じnamespaceに所属
- MetalLBを使ってGrafanaをクラスタ外へ公開
永続ストレージ設定
PersistentVolumeとPersistentVolumeClaimの設定をします。prometheus-alertmanager
は、ストレージは作っていますが、後述のprometheusの設定でOFFにするのでなくても大丈夫です。
ストレージタイプ | name | path |
---|---|---|
PersistentVolume | prometheus-server | /mnt/k8s/pv/prometheus-server |
PersistentVolume | prometheus-alertmanager | /mnt/k8s/pv/prometheus-alertmanager |
PersistentVolume | grafana | /mnt/k8s/pv/grafana |
ストレージタイプ | namespace | name |
---|---|---|
PersistentVolumeClaim | monitoring | prometheus-server |
PersistentVolumeClaim | monitoring | prometheus-alertmanager |
PersistentVolumeClaim | monitoring | grafana |
monitoringネームスペース作成
kubectl create namespace monitoring
pvとpvcはcontrol-planeにデフォルトでは設定できないため、pi2側で設定します
pi2で実行!!!
sudo mkdir -p /mnt/k8s/pv/grafana
sudo mkdir -p /mnt/k8s/pv/prometheus-server
sudo mkdir -p /mnt/k8s/pv/prometheus-alertmanager
pvとpvcを定義します
volume.yaml
# grafana
kind: PersistentVolume
apiVersion: v1
metadata:
name: grafana
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: grafana
local:
path: /mnt/k8s/pv/grafana
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- pi2
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: grafana
namespace: monitoring
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: grafana
---
# prometheus-server
kind: PersistentVolume
apiVersion: v1
metadata:
name: prometheus-server
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: prometheus-server
local:
path: /mnt/k8s/pv/prometheus-server
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- pi2
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: prometheus-server
namespace: monitoring
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: prometheus-server
---
# prometheus-alertmanager
kind: PersistentVolume
apiVersion: v1
metadata:
name: prometheus-alertmanager
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: prometheus-alertmanager
local:
path: /mnt/k8s/pv/prometheus-alertmanager
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- pi2
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: prometheus-alertmanager
namespace: monitoring
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: prometheus-alertmanager
volumeを適用します
kubectl apply -f volume.yaml
表通り作成されていることを確認できたらOKです
kubectl get pv
kubectl get pvc -A
Promethuesの設定
helmfileを書きます
helmfile.yaml
repositories:
- name: prometheus-community
url: https://prometheus-community.github.io/helm-charts
releases:
- name: prometheus
namespace: monitoring
chart: prometheus-community/prometheus
version: 25.9.0
values:
- values.yaml
実行自体はこけますが、repositoriesは追加されます
helmfile sync
以下のコマンドで変更可能箇所を出力します。出力しますが変更箇所はありません。
helm show values prometheus-community/prometheus > values.yaml
以下のように修正します
values.yaml
server:
## Prometheus server container name
##
(中略)
persistentVolume:
(中略)
existingClaim: "prometheus-server" # <- 先ほど作成したPVCを指定
(中略)
size: 10Gi # <- 8Giを10Giに変更
(中略)
service:
(中略)
type: NodePort # <- ClusterIPをNodePortへ変更
(中略)
alertmanager:
## If false, alertmanager will not be installed
##
enabled: false # <- 今回は利用しないためtrueからfalseへ変更
適用します
helmfile sync
Grafanaの設定
同様にhelmfileを書きます
helmfile.yaml
repositories:
- name: grafana
url: https://grafana.github.io/helm-charts
releases:
- name: grafana
namespace: monitoring
chart: grafana/grafana
version: 7.2.3
values:
- values.yaml
こちらも同様です。
helmfile sync
以下のコマンドで変更可能箇所を出力します。
helm show values grafana/grafana > values.yaml
以下のように修正します
values.yaml
service:
enabled: true
type: NodePort # <- ClusterIPをNodePortへ変更
(中略)
persistence:
type: pvc
enabled: true # <- ストレージ永続化ため、falseからtrueへ変更
(中略)
existingClaim: "grafana" # <- 先ほど作成したPVCを指定
(中略)
# データソースを追加
datasources:
datasources.yaml:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: http://prometheus-server.monitoring.svc.cluster.local/
isDefault: true
適用します
helmfile sync
以下のコマンド実行して、マスターノードのクラスタ外から見れるプライベートIP:3000で、PCからアクセスしてGrafanaのログイン画面が見れれば成功です。
マスターノード
kubectl port-forward --address 0.0.0.0 svc/grafana 3000:80 -n monitoring
ログインIDとパスワードは以下のコマンド出力できます
kubectl get secret grafana --namespace monitoring -o jsonpath="{.data.admin-user}" | base64 --decode ; echo
kubectl get secret grafana --namespace monitoring -o jsonpath="{.data.admin-password}" | base64 --decode ; echo
Grafanaをクラスタ外から閲覧する
MetalLBを利用して外部アクセス可能なLoadBalancerタイプのサービスを定義します。宛先にgrafanaを指定します。
grafana-loadbalancer.yaml
apiVersion: v1
kind: Service
metadata:
name: grafana-loadbalancer
namespace: monitoring
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 3000
protocol: TCP
selector:
app.kubernetes.io/instance: grafana
app.kubernetes.io/name: grafana
適用します
kubectl apply -f grafana-loadbalancer.yaml
grafana-loadbalancer
に192.168.86.200
が振られていることが確認できます。
$ k get svc -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
calico-apiserver calico-api ClusterIP 10.109.235.205 <none> 443/TCP 5h2m
calico-system calico-kube-controllers-metrics ClusterIP None <none> 9094/TCP 5h2m
calico-system calico-typha ClusterIP 10.96.11.42 <none> 5473/TCP 5h3m
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5h8m
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 5h8m
metallb metallb-webhook-service ClusterIP 10.98.53.232 <none> 443/TCP 4h31m
monitoring grafana NodePort 10.100.215.182 <none> 80:31851/TCP 4h35m
monitoring grafana-loadbalancer LoadBalancer 10.98.112.253 192.168.86.200 80:30699/TCP 4h17m
monitoring prometheus-kube-state-metrics ClusterIP 10.110.198.118 <none> 8080/TCP 4h34m
monitoring prometheus-prometheus-node-exporter ClusterIP 10.108.144.213 <none> 9100/TCP 4h34m
monitoring prometheus-prometheus-pushgateway ClusterIP 10.107.27.149 <none> 9091/TCP 4h34m
monitoring prometheus-server NodePort 10.111.109.182 <none> 80:31648/TCP 4h34m
http://192.168.86.200にアクセスすると、http://192.168.86.200/loginにリダイレクトされて、grafanaのダッシュボードが閲覧できました
Data sourcesを開きます
DatasourceはHelmのvalue.yamlで定義しているため、Prometheusがすでにある状態です。
今回は、こちらのダッシュボードを利用しますので、11074を入力してLoadします。
Loadされたら、importします
無事importできました。このときはまだ3つしかノードがないので、正常に動作しています!
最後に
先人が沢山いたのですが、かなり大変でした。環境を完全に消すのも大変だったので、都度M.2にイメージを焼き直してました。気づいたらUbuntu Server 24.04 LTSがリリースされていて手順を差し替えるか迷いました。23.10だと、クーラー周りの問題で音がかなり厳しいのでカーネルアップデートが必須でしたが、24.04は問題なかったので、結果待ち時間が少ない24.04の手順にしました。
大変だった点は主にネットーワーク周りになります。最初はp1をNAT化し、pi2以降はpi1をデフォルトゲートウェイとしてネットーワークに出ていたのですが、MetalLBを入れてから通信がループして設定を削除したりなど。。ここら辺もう少し再現確認してブログにできたらと思います。
各ワーカーノードのストレージとしてM.2を使いましたが、容量の大きいNFSサーバーを作ってそこに永続化リソースをまとめるの方が良いのかなとも思いました!
また弊社のハードに強い方にいくつかアドバイス頂き感謝です!
今後経過についてもブログ記事化できたらと思います!
付録
トラブルシューティング
MetalLBで割り当てられたIPに接続できない
curlやブラウザ経由でMetalLBで割り当てられたIPでgrafanaの画面が閲覧できな場合です。以下のコマンドでARPリクエスト送信し、tcpdumpでARP応答を返していることを確認できれば、MetalLBとしては動作しています。FW設定などに問題あるかどうかを切り分けできます。
# ARPリクエストを送信
arping -I eth0 192.168.86.100
# 応答していることを確認
sudo tcpdump -n -i eth0 arp src host 192.168.86.100
Grafanaダッシュボードからテストリクエストが通らない
corednsを再起動したら動くケースがありました。
kubectl rollout restart deployment coredns -n kube-system
PrometheusやGrafanaのホスティングをやり直したい
永続化ストレージの消し忘れに注意
helm uninstall prometheus -n monitoring
helm uninstall grafana -n monitoring
sudo rm -rf /mnt/k8s/pv/grafana
sudo rm -rf /mnt/k8s/pv/prometheus-server
sudo rm -rf /mnt/k8s/pv/prometheus-alertmanager
参考資料
- Raspberry Pi 5でNVMeストレージ起動を試す
- Raspberry Pi 5をM.2 NVMe SSDからブートしてベンチマークしてみた
- オンプレKubernetes環境にMetallbをインストールしてLoadbalanserServiceを利用できるようにする
- minikubeでHelmを勉強してみた
- ローカルKubernetesクラスター上でPrometheusのメトリクスをGrafanaダッシュボードで可視化する
- 我流お家クラウドを構築する【1】~kubernetesをセットアップする~
- 4台のラズパイでk8sクラスタを組んだ
- TROUBLESHOOTING METALLB