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の方で受信できた。
とりあえず今回はここまでか。