Kubernetes 上で EMQX クラスタをデプロイする徹底ガイド

EMQX を Kubernetes 上にデプロイするには、Helm チャートを利用した簡易手法と、カスタムリソースで完全制御する手法の2通りがあります。以下では、実用的な構成を段階的に解説します。

Helm を用いた迅速なデプロイ

EMQX の公式 Helm リポジトリを追加し、デフォルト設定でクラスタを起動します。

helm repo add emqx https://repos.emqx.io/charts
helm repo update
helm install emqx-cluster emqx/emqx --set service.type=NodePort --set service.nodePorts.dashboard=30083

デプロイ後、Pod の状態とクラスタの接続状況を確認します。

kubectl get pods -l app.kubernetes.io/instance=emqx-cluster
kubectl exec emqx-cluster-0 -- emqx_ctl status
kubectl exec emqx-cluster-0 -- emqx_ctl cluster status

サービスのエンドポイントを確認すると、NodePort で外部アクセス可能なポートが割り当てられています。

kubectl get svc emqx-cluster
# 出力例: emqx-cluster   NodePort   10.96.23.45   <none>   1883:31000/TCP,18083:30083/TCP

クラスタのスケールアップは、`helm upgrade` で簡単に実行できます。

helm upgrade emqx-cluster emqx/emqx --set replicaCount=5

永続化クラスタの構築

EMQX のメッセージ状態やクライアントセッションを永続化するには、/opt/emqx/data/mnesia ディレクトリを PVC にマウントします。StorageClass が事前に定義されている前提で、以下のように設定します。

helm install emqx-persistent emqx/emqx \
  --set persistence.enabled=true \
  --set persistence.storageClassName=fast-ssd \
  --set persistence.size=2Gi

各 Pod は、自動的に個別の PVC を生成し、Pod の再スケジュール時にもデータを保持します。

kubectl get pvc -l app.kubernetes.io/instance=emqx-persistent
# 出力例:
# NAME                            STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
# emqx-data-emqx-persistent-0     Bound    pvc-7a1b2c3d-4e5f-6789-0123-456789abcdef   2Gi        RWO            fast-ssd       2m
# emqx-data-emqx-persistent-1     Bound    pvc-8b2c3d4e-5f67-8901-2345-6789abcdef01   2Gi        RWO            fast-ssd       1m

カスタムリソースによる完全制御

StatefulSet と Headless Service の構成

EMQX クラスタは、ノード間の安定した通信と一意のホスト名が必要です。そのため、StatefulSetHeadless Service を組み合わせて構成します。

Headless Service(ClusterIP: None)は、各 Pod に一意の DNS 名を提供します。

apiVersion: v1
kind: Service
metadata:
  name: emqx-headless
spec:
  clusterIP: None
  selector:
    app: emqx
  ports:
    - name: mqtt
      port: 1883
      targetPort: 1883
    - name: dashboard
      port: 18083
      targetPort: 18083

StatefulSet では、Pod の名前が emqx-0emqx-1 のように順序付きで生成され、DNS 名は emqx-0.emqx-headless.default.svc.cluster.local の形式になります。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: emqx
spec:
  serviceName: emqx-headless
  replicas: 3
  selector:
    matchLabels:
      app: emqx
  template:
    metadata:
      labels:
        app: emqx
    spec:
      containers:
      - name: emqx
        image: emqx/emqx:5.7.1
        ports:
        - containerPort: 1883
        - containerPort: 18083
        env:
        - name: EMQX_NAME
          value: "emqx"
        - name: EMQX_CLUSTER__DISCOVERY_STRATEGY
          value: "k8s"
        - name: EMQX_CLUSTER__K8S__APISERVER
          value: "https://kubernetes.default.svc:443"
        - name: EMQX_CLUSTER__K8S__SERVICE_NAME
          value: "emqx-headless"
        - name: EMQX_CLUSTER__K8S__NAMESPACE
          value: "default"
        - name: EMQX_CLUSTER__K8S__ADDRESS_TYPE
          value: "hostname"
        - name: EMQX_CLUSTER__K8S__SUFFIX
          value: "svc.cluster.local"
        volumeMounts:
        - name: emqx-data
          mountPath: /opt/emqx/data/mnesia
  volumeClaimTemplates:
  - metadata:
      name: emqx-data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 2Gi

