ブログに書くつもりじゃなかった

フリーのプログラマーが綴る、裏チラ系の備忘録や雑記帳。

nginxでMQTTを負荷分散する

はじめに

nginxを使ってMQTTを負荷分散できることを確認したい。NginxとMosquittoはDockerコンテナを使用する。また、メッセージ送信確認に使用するmosquitto-clientはホスト側にインストール済み。

事前準備

Docker Hubにあるnginxの公式イメージが使用可能か確認する。コンテナを起動してコンパイルフラグを確認する。

root@a7ef8471144d:/# nginx -V 2>&1 | grep -oP '\-\-with-[a-z_]+'
--with-compat
--with-file
--with-threads
--with-http_addition_module
--with-http_auth_request_module
--with-http_dav_module
--with-http_flv_module
--with-http_gunzip_module
--with-http_gzip_static_module
--with-http_mp
--wit-http_random_index_module
--with-http_realip_module
--with-http_secure_link_module
--with-http_slice_module
--with-http_ssl_module
--with-http_stub_status_module
--with-http_sub_module
--with-http_v
--with-mail
--with-mail_ssl_module
--with-stream
--with-stream_realip_module
--with-stream_ssl_module
--with-stream_ssl_preread_module
--with-cc
--with-ld

--with-streamを確認できたので使えるみたいだ。

環境構築

docker-compose.yml
version: '3'
services:

  broker1:
    image: eclipse-mosquitto
    container_name: broker1
    volumes:
      - ./mosquitto.conf:/mosquitto/config/mosquitto.conf

  broker2:
    image: eclipse-mosquitto
    container_name: broker2
    volumes:
      - ./mosquitto.conf:/mosquitto/config/mosquitto.conf

  nginx:
    image: nginx
    container_name: nginx
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    ports:
      - 1883:1883
mosquitto.conf
allow_anonymous true
listener 1883
nginx.conf
user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}

stream {
    upstream mqtt_brokers {
        server broker1:1883;
        server broker2:1883;
    }
    server {
        listen 1883;
        proxy_pass mqtt_brokers;
    }
}

動作確認その1

2つのブローカーそれぞれから自分に接続してトピックをサブスクライブ。

$ docker exec -it broker1 /bin/sh
/ # mosquitto_sub -t topic1
$ docker exec -it broker2 /bin/sh
/ # mosquitto_sub -t topic1

ホストからメッセージをパブリッシュする。

$ mosquitto_pub -t topic1 -m "It is $(date) now."

TCPコネクションの単位で負荷分散されて、ラウンドロビンでbroker1とbroker2に交互にメッセージが届くことを確認。ちなみにCONNECTしてPUBLISHしてDISCONNECTまでの一連のMQTTパケットは、HTTPのKeep-Aliveみたいに1つのTCPコネクションの中で送信されていた。MQTTパケット毎に別々のブローカーに届いたら予期しない動作をするのでは?という事は杞憂に過ぎない。はずである。そう願いたい。

動作確認その2

ブローカーが止まった時の動作も見てみよう。最後にbroker1でメッセージを受け取ったあと、broker2を落とす。

$ docker stop broker2

クライアントからメッセージをパブリッシュする。

$ mosquitto_pub -t topic1 -m "It is $(date) now."

20秒弱待ったあとでbroker1の方で受信できた。tcpdumpで通信内容を見るとこんな動きをしていた。

  • broker2に対して接続を実行。
  • 約1秒後にbroker2に対して接続リトライ(1回目)を実行。
  • 約2秒後にbroker2に対して接続リトライ(2回目)を実行。
  • 約4秒後にbroker2に対して接続リトライ(3回目)を実行。
  • 約12秒後にbroker1に対して接続リトライ(4回目)を実行。

この辺りのリトライの上限や間隔は設定値で変えられるはず。今後の調査課題としよう。また、この状態でしばらく放置したが、ヘルスチェックみたいな動きはなかった。ドキュメントを見る限り、この辺は別モジュールの機能っぽいな。

broker2を復活させる。

$ docker start broker2

broker2自身に接続してトピックをサブスクライブ。

$ docker exec -it broker2 /bin/sh
/ # mosquitto_sub -t topic1

ホストからnginxに向けてメッセージをパブリッシュする。3回目の実行でbroker2の方で受信できた。

とりあえず今回はここまでか。