Raspberry Pi 5を2台使用して、軽量なKubernetesディストリビューションであるk3sを活用したクラスタを構築しました。本記事では、この環境にNode.jsで書かれたAPI1と、Flaskを使用したAPI2をデプロイする手順について紹介します。初心者向けのチュートリアルとして、デプロイの流れを中心に解説しますので、k8sやk3sの世界に興味がある方はぜひご覧ください。
目次
イントロダクション
今回は、Raspberry Pi 5を2台使用してk3sクラスタを構築し、その上でNode.jsとFlaskで作成したAPIをデプロイする手順を紹介します。この記事では、特にデプロイの手順に焦点を当て、初心者の方でも簡単にk3s環境でアプリケーションを動かせるように解説していきます。
k3sは軽量なKubernetesディストリビューションで、リソースが限られたデバイスでも利用可能です。Raspberry Piのような小型デバイスでKubernetesを試してみたいという方には、非常におすすめの選択肢です。
それでは、まず今回のアプリケーション構成について説明し、その後に具体的なデプロイ手順を見ていきましょう。
この構成図は、k3sクラスタにデプロイされている各コンポーネントと、それらの間で行われる通信の流れを視覚化したものです。具体的には、Raspberry Pi 5の2台で構成されたk3sクラスタが、MasterノードとWorkerノードに分かれ、それぞれにAPI1(Node.jsで実装)とAPI2(Flaskで実装)が配置されています。これらのAPIは、NginxやGunicornを介して適切にリクエストを処理し、応答を返します。
Node.jsアプリケーションのデプロイ
最初に、Node.jsで作成したAPI1をデプロイする手順を見ていきましょう。以下のコードは、API1として動作するNode.jsアプリケーションです。
index.js
const express = require('express');
const axios = require('axios');
const app = express();
const port = 3000;
app.get('/api1', async (req, res) => {
const { fqdn, apiRoute } = req.query;
if (!fqdn || !apiRoute) {
return res.status(400).json({ error: 'Missing fqdn or apiRoute in query parameters' });
}
// curl "192.168.11.8:32636/api1?fqdn=flask-app-mac-service&apiRoute=api2"
try {
const response = await axios.get(`http://${fqdn}/${apiRoute}`);
res.json({ message: 'Hello from API1', api2Response: response.data });
} catch (error) {
res.status(500).json({ error: 'Error calling API2' });
}
});
app.listen(port, () => {
console.log(`API1 running on port ${port}`);
});
package.json
{
"name": "api1",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^1.7.2",
"express": "^4.19.2"
}
}
このAPI1は、リクエストを受け取ると、別のAPI2に対してリクエストを送り、その結果を返します。このNode.jsアプリケーションをk3sにデプロイするために、まずDockerfile
を作成し、イメージをビルドします。
Dockerfile
FROM node:14
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "index.js"]
docker build --platform linux/arm64 -t <your-dockerhub-username>/api1_arm64:v1.0.0 . --no-cache
docker push <your-dockerhub-username>/api1_arm64:v1.0.0
次に、そのイメージを使ってk3sにデプロイするためのDeployment
とService
を定義します。
# api1-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api1-deployment
spec:
replicas: 2
selector:
matchLabels:
app: api1
template:
metadata:
labels:
app: api1
spec:
containers:
- name: api1
image: <your-dockerhub-username>/api1_arm64:v1.0.0
ports:
- containerPort: 3000
---
# api1-service.yaml
apiVersion: v1
kind: Service
metadata:
name: api1-service
spec:
selector:
app: api1
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: NodePort
これらの設定ファイルを使用して、マスターノードにsshでログインして下記コマンドを実行すると、API1がk3sクラスタ上にデプロイされます。
kubectl apply -f api1-deployment.yaml
kubectl apply -f api1-service.yaml
Flaskアプリケーションのデプロイ
次に、Flaskで作成したAPI2をデプロイします。Flaskアプリケーションのコードは以下の通りです。
app.py
from flask import Flask, jsonify
import os
app = Flask(__name__)
@app.route('/api', methods=['GET'])
def get_api():
pid = os.getpid()
return jsonify(message="Hello from Flask!", pod_name=os.getenv('HOSTNAME'), pid=pid)
@app.route('/api2', methods=['GET'])
def get_api2():
pid = os.getpid()
return jsonify(message="Hello from API2", pod_name=os.getenv('HOSTNAME'), pid=pid)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
requirements.txt
Flask
gunicorn
API1と同様にイメージをビルドします。DockerfileにGunicornというWSGI HTTPサーバーを用いるためのコマンドを記述します。
Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"]
docker build --platform linux/arm64 -t <your-dockerhub-username>/flask-app_arm64:v1.0.0 . --no-cache
docker push <your-dockerhub-username>/flask-app_arm64:v1.0.0
次に、そのイメージを使ってk3sにデプロイするためのDeployment
とService
を定義します。
# flask-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-deployment
spec:
replicas: 2
selector:
matchLabels:
app: flask
template:
metadata:
labels:
app: flask
spec:
containers:
- name: flask
image: <your-dockerhub-username>/flask-app_arm64:v1.0.0
ports:
- containerPort: 5000
---
# flask-service.yaml
apiVersion: v1
kind: Service
metadata:
name: flask-service
spec:
selector:
app: flask
ports:
- protocol: TCP
port: 80
targetPort: 5000
type: NodePort
これらの設定ファイルを使用して、マスターノードにsshでログインして下記コマンドを実行してk3sクラスタ上にFlaskアプリケーションを配置します。
kubectl apply -f flask-deployment.yaml
kubectl apply -f lask-service.yaml.yaml
Nginxのデプロイ
k3sにNginxをデプロイするたにConfigMapとDeployment
とService
を定義します。
Nginxの使用方法についてはこちらをご参考ください
# nginx-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
data:
nginx.conf: |
events { }
http {
upstream flask {
server flask-service:80;
}
server {
listen 80;
location / {
proxy_pass http://flask;
}
}
}
---
# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
volumes:
- name: nginx-config
configMap:
name: nginx-config
---
# nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: NodePort
これらの設定ファイルを使用して、マスターノードにsshでログインして下記コマンドを実行してk3sクラスタ上にNginxを配置します。
kubectl apply -f nginx-configmap.yaml
kubectl apply -f nginx-deployment.yaml
kubectl apply -f nginx-service.yaml
サービス間通信の動作確認
最後に、サービス間通信を動作確認します。API1からAPI2への通信は、Node.jsアプリケーションがaxios
を使ってAPI2にリクエストを送信することで行われます。
まず、サービスにアクセスするためのポート番号を確認します。下記コマンドを実行すると「NAME, TYPE, CLUSTER-IP, EXTERNAL-IP, PORTS(S), AGE」が出力されます。配備したService名に対応するPORT番号(PORTSの「80:<PORT番号>/TCP」)を控えます。
kubectl get services
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# api1-service NodePort 10.43.232.231 <none> 80:32636/TCP 7d1h
# flask-service NodePort 10.43.173.213 <none> 80:32471/TCP 7d1h
# nginx-service NodePort 10.43.165.217 <none> 80:31953/TCP 7d1h
以下は、MacBook Airからk3sクラスタ上の各サービスにcurl
コマンドを使ってアクセスする例です。
# NodeService(api1-service)にリクエストを送る
curl "http://<マスターノードのIP>:<NodeServiceに対応するポート番号>/api1?fqdn=flask-service&apiRoute=api2"
# 実行例
# curl "http://192.168.11.8:32636/api1?fqdn=flask-service&apiRoute=api2"
# {"message":"Hello from API1","api2Response":{"message":"Hello from API2","pid":10,"pod_name":"flask-deployment-7c5b7bcf55-w9kqm"}}%
# NginxService(nginx-service)にリクエストを送る
curl http://<マスターノードのIP>:<NginxServiceに対応するポート番号>/api
# 実行例
# curl http://192.168.11.8:31953/api
# {"message":"Hello from Flask!","pid":8,"pod_name":"flask-deployment-7c5b7bcf55-vm5mk"}
# FlaskService(flask-service)に直接リクエストを送る
curl http://<マスターノードのIP>:<FlaskServiceに対応するポート番号>/api
# 実行例
# curl http://192.168.11.8:32471/api
# {"message":"Hello from Flask!","pid":10,"pod_name":"flask-deployment-7c5b7bcf55-w9kqm"}
まとめ
この記事では、Raspberry Pi 5を使用して構築したk3sクラスタに、Node.jsおよびFlaskで作成したアプリケーションをデプロイする手順を紹介しました。k3sは軽量でありながら、強力なクラスタ管理機能を持つため、小規模なプロジェクトや学習目的で非常に適しています。今回の手順を参考にして、自分のプロジェクトにk3sを取り入れてみてください。