RaspberryPi5のk3sクラスタにNodeとNginxとFlaskをデプロイする方法

9 min

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"]

Raspberry Pi5向けのコンテナを作成する際は下記の通り「–platform linux/arm64」オプションを指定します。

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にデプロイするためのDeploymentServiceを定義します。

# 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"]

Raspberry Pi5向けのコンテナを作成する際は下記の通り「–platform linux/arm64」オプションを指定します。

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にデプロイするためのDeploymentServiceを定義します。

# 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とDeploymentServiceを定義します。
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を取り入れてみてください。

広告_零号機-エリア2
kewton

kewton

大学院卒業後、某大手SIerで10年以上SEとして従事。
社会人3年目までに基本情報・応用情報技術者、データベーススペシャリスト、簿記3級・2級を取得。
基幹系システム・IoTシステム開発のプロジェクト経験多数。AI活用システムの企画・プロト開発経験あり。
強みは、プロマネだけでなく自身で開発も実施してきたこと。
【扱える言語】
C#、java、python、javascript、Excel VBA
【扱えるDB】
oracle、sql server、postgreSQL、mongoDB

FOLLOW

カテゴリー:
タグ:
関連記事

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA