资讯专栏INFORMATION COLUMN

用 docker 部署 mosquitto 并编译 mosquitto-auth-plug

maybe_009 / 2571人阅读

摘要:再运行一次它往发送了一个消息我们可以看到就已经收到了总结我们成功地搭建了一个的消息代理服务并为它编译了可以供后续后端消息业务的开发详细代码也可以参考已经上传到的

mosquitto 的简介

mosquitto 是一个开源的轻量级消息代理服务, 支持 MQTT-3.1 和 MQTT-3.1.1, 采用发布订阅模式. mosquitto 目前广泛用于手机设备, 底端传感器, 嵌入式计算机的消息通信, 是一个成熟的物联网通信服务方案. 作为一个用 C 编写的应用服务, mosquiitto 项目同样提供了 C library 便于 MQTT 服务的拓展, 比如有名的 mosquitto-auth-plug.

mosquitto 的部署

这里我们打算部署在 docker 容器内.

在 docker 内构建

参考 eclipse-mosquitto docker hub
构建目录 /mosquitto
目录结构是:

- mosquitto
    - config
        - mosquitto.conf
    - data
    - log

其中 mosquitto.conf 的内容是:

persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log
在 docker 内启动

启动命令:

$ docker run -d -p 1883:1883 -p 9001:9001 -v mosquitto/:/mosquitto/ eclipse-mosquitto
编译 mosquitto-auth-plug

参考: https://www.loraserver.io/gui...

准备工作

注意: 在 docker 内部构建编译环境的时候(尤其是在 alpine 环境中)会经常出现:

temporary error (try again later)

这是因为在获取像openssl,build-base之类包的时候, 需要从一些官方的镜像库中获取, 而由于国内的"某些"网络原因, DNS 往往不能直接解析到官方镜像库. 参考这篇文章:彻底解决docker build时安装软件失败问题, 可以用tcpdump观察这一现象. 所以我们最好先安排一下宿主机的 DNS.

查看本机 nameserver

根据阿里云ECS启动Docker容器无法访问外网这篇文章, 我们可以这样来获取本机 nameserver:

$ cat /etc/resolv.conf
nameserver 10.143.xx.xxx
nameserver 10.143.xx.xxy
options timeout:2 attempts:3 rotate single-request-reopen

这里我们可以看到有可能有多个域名解析服务地址, 这是一种容灾措施, 如果其中一个解析失败了, 在若干次尝试后会使用另外一个服务器去解析. 我们选取其中一个就好.

然后修改/etc/docker/daemon.json, 添加:

"dns": ["10.143.xx.xxx"]

重启守护进程:

$ sudo systemctl daemon-reload
// 最好也重启一下docker
$ sudo systemctl restart docker

当然, 如果网络环境足够好(比如在国外), 可以不需要配置 DNS. 即使不配置 DNS, 也有一定的几率安装软件都成功.

修改软件源为国内加速镜像

现在发现单纯改 DNS 还是经常会下载软件失败, 只好修改容器的软件源了. 在 Dockerfile 中将 aliyun 的软件源写入 /etc/apk/repositores:

RUN echo "http://mirrors.aliyun.com/alpine/v3.4/main/" > /etc/apk/repositories
创建宿主机文件目录结构

目录结构如下:

- mosquitto
    - Dockerfile
    - config
        - mosquitto.conf
        - config.mk
    - data
    - log
    - src
- postgres
    - Dockerfile
- docker-compose.yml

其中config放置 mosquitto 的启动配置mosquitto.conf和 mosquitto-auth-plug 的编译配置config.mk. data, log分别作为 mosquitto 的数据存储目录和日志存储目录, src用于放置编译需要的源文件.

配置 mosquitto.conf

mosquitto.conf 应该声明 mosquitto-auth-plug 插件编译之后生产的 auth-plug.so的位置, 并说明 postgres 数据库的连接用户, 连接端口, 连接密码, 以及处理权限和读写的 sql 语句. 参考:
Setting up ACL in Mosquitto using Postgres, 和MQTT authentication & authorization, 和mosquitto-auth-plug 在 github 上的 README.下面是一个可供参考和使用的 mosquitto.conf:

persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log

allow_anonymous false

auth_plugin /mosquitto/src/mosquitto-auth-plug/auth-plug.so
auth_opt_backends postgres

auth_opt_host pgsql_db
auth_opt_port 5432
auth_opt_dbname mosquitto
auth_opt_user root
auth_opt_pass root_password

auth_opt_userquery SELECT password FROM account WHERE username = $1 limit 1
auth_opt_superquery SELECT COALESCE(COUNT(*), 0) FROM account WHERE username = $1 AND super = 1
auth_opt_aclquery SELECT topic FROM acls WHERE (username = $1) AND rw >= $2
配置 config.mk

这里我们打算将 postgres 作为验证后端, 因此 config.mk 应该是这样的:

BACKEND_CDB ?= no
BACKEND_MYSQL ?= no
BACKEND_SQLITE ?= no
BACKEND_REDIS ?= no
BACKEND_POSTGRES ?= yes 
BACKEND_LDAP ?= no
BACKEND_HTTP ?= no
BACKEND_JWT ?= no
BACKEND_MONGO ?= no
BACKEND_FILES ?= no
BACKEND_MEMCACHED ?= no

# MOSQUITTO_SRC = /mosquitto/src/mosquitto
MOSQUITTO_SRC = 

OPENSSLDIR = /usr/lib
SUPPORT_DJANGO_HASHERS ?= no
CFG_LDFLAGS =
CFG_CFLAGS =

OPENSSLDIR 指定为 /usr/lib,
MOSQUITTO_SRC 指定为/mosquitto/src/mosquitto/src, 也可以不需要指定, 因为在容器中已经安装了 mosquitto, 编辑脚本可以从环境中获取源码地址, 这样甚至后面我们都不需要用 git 从 github 上拉取 mosquitto 的源码了, 注意: 这里最好不要指定, 因为在实际编译的时候经常会发生报错, 直接使用环境中即可.

编写 Dockerfile

镜像的源来自 eclipse mosquitto 的 Docker Hub, 基于 alpine linux.
这是我根据编译经验写的Dockerfile:

FROM eclipse-mosquitto
WORKDIR /mosquitto
ADD mosquitto/config /mosquitto/config

RUN apk update && 
    apk add wget && 
    apk add mosquitto-dev && 
    apk add postgresql-dev && 
    apk add openssl && 
    apk add build-base && 
    mkdir /mosquitto/src && 
    wget https://github.com/jpmens/mosquitto-auth-plug/archive/0.1.2.tar.gz 
        -O /mosquitto/src/mosquitto-auth-plug.tar.gz && 
    tar zxf /mosquitto/src/mosquitto-auth-plug.tar.gz -C /mosquitto/src && 
    mv /mosquitto/src/mosquitto-auth-plug-0.1.2 /mosquitto/src/mosquitto-auth-plug && 
    rm -rf /mosquitto/src/mosquitto_auth_plug.tar.gz && 
    mv /mosquitto/config/config.mk /mosquitto/src/mosquitto-auth-plug/config.mk && 
    cd /mosquitto/src/mosquitto-auth-plug && 
    make clean && make

EXPOSE 1881 9001

可以看到这里我们安装了以 postgresql 为后端编译 auth-plug 需要的依赖: mosquitto-dev, postgresql-dev, openssl, 和 C 语言的 GCC 编译环境build-base. 用 git 拉取了 mosquitto 和 mosqtuitto-auth-plug 的源码, 最好还是直接从mosquitto-auth-plug/release中获取发布的稳定版本, 最后使用自定义的 config.mk 进行 make 编译.

编写 docker-compose.yml

打算用容器编排工具 docker-compose 运行编译了 mosquitto-auth-plug 的 mosquitto 容器, 下面是一个可供使用和参考的 docker-compose.yml:

version: "3"
services:
  mosquitto:
    build: ./Dockerfile_mosquitto
    volumes:
      - ./mosquitto/config/mosquitto.conf:/mosquitto/config/mosquitto.conf
    command: ["mosquitto", "-c", "/mosquitto/config/mosquitto.conf"]
    ports:
      - "1881:1881"
      - "9001:9001"
部署 postgres 后端

前面已经把 mosquitto 部署成功, 但是由于这里我们打算让 mosquitto 的权限认证和读写认证交给 postgres(不然为什么我们要花那么费劲去编译 mosquitto-auth-plug?), 所以我们还要部署一个 mosquitto 可以访问的 postgres 后端或者在已有的 postgres 上创建相关的表结构. 注意 postgres 后端的用户, 密码等应该和 mosquitto.conf 上配置的一致.

编写 postgres 的 docker-compose.yml

直接在原有的 docker-compose.yml 上加一个 service:

services:
  ...
  postgres_db:
    image: postgres
    environment:
      POSTGRES_DB: mosquitto
      POSTGRES_USER: root
      POSTGRES_PASSWORD: root_password

这里我们不暴露 postgres 的 5432 端口, 因为 docker 内部的同一个网络可以直接通过容器名字来访问对应的容器, 而且我们不希望容器外的其他人可以访问数据库. 注意这里的POSTGERS_USER应该和 mosquitto.conf 中的auth_opt_user对应, 类似的也有POSTGRES_DB, POSTGRES_PASSWORD. 而容器名字postgres_db可以成为auth_opt_host的值, docker 会为这个 host 做 DNS 解析.

然后我们就可以直接运行 postgres 容器:

$ docker-compose up -d
初始化 postgres

为了满足 mosquitto-auth-plug 的业务需求, 需要为 postgres 生成对应的表, 可以参考mosquitto-auth-plug/examples/mysql.sql

CREATE TABLE account(
id SERIAL,
username TEXT NOT NULL,
password TEXT,
super smallint DEFAULT 0 NOT NULL,
PRIMARY KEY (id)
);

CREATE INDEX account_username ON account (username);

CREATE TABLE acls (
id SERIAL,
username TEXT NOT NULL,
topic TEXT NOT NULL,
rw INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (id)
);

CREATE UNIQUE INDEX acls_user_topic ON acls (username, topic);

这些 SQL 语句也可以写在 Dockerfile 里面作为第一次构建时就执行的.
接着就可以插入几条测试数据了. 用户的密码生成需要使用 mosquitto-auth-plug 提供的密码生成器, 它是以 PBKDF2 的形式储存在数据库后端的.

比如我们 进入 mosquitto 容器内:

$ docker exec -it  /bin/sh

为用户生成一个明文为123456的密码:

$ /mosquitto/src/mosquitto-auth-plug/np
Enter password:
Re-enter same password:
PBKDF2$sha256$901$Yf1FSeMi1j7OjWdW$u1MqFR7iIZj+m6P7vKDvuMx+oDDJW4ub

之后为 account 表内为test_user添加一条记录:

> INSERT INTO account(username, pasword, super) values (
    "test_user", 
    "PBKDF2$sha256$901$Yf1FSeMi1j7OjWdW$u1MqFR7iIZj+m6P7vKDvuMx+oDDJW4ub",
    0)

并设定test_user对于 topic /topic1为可读可写:

> INSERT INTO acls(username, topic, rw) values (
    "test_user",
    "/topic1",
    2)

现在数据库内的记录是:

 id | username |                              password                               | super 
----+----------+---------------------------------------------------------------------+-------
  1 | test_user | PBKDF2$sha256$901$S55aebUoogFy6XSf$E6PbQWDFQ06KLFlqqR3x+NBjg6ixjwez |     0


 id | username |   topic    | rw 
----+----------+------------+----
  1 | test_user| /topic1    | 2

意味着用户 test_user 可以用密码123456向 topic topic1 发布主题和订阅主题.

搭建应用环境进行测试

我们这里用 python 中同样实现了 mqtt 协议的 paho-mqtt 进行测试, 如果生产环境是使用 python 的话, 也可以直接使用这个库作为连接库.
安装很简单的:

$ pip install paho-mqtt
搭建一个 python-alpine 容器:

应用程序代码就放在 python-alpine 容器里好了. 下面是一个可供参考和使用的 docker-compose.yml:

services:
  mqtt_server:
    image: python-alpine
    command: ["tail"]

docker-compose up mqtt_server -d 使得该容器挂起, 然后用docker exec -it /bin/sh黑魔法进入容器. 编写一个测试用的订阅者(client.py):

import paho.mqtt.client as mqtt
import time

HOST = "mosquitto"
PORT = 1883

def client_loop():
    client_id = time.strftime("%Y%m%d%H%M%S", time.localtime(time.time()))
    client = mqtt.Client(
        client_id=client_id, clean_session=True)
    client.username_pw_set("test_user", "123456")
    client.on_connect = on_connect 
    client.on_message = on_message
    client.connect(HOST, PORT, 60)
    client.loop_forever()

def on_connect(client, userdata, flags, rc):
    print("Connected with result code " + str(rc))
    client.subscribe("/topic1")

def on_message(client, userdata, msg):
    print(msg.topic + " " + msg.payload.decode("utf-8"))

if __name__ == "__main__":
    client_loop()

然后编写一个测试用的发布者(publish.py):

import paho.mqtt.publish as publish
import time

HOST = "mosquitto"
PORT = 1883

if __name__ == "__main__":
    client_id = time.strftime("%Y%m%d%H%M%S", time.localtime(time.time()))
    publish.single("topic1", "hello mqtt", qos=2, hostname=HOST, port=PORT,
            client_id=client_id, auth={"username": "test_user", "password": "123456"})

当运行 client.py 之后, client 陷入监听 mosquitto 的状态, 等待收到发送在 topic topic1 的消息。 再运行一次 publish.py, 它往 topic1发送了一个消息, 我们可以看到 client.py 就已经收到了.

总结

我们成功地搭建了一个 mosquitto 的消息代理服务, 并为它编译了 mosquitto-auth-plug, 可以供后续后端消息业务的开发. 详细代码也可以参考已经上传到 github 的 example.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/27476.html

相关文章

  • mosquitto 与 websocket 的结合

    摘要:前言作为一个消息代理客户端与服务端的通信时基于协议的而现在的主流应用时呈现在浏览器中这意味着用户与服务端只能通过或者这类浏览器能理解的协议传输所以后端还要建立一个代理层将协议传输的内容解析一下以协议发送到最后再由发送到硬件端在浏览器支持的协 前言 mosquitto 作为一个消息代理, 客户端与 mosquitto 服务端的通信时基于 MQTT 协议的, 而现在的主流 web 应用时呈...

    joy968 评论0 收藏0
  • mosquitto 与 websocket 的结合

    摘要:前言作为一个消息代理客户端与服务端的通信时基于协议的而现在的主流应用时呈现在浏览器中这意味着用户与服务端只能通过或者这类浏览器能理解的协议传输所以后端还要建立一个代理层将协议传输的内容解析一下以协议发送到最后再由发送到硬件端在浏览器支持的协 前言 mosquitto 作为一个消息代理, 客户端与 mosquitto 服务端的通信时基于 MQTT 协议的, 而现在的主流 web 应用时呈...

    tomener 评论0 收藏0
  • 基于MQTT的物联网云测量解决方案

    摘要:本文是其中的一个解决方案。地址客户端服务端前端网页介绍,消息队列遥测传输是开发的一个即时通讯协议,有可能成为物联网的重要组成部分。必须用于在顶层分隔符之后,除了当自己指定时。 1. 问题描述 最近,本实验室大量上马云测量,云监控方面的项目,大概是属于物联网应用的一个分支。老板也有将旧有仪器改造的想法,所以要实现仪器设备的云控制。本文是其中的一个解决方案。 2. 技术选型 消息队列:M...

    张金宝 评论0 收藏0
  • PHP 使mosquiito

    摘要:近期业务需要,调研和使用基于协议的,开发一个消息推送系统。环境安装安装的扩展然后把加到重启编写代码,订阅为的消息想为,发布消息运行脚本发布消息脚本接受到消息并且打印出来脚本中,在收到消息后,会发布一个消息到为中,后台会接收消息 近期业务需要,调研和使用基于mqtt协议的mosquitto,开发一个消息推送系统。 环境:ubuntu 14.04,php7.1,mosquitto 安装mo...

    Developer 评论0 收藏0

发表评论

0条评论

maybe_009

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<