Featured image of post 如何快速部署开源服务器

如何快速部署开源服务器

快速部署MQTT消息服务器

背景

当需要提供一种新能力,考虑到成本问题,我们往往不会从0到1开发一个服务器,而是先在开源产品中选型对比,看是否已有能满足自身需求的开源产品(不重复造轮子的原则)。因为对开源服务器的不熟悉,部署过程中会踩这样那样的坑,效率比较低,如何快速部署并应用于实际业务场景呢?
随便提一下,私有化部署的原因主要是考虑到数据安全,安全无小事,安全大于一切

准备工作

  1. 云主机(内部安装了docker、docker-compose、防火墙开放指定端口)
  2. 个人电脑(内部安装了docker-desktop、xshell、vscode、golang)
  3. 仔细看官方文档以及官方开源社区

注意:emqx是4.4.4版本

部署

预期效果

(配置文件、日志)文件/目录挂载到本地磁盘
监控、告警、可视化等功能
协议接入、集群部署、接入安全

具体流程

个人电脑 -> 云主机
单机版 -> 集群版(负载均衡)

启动

仔细检查docker-compose-emqx-cluster.yaml 配置文件,否则会启动失败

启动命令: docker-compose -f docker-compose-emqx-cluster.yaml up &
退出命令: docker-compose -f docker-compose-emqx-cluster.yaml down

接入安全

服务端访问控制

在loaded_plugins配置文件中启用emqx_auth_mysql插件。

1
2
3
4
5
6
7
8
9
{emqx_management,true}.
{emqx_dashboard,true}.
{emqx_modules,false}.
{emqx_recon,true}.
{emqx_retainer,true}.
{emqx_telemetry,true}.
{emqx_rule_engine,true}.
{emqx_bridge_mqtt,false}.
{emqx_auth_mysql,true}.
emqx默认是不设置权限,需要修改acl.conf启用鉴权。
1
2
3
{allow, {user, "dashboard"}, subscribe, ["$SYS/#"]}.
{allow, {ipaddr, "127.0.0.1"}, pubsub, ["$SYS/#", "#"]}.
{deny, all, subscribe, ["$SYS/#", {eq, "#"}]}.
修改emqx_auth_mysql.conf配置文件:修改数据库实例,创建认证表和授权表。
用户名/密码认证防止非法客户端连接。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
CREATE TABLE `mqtt_user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(100) DEFAULT NULL,
  `password` varchar(100) DEFAULT NULL,
  `salt` varchar(35) DEFAULT NULL,
  `is_superuser` tinyint(1) DEFAULT '0',
  `created` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `mqtt_username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4

对 MQTT 客户端的发布和订阅操作进行权限控制。控制哪些客户端可以发布或者订阅哪些MQTT主题。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
CREATE TABLE `mqtt_acl` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `allow` int(1) DEFAULT '1' COMMENT '0: deny, 1: allow',
  `ipaddr` varchar(60) DEFAULT NULL COMMENT 'IpAddress',
  `username` varchar(100) DEFAULT NULL COMMENT 'Username',
  `clientid` varchar(100) DEFAULT NULL COMMENT 'ClientId',
  `access` int(2) NOT NULL COMMENT '1: subscribe, 2: publish, 3: pubsub',
  `topic` varchar(100) NOT NULL DEFAULT '' COMMENT 'Topic Filter',
  PRIMARY KEY (`id`),
  KEY `ipaddr` (`ipaddr`),
  KEY `username` (`username`),
  KEY `clientid` (`clientid`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4

客户端SDK安全

SDK引入了第三方库, 要走中台统一的三方库报备、审查,看看有没有漏洞或其他风险 。

集群部署

实际业务场景可能对高性能、高可用有着一定要求。这里以伪分布式集群为例:
image.png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
version: '3.9'

x-default-emqx: &default-emqx
    image: emqx/emqx:4.4.4
    env_file:
      - conf.cluster.env
    healthcheck:
      test: ["CMD", "/opt/emqx/bin/emqx_ctl", "status"]
      interval: 5s
      timeout: 25s
      retries: 5

services:
  haproxy:
    container_name: haproxy
    image: haproxy:2.4
    privileged: true
    depends_on:
      - emqx1
      - emqx2
    volumes:
      - ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
      - ./certs_ha/certs:/usr/local/etc/haproxy/certs
    ports:
      - "18083:18083"
      - "1883:1883"
#     - "8883:8883"
      - "8083:8083"
#     - "8084:8084"
      - "9090:9090"
      - "8081:8081"
    networks:
      - emqx_bridge
    working_dir: /usr/local/etc/haproxy
    command:
      - bash
      - -c
      - |
        set -x
        cat /usr/local/etc/haproxy/certs/cert.pem /usr/local/etc/haproxy/certs/key.pem > /tmp/emqx.pem
        haproxy -f /usr/local/etc/haproxy/haproxy.cfg        

  emqx1:
    <<: *default-emqx
    container_name: node1.emqx.io
    environment:
      - "EMQX_HOST=node1.emqx.io"
    ports:
      - "18081:18083"
      - "8881:8083"
      - "1882:1883"
      - "18087:8081"
    networks:
      emqx_bridge:
        aliases:
        - node1.emqx.io
    volumes:
      - ./dockeremqx/emqx.conf:/opt/emqx/etc/emqx.conf
      - ./dockeremqx/acl.conf:/opt/emqx/etc/acl.conf
      - ./dockeremqx/emqx_auth_mysql.conf:/opt/emqx/etc/plugins/emqx_auth_mysql.conf
      - ./dockeremqx/loaded_plugins:/opt/emqx/data/loaded_plugins
      - ./dockeremqx/emqx1_log/log:/opt/emqx/log

  emqx2:
    <<: *default-emqx
    container_name: node2.emqx.io
    environment:
      - "EMQX_HOST=node2.emqx.io"
    ports:
      - "18082:18083"
      - "8882:8083"
      - "1881:1883"
      - "18088:8081"
    networks:
      emqx_bridge:
        aliases:
        - node2.emqx.io
    volumes:
      - ./dockeremqx/emqx.conf:/opt/emqx/etc/emqx.conf
      - ./dockeremqx/acl.conf:/opt/emqx/etc/acl.conf
      - ./dockeremqx/emqx_auth_mysql.conf:/opt/emqx/etc/plugins/emqx_auth_mysql.conf
      - ./dockeremqx/loaded_plugins:/opt/emqx/data/loaded_plugins
      - ./dockeremqx/emqx2_log/log:/opt/emqx/log

networks:
  emqx_bridge:
    driver: bridge
    name: emqx_bridge
    # ipam:
    #   driver: default
    #   config:
    #     - subnet: 172.100.239.0/24
    #       gateway: 172.100.239.1
image.png

协议接入

应用层协议包括mqtt、websocket、http。负载均衡器方面的配置非常关键,下面以haproxy为例(核心配置):
粘性会话是在haproxy中配置的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
defaults
    log global
    mode http
    option httplog
    # Replace 1024000 with deployment connections
    maxconn 50000
    timeout connect 30000
    timeout client 600s
    timeout server 600s
    timeout tunnel 1h
    timeout client-fin 30s
    timeout http-keep-alive  5s
    timeout http-request    5s
frontend emqx_tcp
   mode tcp
   option tcplog
   bind *:1883
   # Reject connections that have an invalid MQTT packet
   # tcp-request content reject unless { req.payload(0,0), mqtt_is_valid }
   default_backend emqx_tcp_back
frontend emqx_ws
   mode tcp
   option httplog
   bind *:8083
   acl host_ws hdr_beg(Host) -i ws.
   acl hdr_connection_upgrade hdr(Connection)  -i upgrade
   acl hdr_upgrade_websocket  hdr(Upgrade)     -i websocket
   use_backend emqx_ws_back if hdr_connection_upgrade hdr_upgrade_websocket
   default_backend emqx_ws_back
backend emqx_tcp_back
    mode tcp
    stick on req.payload(0,0),mqtt_field_value(connect,client_identifier)
    server emqx-1 node1.emqx.io:1883 check-send-proxy send-proxy-v2
    server emqx-2 node2.emqx.io:1883 check-send-proxy send-proxy-v2
backend emqx_ws_back
    mode http
    balance roundrobin
    acl hdr_connection_upgrade hdr(Connection)                 -i upgrade
    acl hdr_upgrade_websocket  hdr(Upgrade)                    -i websocket
    acl hdr_websocket_key      hdr_cnt(Sec-WebSocket-Key)      eq 1
    acl hdr_websocket_version  hdr_cnt(Sec-WebSocket-Version)  eq 1
    server emqx-1 node1.emqx.io:8083
    server emqx-2 node2.emqx.io:8083

测试

功能测试

目标:连接、发布、订阅等功能符合预期。
EMQX是否正在运行的判断方法:正常进入管理后台界面、正常监听某些指定端口。
运行指定语言的客户端SDK来进行关键功能测试,比如golang版本的SDK paho.golang ,JS版本的SDK mqtt-client-WebSocket 。当然也可以通过官方推荐的可视化工具来测试。

测试方法

测试点 期望结果 具体说明
客户端断线重连 emqx重启后,客户端自动重连 取决于客户端SDK是否实现了心跳重连
消息下发成功率 正常情况下,QoS 1报文不会丢失消息 Broker提供3种QoS,通常选择QoS 1报文(至少送达一次)来兼顾吞吐量和发布质量。
消息状态码 每次操作都能拿到实时且正确的状态码 状态码是MQTT 5.0新特性,客户端SDK需要支持5.0协议

image.png

性能测试

目标:确认是否与官方宣称的性能接近(机器硬件条件相同情况下),提前暴露可能存在的BUG

测试方法

一般官方都会提供配套的压测工具脚本,比如emqtt-bench开源项目,将其运行在云主机,压测效果最好,减少网络传输时延。

1
 ./emqtt_bench sub -t bench -h ${ip} -p 1883 -c 1000 -i 10 -q 1 -u ${username}-P ${password}  

压测注意事项

压测客户端被限制:ulimit命令修改进程最大连接数限制,否则会压测不准,最大并发连接数卡在1007上不去。
发布端发送消息速率过快: max_mqueue_len参数默认为1000,队列满会消息发送失败
负载均衡器maxconn设置过小:haproxy的maxconn默认值是1000,需要调大为50000
image.png

总结

搭建开源服务器,需要实现协议接入、集群部署、接入安全等核心功能来满足业务需求,最终目的是为业务带来一定的价值。

最后更新于 2024年03月22日 11点44分33秒
慢慢来,欣赏啊
Built with Hugo
主题 StackJimmy 设计