はじめに
この記事では、2台のRaspberry Pi 5を使って、Ubuntu 24.04環境でk3sクラスタを構築する手順を共有します。クラウド上のKubernetes環境は主流ですが、「もっと手軽に自宅で試したい!」と思っている方(特に私w)に向けて、リーズナブルなハードウェアで、手軽にk3sクラスタを構築する方法をお伝えします(単なる作業メモです。。)。エッジコンピューティングやIoT分野での学習やプロジェクトの第一歩として、ぜひ参考にしてください。
前提条件
- 2台のRaspberry Pi 5(Rpi1とRpi2)
- 両方にUbuntu 24.04がインストール済み
- 両方がネットワークに接続されている
1. k3sのインストール
Raspberry Pi 2台を使用してk3sクラスタを構築する手順を解説します。最初に各デバイスのIPアドレスを確認し、ファイアウォールを設定します。その後、マスターノードとして機能するRpi1にk3sをインストールし、ワーカーノードであるRpi2をクラスタに参加させます。最後に、クラスタが正しく構成され、動作しているかを確認します。このガイドに従うことで、手軽にRaspberry Piでk3sクラスタを構築する方法を学べます。
1-1. IPアドレスの確認
両方のRaspberry Piで以下のコマンドを実行し、IPアドレスを確認します。
ip addr show
この例では、以下のIPアドレスを使用します。
- Rpi1 (マスターノード): 192.168.11.8
- Rpi2 (ワーカーノード): 192.168.11.5
1-2. ファイアウォールの設定
両方のRaspberry Piで以下のコマンドを実行し、必要なポートを開放します。
sudo ufw allow 22/tcp
sudo ufw allow 6443/tcp
sudo ufw allow 8472/udp
sudo ufw allow 10250/tcp
sudo ufw enable
1-3. Rpi1(マスターノード)の設定
Rpi1にSSH接続し、以下のコマンドを実行してk3sをインストールします。
curl -sfL https://get.k3s.io | sh -
k3sのステータスを確認します。
sudo systemctl status k3s
これにより、k3sサービスが正常に稼働しているかを確認できます。active (running) と表示されていれば、k3sが正常に動作していることを示します。
k3s.service - Lightweight Kubernetes
Loaded: loaded (/etc/systemd/system/k3s.service; enabled; preset: >
Active: active (running) since Sat 2024-08-10 22:25:33 JST; 16h ago
Docs: https://k3s.io
Process: 1272 ExecStartPre=/bin/sh -xc ! /usr/bin/systemctl is-enab>
Process: 1274 ExecStartPre=/sbin/modprobe br_netfilter (code=exited>
Process: 1277 ExecStartPre=/sbin/modprobe overlay (code=exited, sta>
Main PID: 1279 (k3s-server)
Tasks: 125
Memory: 777.6M (peak: 973.0M)
CPU: 55min 34.844s
CGroup: /system.slice/k3s.ser
インストールが完了したら、ノードトークンを取得します。
sudo cat /var/lib/rancher/k3s/server/node-token
このトークンをメモしておきます。
1-4. Rpi2(ワーカーノード)の設定
Rpi2にSSH接続し、以下のコマンドを実行してk3sエージェントをインストールします。とを適切な値に置き換えてください。
curl -sfL https://get.k3s.io | K3S_URL=https://<MASTER_IP>:6443 K3S_TOKEN=<NODE_TOKEN> sh -
例:
curl -sfL https://get.k3s.io | K3S_URL=https://192.168.11.8:6443 K3S_TOKEN=K10cf3cf691b31e613c5b7e69e81edb1994b8351538c375f3a59af515a58725d37f::server:980d9f3d5f3fc7e6fb8d086ce4cec5a4 sh -
インストールの進行状況は以下のように確認できます。
[INFO] Finding release for channel stable
[INFO] Using v1.30.3+k3s1 as release
[INFO] Downloading hash https://github.com/k3s-io/k3s/releases/download/v1.30.3+k3s1/sha256sum-arm64.txt
[INFO] Downloading binary https://github.com/k3s-io/k3s/releases/download/v1.30.3+k3s1/k3s-arm64
[INFO] Verifying binary download
[INFO] Installing k3s to /usr/local/bin/k3s
[INFO] Skipping installation of SELinux RPM
[INFO] Skipping /usr/local/bin/kubectl symlink to k3s, command exists in PATH at /usr/bin/kubectl
[INFO] Skipping /usr/local/bin/crictl symlink to k3s, command exists in PATH at /usr/bin/crictl
[INFO] Skipping /usr/local/bin/ctr symlink to k3s, already exists
[INFO] Creating killall script /usr/local/bin/k3s-killall.sh
[INFO] Creating uninstall script /usr/local/bin/k3s-agent-uninstall.sh
[INFO] env: Creating environment file /etc/systemd/system/k3s-agent.service.env
[INFO] systemd: Creating service file /etc/systemd/system/k3s-agent.service
[INFO] systemd: Enabling k3s-agent unit
Created symlink /etc/systemd/system/multi-user.target.wants/k3s-agent.service → /etc/systemd/system/k3s-agent.service.
[INFO] systemd: Starting k3s-agent
1-5. クラスタの確認
Rpi1(マスターノード)で以下のコマンドを実行し、ノードの状態を確認します。
sudo k3s kubectl get nodes
両方のノードが表示され、STATUSが"Ready"になっていれば、クラスタの構築は完了です。
NAME STATUS ROLES AGE VERSION
Rpi1 Ready control-plane,master 17h v1.30.3+k3s1
Rpi2 Ready <none> 15h v1.30.3+k3s1
2. サンプルアプリケーションのデプロイ
クラスタの動作を確認するために、サンプルのFlaskアプリケーションをデプロイします。
2-1. アプリケーションの準備
Rpi1で以下の手順を実行します
-
アプリケーションディレクトリの作成:
mkdir flask-app cd flask-app
-
app.py
の作成:
k3sにデプロイ時、os.getenv('HOSTNAME')を使用してPod名を取得できます。Podが切り替わることを確認します。from flask import Flask, jsonify import os app = Flask(__name__) @app.route('/api', methods=['GET']) def get_api(): return jsonify(message="Hello from Flask!", pod_name=os.getenv('HOSTNAME')) @app.route('/api2', methods=['GET']) def get_api2(): return jsonify(message="Hello from API2", pod_name=os.getenv('HOSTNAME')) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)
-
Dockerfile
の作成:FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY app.py . EXPOSE 5000 CMD ["python", "-u", "app.py"]
-
requirements.txt
の作成:Flask
2-2. Dockerイメージのビルドとプッシュ
Raspberry PiなどのARM64プラットフォームを使用するデバイスで動作させるために、ARM64向けにコンパイルされたバイナリを含むイメージをビルドします。
"--platform linux/arm64"を指定すればmacでビルドしても問題なく動作しました
docker build --platform linux/arm64 -t <your-dockerhub-username>/flask-app:v1.0.1 .
docker push <your-dockerhub-username>/flask-app:v1.0.1
2-3. マニフェストの作成
下記内容で、flask-app-deployment.yamlという名称のファイルを作成します。replicasに2を指定して、2つのPodを作成します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app
spec:
replicas: 2
selector:
matchLabels:
app: flask-app
template:
metadata:
labels:
app: flask-app
spec:
containers:
- name: flask-app
image: <your-dockerhub-username>/flask-app:v1.0.1
ports:
- containerPort: 5000
---
apiVersion: v1
kind: Service
metadata:
name: flask-app-service
spec:
selector:
app: flask-app
ports:
- port: 80
targetPort: 5000
2-4. アプリケーションのデプロイ
Rpi1で下記コマンドを実行し、アプリケーションをデプロイします。
sudo k3s kubectl apply -f flask-app-deployment.yaml
2-5. デプロイメントの確認
下記コマンドを実行して、デプロイメントを確認します。
sudo kubectl get pods -o wide
実行結果:
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
flask-app-698574456b-nb6xf 1/1 Running 0 15h 10.42.2.3 Rpi2 <none> <none>
flask-app-698574456b-pldh8 1/1 Running 0 16h 10.42.0.15 Rpi1 <none> <none>
flask-appの2つのPodが、それぞれRpi2(pod名=flask-app-698574456b-nb6xf)とRpi1(pod名=flask-app-698574456b-pldh8)のノード上で正常に稼働していることが確認できます。各Podは独自の内部IPアドレスを持ち、再起動もなく安定して稼働しています。
3. アプリケーションへのアクセス
この章では、デプロイしたFlaskアプリケーションへのアクセス方法を説明します。まず、k3sクラスタ内で使用されるポート番号を確認し、次に外部からアクセスできるように設定を行います。複数のリクエストを行うことで、クラスタ内でのラウンドロビン方式による負荷分散の効果を確認し、各Podのステータスをチェックします。
3-1. ポート番号の確認
ポート番号の確認:
sudo k3s kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
flask-app-service NodePort 10.43.206.71 <none> 80:30458/TCP 16h
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 17h
3-2. 動作確認
- 1回目
- 正しく実行できることが確認できます。
- pod_nameの値より、Rpi2上で動作しているPodにアクセスしたことがわかります。
curl http://192.168.11.8:30458/api {"message":"Hello from Flask!","pod_name":"flask-app-698574456b-nb6xf"}
- 2回目
- 正しく実行できることが確認できます
- pod_nameの値より、Rpi1上で動作しているPodにアクセスしたことがわかります。
- 複数のサーバーをまたいでクラスタ化されていることがわかります。
curl http://192.168.11.8:30458/api {"message":"Hello from Flask!","pod_name":"flask-app-698574456b-pldh8"}
- 3回目
- APIのエンドポイントを変えても正しく実行できることが確認できます。
- pod_nameの値より、Rpi2上で動作しているPodにアクセスしたことがわかります。
- ラウンドロビンで割り振られていることがわかります。
curl http://192.168.11.8:30458/api2 {"message":"Hello from API2","pod_name":"flask-app-698574456b-nb6xf"}
4. FAQ
4.1 sudoを使用せずに「kubectl get nodes」を実行する方法
-
KUBECONFIG 環境変数の永続化:
KUBECONFIG 環境変数を永続的に設定するために、ユーザーのシェル設定ファイル(例えば .bashrc や .zshrc)に以下の行を追加します。これにより、再ログイン時や再起動時にも設定が引き継がれます。echo 'export KUBECONFIG=/etc/rancher/k3s/k3s.yaml' >> ~/.bashrc source ~/.bashrc
-
設定ファイルのパーミッションの設定
再起動後に設定ファイルのパーミッションが変更されていないか確認します。もし変更されていた場合、セキュリティポリシーや自動スクリプトが原因の可能性があります。この場合、以下のコマンドで再度パーミッションを設定して、状況を確認します。sudo chmod 644 /etc/rancher/k3s/k3s.yaml
毎回起動時に実行する必要ありかもです
おわりに
ここまで読んでいただき、ありがとうございます!2台のRaspberry Pi 5を使ってk3sクラスタを構築する手順をざっくりまとめましたが、いかがでしたでしょうか?(私も手探りでやってますw)。クラスタを自分で構築してみると、Kubernetesの基礎を実際に体感できるので、学びが深まりますよ。次は、アプリケーションをスケールさせたり、異なるワークロードをデプロイしたりと、さらなる挑戦に進んでみてください!やっぱり実践あるのみですね。