はじめに
異なる機器間の通信を”トピック”と”メッセージ”という抽象化された概念でやり取りできるMQTTはとても便利ですよね。
PythonにもPaho-MQTTという便利なMQTT Clientライブラリがあり、これを使うことで簡単にMQTT通信ができます。
しかし、Pythonを使っていると複数バージョンの環境を構築する必要が生じるなど、何かと環境設定が煩雑になりがちではないでしょうか。こういった環境はDockerのコンテナごとにバージョンや用途を切り分け、ローカル環境を汚さないようにする使い方はわりと一般的ではないかと思います。
「MQTT通信を試してみよう!」の記事でパソコンとスマホを使ったMQTT通信の紹介をしましたが、こういった環境をDockerの仮想環境内で閉じておきたい方のために、その実施例を紹介します。
下図の環境を構築し、ローカルエリア内で複数のパソコンおよびスマホ間でMQTT通信を行う方法を説明します。
Dockerのインストール
Dockerをインストールするには以下URLからアカウント作成、インストールを行います。
https://docs.docker.com/install/
Dockerについては、過去の記事も参考にしてください。
Gitのインストール
Macでは最初からgitが使えるので新たにインストールする必要はありませんが、Windowsでは以下からgitをインストールしておくと便利です。
https://gitforwindows.org/
動作環境
以下の環境で動作確認しました。
使用機器 | OS | 使用ツール |
---|---|---|
Mac | macOS Catalina | Docker for Mac 2.1.0.5 |
Windows | Windows10 | Docker for Windows 2.1.0.5 |
スマホ | Android 8.0.0 | MQTT Dash |
Docker用ファイルのダウンロード
Macではターミナル、WindowsではPowerShellなどのコマンドラインツールを立ち上げ、作業場所まで移動(cd)してから以下のgitコマンドで必要なファイルをGitHubからダウンロードします。
Mac側とWindows側とも同じものをダウンロードすることで良いです。
git clone https://github.com/tak6uch1/paho-mqtt
以降の説明において、MacとWindowsは役割を入れ替えることもできますし、両方Macあるいは両方Windowsにすることもできますので、適宜読み替えて頂ければ結構です。
Mac側(Broker & Client 1)の環境構築および動作確認
ダウンロード(git clone)したファイルのうち、docker-compose.ymlは以下であり、全体の構成を表しています。
このファイルは特に編集しなくても良いです。
version: "3.4" services: mosquitto: image: eclipse-mosquitto restart: always ports: - "1883:1883" volumes: - ./mosquitto/config:/mosquitto/config - ./mosquitto/data:/mosquitto/data - ./mosquitto/log:/mosquitto/log entrypoint: - mosquitto networks: mqtt_net: ipv4_address: 172.19.0.11 paho: build: paho container_name: paho command: /usr/sbin/sshd -D ports: - "49944:22" volumes: - type: bind source: ./work target: /work networks: mqtt_net: ipv4_address: 172.19.0.12 networks: mqtt_net: ipam: config: - subnet: 172.19.0.0/16
コンテナはmosquitto(Broker)とpaho(Client)の2つで、mqtt_netというDocker内のネットワークに共に接続しています。
mosquittoのイメージは、既存のeclipse-mosquittoをそのまま使いますので、Dockerfileは用意していません。
pahoの方はpython:3.6のイメージに対し、開発環境でよく使うものをインストールしているのと、ある程度の使いやすさを考慮した設定を行っています。これらは適宜お好みに合わせて編集して構いません。
mosquittoとpahoにはそれぞれ固定のIPアドレスを割り当てています。
Clientの設定ファイルであるpaho/Dockerfileは以下のようになっており、4行目のyour_passwordを好きなパスワードに変更しておきます。
FROM python:3.6 ARG USER=user ARG GROUP=developer ARG PASS=your_password ENV DEBIAN_FRONTEND noninteractive ENV DEBCONF_NOWARNINGS yes # Common RUN apt-get update && apt-get install -y \ apt-transport-https \ libasound2-dev \ bash-completion \ build-essential \ bzip2 \ cmake \ curl \ gcc \ g++ \ git \ less \ libatlas-base-dev \ libgl1-mesa-dev \ locales \ locales-all \ make \ man \ manpages-dev \ mosquitto-clients \ net-tools \ openssh-server \ openssh-client \ p7zip \ postgresql-client \ software-properties-common \ sudo \ unzip \ vim \ wget \ xorg-dev \ zlib1g-dev \ zsh RUN groupadd -g 1000 $GROUP RUN useradd -g $GROUP -G sudo -m -s /bin/bash $USER RUN mkdir /var/run/sshd RUN echo "${USER}:${PASS}" | chpasswd RUN echo "root:${PASS}" | chpasswd # SSH settings. Otherwise user is kicked off after login RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd RUN sed -e 's@#Port 22@Port 22@' -e 's@#AddressFamily any@AddressFamily inet@' -i /etc/ssh/sshd_config # Locale ARG LANGVAL=en_US.utf8 RUN echo "export LANG=$LANGVAL" >> /etc/profile RUN echo "export LANGUAGE=$LANGVAL" >> /etc/profile RUN echo "export LC_CTYPE=$LANGVAL" >> /etc/profile RUN echo "export LC_ALL=$LANGVAL" >> /etc/profile ENV LANG $LANGVAL ENV LANGUAGE $LANGVAL ENV LC_CTYPE $LANGVAL ENV LC_ALL $LANGVAL RUN echo "$LANGVAL UTF-8" > /etc/locale.gen && \ locale-gen && \ update-locale LANG=$LANGVAL RUN cp /usr/share/zoneinfo/Japan /etc/localtime # paho-mqtt RUN pip3 install paho-mqtt #### User #### USER $USER WORKDIR /home/$USER # Settings RUN echo 'alias ls="ls -a --color=auto --show-control-chars --time-style=long-iso -FH"' >> /home/$USER/.profile RUN echo 'alias ll="ls -a -lA"' >> /home/$USER/.profile RUN echo 'alias h=history' >> /home/$USER/.profile RUN echo 'alias vi=vim' >> /home/$USER/.profile RUN echo 'PS1="\$ "' >> /home/$USER/.bashrc RUN touch /home/$USER/.Xauthority && chmod 600 /home/$USER/.Xauthority USER root ADD .vimrc /home/$USER/.vimrc ADD .gvimrc /home/$USER/.gvimrc RUN chown -R $USER:$GROUP /home/$USER/.*vim* RUN chmod -R 644 /home/$USER/.*vim* EXPOSE 22 EXPOSE 8080
上記ファイル内で変更したパスワードはSSHログインする際に必要になりますので覚えておいてください。
では、ビルドを行います。
docker-compose build
正常にビルドが終了したら、コンテナを立ち上げ、ClientにSSHログインします。ここで上記で変更したパスワードを使います。
docker-compose up -d ./run_ssh.sh
※Windowsの場合は./run_ssh.batを使います。
再ビルドしたりするとssh接続時にエラーが出るので、以下のようにリフレッシュすると良いです。(他にも方法はありますが、説明は他のサイトにお任せします)
rm ~/.ssh/known_hosts
ClientへのSSHログインに成功したら、以下のように作業ディレクトリまで移動し、lsを実行すると以下のように2つのファイルが表示されると思います。
cd /work/mqtt ls ./ ../ publisher.py subscriber.py
ここでSubscriberを立ち上げます。
python subscriber.py
すると次のように表示されて何も起きないと思いますが、これで問題ありません。
status 0
Paho-MQTTのソースコードはこちらのページを参考にさせて頂きました。
subscriber.pyの内容は下記の通り、test/messageというトピックのメッセージを受け取るとその内容を表示するプログラムになっています。
# -*- coding: utf-8 -*- import paho.mqtt.client as mqtt HOST = '172.19.0.11' PORT = 1883 KEEP_ALIVE = 60 TOPIC = 'test/message' def on_connect(client, userdata, flags, respons_code): print('status {0}'.format(respons_code)) client.subscribe(client.topic) """ def on_message(client, userdata, message): topicを受信したときに実行する """ def on_message(client, userdata, message): print(message.topic + ' ' + str(message.payload)) if __name__ == '__main__': client = mqtt.Client(protocol=mqtt.MQTTv311) client.topic = TOPIC client.on_connect = on_connect client.on_message = on_message client.connect(HOST, port=PORT, keepalive=KEEP_ALIVE) # ループ client.loop_forever()
次に別のターミナルを立ち上げ、同様にSSHログインして上記と同じ作業ディレクトリまで移動します。
この新しいターミナルからPulish(メッセージの送信)をしてみます。
python publisher.py
これで、先に立ち上げたSubscriberのターミナルに以下のようにhelloのメッセージが表示されればMQTT通信ができています。
test/message b'hello'
ただし、これは1つのDocker環境内に構築した同一ネットワーク(mqtt_net)につながる2つのClient間の通信に成功したに過ぎません。
ここでは単に確認のために同一ネットワーク内の通信を試しましたが、本来やりたかったのは他の機器との通信であるため、それを次節で行います。
publisher.pyの内容は以下で、test/messageというトピックでhelloというメッセージを送ります。
import paho.mqtt.client as mqtt HOST = '172.19.0.11' PORT = 1883 KEEP_ALIVE = 60 TOPIC = 'test/message' MESSAGE = 'hello' if __name__ == '__main__': client = mqtt.Client(protocol=mqtt.MQTTv311) client.connect(HOST, port=PORT, keepalive=KEEP_ALIVE) client.publish(TOPIC, MESSAGE) client.disconnect()
次節でこのパソコンのIPアドレス(DockerネットワークのIPアドレスとは異なる)を使用するため、調べておきます。
ifconfig
inet 192.168.179.2 (私の場合)のように表示されたものが、このパソコンのIPアドレスになります。
※Windowsの場合はipconfigコマンドでIPアドレスを調べます。
※BrokerをWindows上に立ち上げる際、ファイアーウォールに阻まれて他のマシンからのPublishメッセージをBrokerが受信できなかったので、このページを参考に受信の規則にTCP 1833ポートをプライベートネットワークに対して接続許可し、再起動することでうまく通信できるようになりました。
Windows側(Client 2)の環境構築および動作確認
同様にpaho/Dockerfile内のパスワードを変更します。
こちらはBrokerが必要ありませんので、ビルドとコンテナ立ち上げは、pahoのみとし、SSHログインまで実施します。
docker-compose build paho docker-compose up -d paho ./run_ssh.bat
※Macの場合は、./run_ssh.shを使います。
SSHログインに成功したら、同様に作業ディレクトリに移動します。
cd /work/mqtt
ここで、今度はもう一方のパソコンにPublishしたいので、publisher.pyの3行目HOST = ‘172.19.0.11’の部分を先ほど調べたもう一方のパソコンのIPアドレスに書き換えます(私の場合、HOST = ‘192.168.179.2’)。
IPアドレスを書き換えたらPublishします。
python publisher.py
これでもう一方のパソコンのSubscriberのターミナルに以下のメッセージが出ればパソコン間のMQTT通信は成功です!
test/message b'hello'
スマホ(Client 3)との通信
AndroidスマホのアプリMQTT Dashを使ってBrokerに接続します。
「MQTT通信を試してみよう!」と同様の内容ですので、ポイントのみ説明します。
まず、以下のようにName, Address, Portを設定します。
Addressは先ほどifconfigやipconfigで調べたBrokerのIPアドレス、Portは1883とします。
続いて、チェックボックスを作ります(例)。トピックは前節と同じtest/messageとしておき、チェックを入れると1を、チェックを外すと0を送るようにします。
できたチェックボックスをタップしてチェックを入れたり、チェックを外したりしてみます。
パソコンの Subscriberのターミナルに以下のようにメッセージが表示されれば、スマととパソコン間のMQTT通信も成功です!
test/message b'1' test/message b'0'
まとめ
Docker仮想環境上にMQTTブローカー(Mosquitto)を立ち上げ、Paho-MQTTを利用したクライアントプログラムからMQTT通信を行う方法を紹介しました。
またMQTT Dashを利用してAndroidスマホとの通信についても触れました。
機器の違いやプロトコルを意識する必要がなく、単に”トピック”と”メッセージ”で機器間通信ができる便利さを実感して頂けたのではないでしょうか。
MQTT通信をさまざまなアプリケーションに応用して頂けたらと思います。
書籍紹介
コンテナ型アプリケション開発の概念から運用まで説明されており、これまで何となくDockerを使っていた方が本格的に学ぶのに役立つ一冊です。
参考文献
- Paho-MQTT(https://pypi.org/project/paho-mqtt/)
- MQTT入門(導入編)(https://qiita.com/pocket8137/items/0205b7a1c0b38890523e)
- Windows10 – ファイアウォール – 特定のポート番号の通信を許可(https://pc-karuma.net/windows-10-firewall-open-port/)