RBAC 設定による API アクセス権限付与

EMQX は Kubernetes API を利用してクラスターノードを自動検出するため、ServiceAccount に適切な権限を付与する必要があります。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: emqx-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: emqx-role
rules:
- apiGroups: [""]
  resources: ["endpoints", "pods"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: emqx-binding
subjects:
- kind: ServiceAccount
  name: emqx-sa
  namespace: default
roleRef:
  kind: Role
  name: emqx-role
  apiGroup: rbac.authorization.k8s.io

StatefulSet のテンプレートにこの ServiceAccount を指定します。

spec:
  template:
    spec:
      serviceAccountName: emqx-sa

ConfigMap による設定分離

環境ごとの設定差異を管理するため、EMQX の設定は ConfigMap で外部化します。

apiVersion: v1
kind: ConfigMap
metadata:
  name: emqx-config
data:
  EMQX_LISTENER__TCP__DEFAULT: "1883"
  EMQX_DASHBOARD__LISTENER__HTTP: "18083"
  EMQX_CLUSTER__DISCOVERY_STRATEGY: "k8s"
  EMQX_CLUSTER__K8S__APISERVER: "https://kubernetes.default.svc:443"
  EMQX_CLUSTER__K8S__SERVICE_NAME: "emqx-headless"
  EMQX_CLUSTER__K8S__NAMESPACE: "default"
  EMQX_CLUSTER__K8S__ADDRESS_TYPE: "hostname"
  EMQX_CLUSTER__K8S__SUFFIX: "svc.cluster.local"

StatefulSet のコンテナに環境変数としてマウントします。

envFrom:
- configMapRef:
    name: emqx-config

永続化ストレージの運用

StatefulSet の volumeClaimTemplates は、各 Pod に対して個別の PVC を自動生成します。これにより、ノードが再起動しても、そのノード固有のメッセージデータとセッション情報が保持されます。

StorageClass が local-ssd などの高性能ストレージに設定されている場合、I/O 性能が向上し、高負荷時の安定性が確保されます。

volumeClaimTemplates:
- metadata:
    name: emqx-data
  spec:
    accessModes: ["ReadWriteOnce"]
    storageClassName: local-ssd
    resources:
      requests:
        storage: 5Gi

クラスタを削除しても、PVC はデフォルトで保持されるため、データ復旧が必要な場合に備えて、明示的に削除する必要があります。

kubectl delete statefulset emqx
kubectl delete pvc -l app=emqx

EMQX Edge と EMQX Enterprise の差異

EMQX Edge は軽量版で、リソース制約のあるエッジデバイス向けです。イメージ名を変更するだけでデプロイ可能です。

helm install emqx-edge emqx/emqx \
  --set image.repository=emqx/emqx-edge \
  --set image.tag=5.7.1

EMQX Enterprise はライセンスキーが必要です。Secret にライセンスファイルを登録し、デプロイ時に指定します。

kubectl create secret generic emqx-license --from-file=./emqx.lic
helm install emqx-enterprise emqx/emqx-ee \
  --set emqxLicenseSecretName=emqx-license

運用上の注意点

  • EMQX のクラスタサイズは奇数ノード(3, 5, 7)が推奨。偶数ではクォーラム形成が不安定になる可能性があります。
  • NodePort は環境ごとに変動するため、プロダクションでは Ingress や LoadBalancer を使用してください。
  • TLS 証明書は、Ingress や外部ロードバランサで終端するのがセキュリティ上推奨です。
  • EMQX 5.7 以降では、cluster.discoverycluster.discovery_strategy に変更されています。設定名の変更を忘れずに。

これらの構成により、EMQX は Kubernetes 上で高可用性かつスケーラブルな MQTT ブローカーとして運用可能になります。

タグ: Kubernetes EMQX Helm StatefulSet HeadlessService

6月8日 18:25 投稿