コネクテッドデバイス向けのMQTTブローカーにMosca を使っていましたが、ブローカーにMQTT以外のプロトコルも使えると便利です。マルチプロトコルをサポートしていてMQTTにはMoscaを使っているPonteというブローカーがあるのでこちらを試してみます。Ponte はEclipse Foundation のIoT Working Group、iot.eclipse.org 配下のプロジェクトです。プロトコルにHTTP、MQTT、CoAP の3つをサポートしているのが特徴です。HTTPでpublishしてMQTTでsubscribeすることができます。
PonteのSSL/TLSサポート SSL/TLSのサポートは直接されていませんが、MQTTはMoscaのconfigでプロトコルを指定することができます。HTTPは別途NginxでSSL Terminationをする予定です。今回はMQTT over SSL/TLSの設定を行います。
Brokerコンテナ BrokerコンテナのDockerイメージ用のプロジェクトを作成します。
$ mkdir -p ~/docker_apps/ponte $ cd !$
Dockerfile TLSで使う証明書はとりあえず暗号化できれば良いので、オレオレ証明書を作ります。またDockerベースイメージはgoogle/nodejs を使います。
~/docker_apps/ponte/Dockerfile FROM google/nodejs RUN apt-get update && apt-get install -y libzmq-dev WORKDIR /app ONBUILD ADD package.json /app/ ONBUILD RUN npm install ONBUILD ADD . /app RUN npm install --unsafe-perm -g ponte bunyan RUN mkdir -p /opt/nginx/certs \ && cd /opt/nginx/certs \ && openssl genrsa -out server.key 4096 \ && openssl req -new -batch -key server.key -out server.csr \ && openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt ADD config.js /app/ ENTRYPOINT ["ponte" , "-c" ,"/app/config.js" ,"-v" , "| bunyan" ] EXPOSE 8443 3000 5683 RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
npmのグローバルインストール 通常Node.jsのパッケージインストールはpackage.jsonに指定してnpm install
しますが、グローバルにインストールしようとするといろいろと問題があります。そのためpackage.jsonには指定せずDockerfileに記述します。
~/docker_apps/ponte/package.json { "name" : "ponte" , "description" : "ponte test app" , "version" : "0.0.1" , "private" : true }
またgyp WARN EACCES user "undefined" does not have permission
の警告がでてしまうため、--unsafe-perm
フラグを付けます。
RUN npm install --unsafe-perm -g ponte bunyan
Ponteの設定ファイル Ponte起動時にJSファイルで各プロトコルの設定を渡すことができます。MQTTにはオレオレ証明書と秘密鍵のパスを指定します。
~/docker_apps/ponte/config.js module .exports = { logger: { level: "info" , }, persistence: { type: 'level' , path: './db' }, mqtt: { secure : { port: 8443 , keyPath:"/opt/nginx/certs/server.key" , certPath:"/opt/nginx/certs/server.crt" } } }
Ponteの起動 Dockerイメージのビルドとコンテナの起動をします。MQTT以外にもHTTPとCoAPのポートもDockerホストにマップしておきます。
$ docker pull google/nodejs $ docker build -t ponte . $ docker run -d --name ponte \ -p 8443:8443 \ -p 3000:3000 \ -p 5683:5683 \ -it ponte
ログを見ると3つのプロトコルで起動したことがわかります。
$ docker logs -f ponte {"name" :"ponte" ,"hostname" :"17e232fd73f8" ,"pid" :1,"service" :"MQTT" ,"level" :30,"mqtts" :8443,"msg" :"server started" ,"time" :"2015-03-08T04:57:26.910Z" ,"v" :0} {"name" :"ponte" ,"hostname" :"17e232fd73f8" ,"pid" :1,"service" :"HTTP" ,"level" :30,"port" :3000,"msg" :"server started" ,"time" :"2015-03-08T04:57:26.918Z" ,"v" :0} {"name" :"ponte" ,"hostname" :"17e232fd73f8" ,"pid" :1,"service" :"CoAP" ,"level" :30,"port" :5683,"msg" :"server started" ,"time" :"2015-03-08T04:57:26.919Z" ,"v" :0}
Pub/Subクライアントのコンテナ Pub/SubクライアントのDockerイメージを作成するためプロジェクトを作成します。
$ mkdir -p ~/docker_apps/ponte-pubsub $ cd !$
Dockerfile Node.jsのパッケージをインストールするためpackage.jsonを用意します。日付処理に便利なMoment.js も追加しました。
~/docker_apps/ponte-pubsub/package.json { "name" : "ponte-pubsub" , "description" : "ponte-pubsub test app" , "version" : "0.0.1" , "private" : true , "dependencies" : { "mqtt" : "1.1.0" , "moment" : "2.9.0" , "moment-timezone" : "0.3.0" }, "scripts" : {"start" : "node app.js" } }
Node.jsのメインプログラムです。3秒間隔でPublishをして同じMQTTクライアントを使ってSubscribeします。
~/docker_apps/ponte-pubsub/app.js var mqtt = require ('mqtt' ), moment = require ('moment-timezone' ); var options = { protocol: "mqtts" , port: process.env.MQTT_PORT, host: process.env.MQTT_HOST, username: process.env.MQTT_USERNAME, password: process.env.MQTT_PASSWORD, clientId: "secure-" .concat(Math .floor(65535 * Math .random())), rejectUnauthorized : false }; var client = mqtt.connect(options);client.on('connect' , function ( ) { console .log('client connected' ); }); client.subscribe('hello' ); client.on('message' ,function (topic, data ) { console .log("Message on " + topic + ": " + data); }); setInterval (function ( ) { var now = moment().tz("Asia/Tokyo" ).format("YYYY-MM-DD HH:mm:ssZ" ); client.publish('hello' , now); console .log('Message Published' ); }, 3000 );
コンテナを使ってPub/Subのテスト Dockerfileを作成してイメージをビルドします。
$ echo FROM google/nodejs-runtime > Dockerfile $ docker pull google/nodejs-runtime $ docker build -t ponte-pubsub .
使い捨てのコンテナを起動してPub/Subのテストをします。でMQTTクライアントの設定を環境変数に渡します。
$ docker run --rm --name ponte-pubsub \ -e MQTT_HOST=10.3.0.230 \ -e MQTT_PORT=8443 \ -e MQTT_USERNAME=admin \ -e MQTT_PASSWORD=password \ ponte-pubsub > ponte-pubsub@0.0.1 start /app > node app.js secure-46408 client connected Message Published Message on hello: 2015-03-08 13:47:58+09:00
Ponteコンテナのログにもhelloトピックのsubscribeの様子が出力されています。
{"name" :"ponte" ,"hostname" :"17e232fd73f8" ,"pid" :1,"service" :"MQTT" ,"client" :"secure-58538" ,"level" :30,"msg" :"client connected" ,"time" :"2015-03-08T08:23:29.734Z" ,"v" :0} {"name" :"ponte" ,"hostname" :"17e232fd73f8" ,"pid" :1,"service" :"MQTT" ,"client" :"secure-58538" ,"level" :30,"topic" :"hello" ,"qos" :0,"msg" :"subscribed to topic" ,"time" :"2015-03-08T08:23:29.742Z" ,"v" :0} {"name" :"ponte" ,"hostname" :"17e232fd73f8" ,"pid" :1,"service" :"MQTT" ,"client" :"secure-58538" ,"level" :30,"topic" :"hello" ,"msg" :"unsubscribed" ,"time" :"2015-03-08T08:23:36.788Z" ,"v" :0} {"name" :"ponte" ,"hostname" :"17e232fd73f8" ,"pid" :1,"service" :"MQTT" ,"client" :"secure-58538" ,"level" :30,"msg" :"closed" ,"time" :"2015-03-08T08:23:36.793Z" ,"v" :0}