オンプレ環境の Kubernetes に Mosquito を使って MQTT ブローカーを立ててみた。

今回は自宅の **Kubernetes クラスター** に **Mosquito** を導入してみました。

# 環境

今回の環境は下記のとおりです。

## 開発端末

- **OS**： Windows 11 Pro
- **CPU**： Intel Core i7-9750H
- **メモリ**： 64GB
- **GPU**： NVIDIA GeForce RTX 2070 Max-Q

## Kubernetes クラスター

- **Master Node**：Raspberry Pi 4 (8GB) x3
- **Worker Node**：Raspberry Pi 4 (8GB) ×3
- **Worker Node**：Raspberry Pi 400 (4GB) ×1
- **Load Balancer**：MetalLB
- **Helm**：v3.12.3
- **NFS Server**：Raspberry Pi 4 (4GB)
- **ルーター**： NanoPi R5C（OS：FriendlyWrt）

※NanoPi R5C および FriendlyWrt については下記を参考にしてください。

https://qiita.com/IoriGunji/items/c94cf84cafffdcd947cc

※ロードバランサーとして利用している MetalLB については下記の記事を参考にしてください。

https://qiita.com/IoriGunji/items/a7e5e8b36778d9b676f0?0

# Mosquito の導入方法

**Mosquito** を導入するにあたって、 **Helm** を利用しています。
なお、 **Raspberry Pi** へ **Helm** を導入する方法については、下記の記事を参考にしてください。


## リポジトリの追加

リポジトリの追加を行います。

```shell
sudo helm repo add t3n https://storage.googleapis.com/t3n-helm-charts
```

## Helm チャートリポジトリの更新

リポジトリの追加を行ったので、チャートリポジトリの最新情報を取得するために更新を行います。

```shell
sudo helm repo update
```

## Mosquito のインストール

`helm install` コマンドを利用して、 **Mosquito** のインストールを行います。

```shell
sudo helm install mosquitto t3n/mosquitto \
    --namespace mqtt --create-namespace \
    --set service.type=LoadBalancer \
    --set controller.service.loadBalancerIP=192.168.10.200 \
    --set persistence.storageClass=nfs
```

### service.type

今回の環境では、ロードバランサーに **MetalLB** を利用しているため、 `service.type` は `LoadBalancer` を指定します。
なお、 `externalTrafficPolicy` は、デフォルトの `Cluster` のままで大丈夫です。

:::note info
externalTrafficPolic について
`Local` と `Cluster` から選択できます。
デフォルトは `Cluster` です。
- Local
  Nodeでトラフィックを受けたあとに別Nodeへのホップを無効にします。
  トラフィックの処理が偏る可能性があります。
- Cluster
  Nodeでトラフィックを受けたあとに別Nodeへのホップを有効にします。
  別のNodeへホップを許可することでトラフィック処理を分散できます。
:::

### controller.service.loadBalancerIP

**MetalLB** から払い出される **IPアドレス** を固定します。
`192.168.10.200` は **MetalLB** の **IPアドレスプール** のアドレス範囲内のローカルIPアドレスを指定してください。

:::note info
**参考**
今回の環境の **MetalLB** の `ipaddresspool.yml` は下記のとおりです。
今回は **IPアドレスプール** から払い出される先頭の `192.168.10.200` を指定しました。

```ipaddresspool.yml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: default
  namespace: metallb-system
spec:
  addresses:
  - 192.168.10.200-192.168.10.255
  autoAssign: true
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: default
  namespace: metallb-system
spec:
  ipAddressPools:
  - default
```
:::

### persistence.storageClass

ご自身の環境にあわせて **PersistentVolumeClaim（PVC）** を指定します。
今回は割愛しますが、気が向いたら加筆します。

## MQTT の確認

デプロイが終わったら、 MQTT の設定を確認します。

### MQTT の設定値

```shell
sudo helm get values -n mqtt mosquitto
```

```shell
USER-SUPPLIED VALUES:
controller:
  service:
    loadBalancerIP: 192.168.10.200
persistence:
  storageClass: nfs
service:
  type: LoadBalancer
```

### ネームスペース一覧の確認

```shell
sudo kubectl get namespace
```

```shell
NAME              STATUS   AGE
default           Active   21d
kube-flannel      Active   21d
kube-node-lease   Active   21d
kube-public       Active   21d
kube-system       Active   21d
metallb-system    Active   20d
mqtt              Active   2d18h
```

### MQTT サービスの確認

```shell
sudo kubectl get svc -n mqtt
```

```shell
NAME        TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)                         AGE
mosquitto   LoadBalancer   10.108.71.8   192.168.10.200   1883:32694/TCP,9090:31859/TCP   5m24s
```

### MQTT の Pod の確認

```shell
sudo kubectl get pod -o wide -n mqtt
```

```shell
NAME                         READY   STATUS    RESTARTS   AGE   IP           NODE          NOMINATED NODE   READINESS GATES
mosquitto-75c6f9b4d9-4kj8j   1/1     Running   0          45s   10.244.5.4   k8s-worker3   <none>           <none>
```

# NAPT（IPマスカレード）

MQTT ブローカーの構築は完了しました。
ですが、このままではインターネット側から MQTT ブローカーにアクセスができません。

**Azure Kubernetes Service (AKS)** や **Amazon Elastic Kubernetes Service (EKS)** 、 **Google Kubernetes Engine (GKE)** などの **クラウド環境の Kubernetes** で `type: LoadBalancer` を指定した場合は、自動的にグローバルIPアドレスが払い出されるため、そのままインターネット側から MQTT ブローカーにアクセスすることができます。

ですが、 **MetalLB** の場合には、あらかじめ指定したIPアドレスプールからローカルIPアドレスを払い出して使用します。
そのため、グローバルIPアドレスとローカルIPアドレスを紐付ける仕組みが必要になります。
このグローバルIPアドレスとローカルIPアドレスを変換する仕組みが **NAPT** や **IPマスカレード** と呼ばれるものです。

お使いのルーターにあわせて設定してください。

:::note warn
**注意**
NAPT の設定を行うと、インターネット側からアクセスできるようになるので、セキュリティには最善の注意を払ってください。
:::

## FriendlyWrt および OpenWrt の場合

`ネットワーク` → `ファイアウォール` → `ポートフォワーディング` と画面を開きます。

下記の情報を参考に NAPT の設定を行ってください。

|設定項目|設定値|備考|
|---|---|---|
|名前|MQTT|※任意の名前|
|アドレスファミリの制限|自動||
|プロトコル|TCP||
|送信元ゾーン|wan||
外部ポート|1883||
宛先ゾーン|lan||
内部IPアドレス|192.168.10.200|※MetalLB で払い出されたローカルIPアドレス|
内部ポート|1883||

※NanoPi R5C および FriendlyWrt については下記を参考にしてください。

https://qiita.com/IoriGunji/items/c94cf84cafffdcd947cc

# 参考文献

下記のサイトを参考にさせて頂きました。

https://artifacthub.io/packages/helm/t3n/mosquitto

https://blog.framinal.life/entry/2020/04/16/022042

<!-- EOF -->