跳到主要内容

Install-EMQX-Offline-In-CentOS8

一、相关链接

二、环境介绍

名称版本备注
操作系统CentOS 8.5.2111阿里云镜像站 CentOS 8.5
CentOS依赖-阿里云镜像站 BaseOS Packages
EMQX安装包EMQX-v5.8.5RHEL 8(CentOS 8) amd64 / tar.gz
集群网关Nginx-1.26.3nginx-1.26.3
集群网关HAproxy-3.1.5haproxy-3.1.5

三、准备工作

由于生产服务器无法连接外网,可以先在本地搭建一套与生产版本一致的虚拟机,所有需要离线安装的软件包都预先在虚拟机中进行安装测试,保证兼容性及依赖包的完整性,避免到生产服务器出现不兼容或缺少依赖的请况。

1. 系统安装

系统安装与CentOS7之前有较大区别,可以参考这篇文章 重点是在安装信息摘要页面,按照以下顺序设置

  1. Root用户密码
  2. 网络和主机名 此处的MAC地址需要和虚拟机设置-网络页面的MAC地址一致
  3. 安装目的地
  4. 安装源 http://mirrors.aliyun.com/centos/8/BaseOS/x86_64/os/
  5. 软件选择 最小安装(yum ssh curl ping ip)

如果要设置固定IP,可以修改网络配置文件,手动设置IP、MAC地址、网关、子网掩码、DNS等信息

# 查看网络配置
cat /etc/sysconfig/network-scripts/ifcfg-enp0s3

# 修改网络配置
vim /etc/sysconfig/network-scripts/ifcfg-enp0s3
BOOTPROTO=static
IPADDR=192.168.137.101
# MACADDR=08:00:27:D7:88:87
HWADDR=08:00:27:D7:88:87
GATEWAY=192.168.137.1
NETMASK=255.255.255.0
DNS1=114.114.114.114

# 重启网络服务
systemctl restart NetworkManager

# 测试与宿主机 公网的网络互通
ping -c4 192.168.137.1
ping -c4 jd.com

2. 修改yum源

Errors during downloading metadata for repository 'appstream':
- Curl error (6): Couldn't resolve host name for http://mirrorlist.centos.org/?release=8&arch=x86_64&repo=AppStream&infra=stock [Could not resolve host: mirrorlist.centos.org]
错误:为仓库 'appstream' 下载元数据失败 : Cannot prepare internal mirrorlist: Curl error (6): Couldn't resolve host name for http://mirrorlist.centos.org/?release=8&arch=x86_64&repo=AppStream&infra=stock [Could not resolve host: mirrorlist.centos.org]

由于 CentOS 团队已于 2021 年 12 月 31 日对 CentOS Linux 8 停止更新和维护,并从官方镜像中移除 CentOS 8 的所有包,因此导致在使用 yum 源安装或更新会报上述错误,解决方案可参考这篇文章,下面直接用阿里云软件源替换

  1. 备份软件源
# 进入仓库源文件目录下
cd /etc/yum.repos.d/

# 备份
mkdir /etc/yum.repos.d/backup
mv /etc/yum.repos.d/* /etc/yum.repos.d/backup/

  1. 使用阿里云软件源
# 如果没有wget,可以用curl下载 
# wget http://mirrors.aliyun.com/repo/Centos-8.repo -O /etc/yum.repos.d/CentOS-Linux-BaseOS.repo
curl http://mirrors.aliyun.com/repo/Centos-8.repo -o /etc/yum.repos.d/CentOS-Linux-BaseOS.repo

  1. 更新软件源
# 清空原有缓存并重新生成
yum clean all && yum makecache

# 更新软件包(可选)
yum update -y

3. 查找依赖包

由于离线安装时无法在线安装依赖包,故需要提前准备好相关的依赖包,如何查询并安装依赖包可以参考这篇文章

  1. 安装依赖
# 如果无法在线安装,可以在阿里云镜像站找到 https://mirrors.aliyun.com/centos/8.5.2111/BaseOS/x86_64/os/Packages/
yum -y install yum-utils

# 测试下载依赖
mkdir -p /root/lib/{tar,unzip,wget,vim,net-tools}

yumdownloader --resolve --destdir=/root/lib/tar tar
yumdownloader --resolve --destdir=/root/lib/unzip unzip
yumdownloader --resolve --destdir=/root/lib/wget wget
yumdownloader --resolve --destdir=/root/lib/vim vim
yumdownloader --resolve --destdir=/root/lib/net-tools net-tools

  1. 查看提供某个命令/value的rpm包
yum provides unzip

  1. 查看 rpm 包所需的全部依赖
yum deplist unzip-6.0-45.el8_4

四、测试机安装EMQX

1. 下载依赖包


yumdownloader --resolve --destdir=/root/lib/tar tar
yumdownloader --resolve --destdir=/root/lib/unzip unzip
yumdownloader --resolve --destdir=/root/lib/wget wget
yumdownloader --resolve --destdir=/root/lib/vim vim
yumdownloader --resolve --destdir=/root/lib/net-tools net-tools
yumdownloader --resolve --destdir=/root/lib/libatomic libatomic
yumdownloader --resolve --destdir=/root/lib/chrony chrony

# 如果openssl版本低于1.1,需要进行升级 openssl version
yumdownloader --resolve --destdir=/root/lib/epel-release epel-release
yumdownloader --resolve --destdir=/root/lib/openssl11 openssl11
yumdownloader --resolve --destdir=/root/lib/openssl11-devel openssl11-devel

# 打包为压缩包,方便下载到本地
tar -zcvf lib.tar.gz ./lib/*

# 将依赖同步到其他服务器
scp -r /root/lib/* root@192.168.137.102:/root/lib/

# 上传到服务器之后解压
tar -zxvf lib.tar.gz -C ./

2. rpm 安装

# 下载 wget https://www.emqx.com/zh/downloads/broker/5.8.5/emqx-5.8.5-el8-amd64.rpm
curl https://packages.emqx.net/emqx-ce/v5.8.5/emqx-5.8.5-el8-amd64.rpm -o emqx-5.8.5-el8-amd64.rpm

# 安装
sudo yum install -y emqx-5.8.5-el8-amd64.rpm

# 启动 停止命令
sudo systemctl start emqx
sudo systemctl status emqx
sudo systemctl stop emqx
sudo systemctl restart emqx

# 查看配置文件
cat /etc/emqx/emqx.conf

# 查询安装的包
sudo rpm -qa | grep emqx

# 卸载
sudo rpm -e emqx-5.8.5-1.el8.x86_64

安装完成后相关文件可以从下面位置找到

  • /usr/lib/emqx 安装文件路径
  • /usr/bin/emqx 执行文件路径(软链接)
  • /etc/emqx/ 配置文件路径
  • /var/log/emqx 日志文件路径

3. tar.gz 安装

# 下载 wget https://www.emqx.com/zh/downloads/broker/5.8.5/emqx-5.8.5-el8-amd64.tar.gz
curl https://packages.emqx.net/emqx-ce/v5.8.5/emqx-5.8.5-el8-amd64.tar.gz -o emqx-5.8.5-el8-amd64.tar.gz

# 安装 tar
rpm -ivh ./lib/tar-1.30-5.el8.x86_64.rpm

# 安装
mkdir -p /opt/emqx && tar -zxvf emqx-5.8.5-el8-amd64.tar.gz -C /opt/emqx

# 启动 停止命令
cd /opt/emqx
./bin/emqx start
./bin/emqx stop
./bin/emqx console

安装完成后相关文件可以从下面位置找到

  • /opt/emqx 安装文件路径
  • /opt/emqx/bin 执行文件路径
  • /opt/emqx/etc/ 配置文件路径
  • /opt/emqx/log 日志文件路径

4. 将EMQX注册为系统服务

cat /etc/systemd/system/emqx.service

cat > /etc/systemd/system/emqx.service << 'EOF'
[Unit]
Description=EMQX Broker
After=syslog.target network.target

[Service]
Type=forking
ExecStart=/opt/emqx/bin/emqx start
ExecStop=/opt/emqx/bin/emqx stop
ExecReload=/opt/emqx/bin/emqx restart
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

EOF

# 设置开机自启
systemctl enable emqx

# 启停命令
service emqx start
service emqx stop
service emqx restart

五、集群安装配置

假设现在有三台服务器,其主机名和ip分别如下

主机名IP
emqx-01192.168.137.101
emqx-02192.168.137.102
emqx-03192.168.137.103

1. 测试节点网络

修改hosts文件,确保通过域名(主机名)可以相互访问

cat >> /etc/hosts << EOF

192.168.137.101 emqx-01
192.168.137.102 emqx-02
192.168.137.103 emqx-03

EOF

检查网络互通及延迟情况

ping -c4 emqx-01

ping -c4 emqx-02

ping -c4 emqx-03

注意 官方文档要求各节点之间的延迟不高于10ms,过高的延迟可能造成服务不可用

2. 开放服务器端口

为保证集群节点之间的通讯,需要开放以下端口

端口描述
1883MQTT监听端口
8083WebSocket监听端口
18083Web控制台端口
4370Erlang 分布式传输端口
5370集群 RPC 端口,适用于物理机环境
5369集群 RPC 端口,适用于 Docker 环境
firewall-cmd --zone=public --add-port=1883/tcp --permanent
firewall-cmd --zone=public --add-port=8083/tcp --permanent
firewall-cmd --zone=public --add-port=18083/tcp --permanent

firewall-cmd --zone=public --add-port=4370/tcp --permanent
firewall-cmd --zone=public --add-port=5370/tcp --permanent
firewall-cmd --zone=public --add-port=5369/tcp --permanent

firewall-cmd --reload

firewall-cmd --list-port

3. 时间同步配置

由于EMQX集群对服务器的网络延迟有严格要求,所以必须保证各节点之间的时间同步正常且延迟低于10ms,如果服务器上没有时间同步配置,需要安装配置chrony,可以参考这篇文章

  1. 安装chrony服务
# 检查是否有 ntpd 或 chronyd 服务
systemctl status ntpd
systemctl status chronyd

# 安装chrony
rpm -ivh /root/lib/chrony/timedatex-0.5-3.el8.x86_64.rpm
rpm -ivh /root/lib/chrony/chrony-4.1-1.el8.x86_64.rpm

# 配置文件
cat /etc/chrony.conf

  1. 启动并设置开机自启
# 启动
systemctl start chronyd
systemctl status chronyd

# 开机自启
systemctl enable chronyd

  1. 配置chrony
# 修改配置文件
vim /etc/chrony.conf

# 允许所有客户端同步时间
allow all

# 上游NTP服务
server 210.72.145.44 iburst
server cn.pool.ntp.org iburst
server cn.ntp.org.cn iburst

# 阿里
pool ntp.aliyun.com iburst
server ntp1.aliyun.com iburst
server ntp2.aliyun.com iburst
server ntp3.aliyun.com iburst
server ntp4.aliyun.com iburst
server ntp5.aliyun.com iburst
server ntp6.aliyun.com iburst
server ntp7.aliyun.com iburst

  1. 状态检查
# 检查时区
timedatectl
# 修改时区
timedatectl set-timezone Asia/Shanghai

# 查看时间源
chronyc sources -v

# 查看当前时间状态
chronyc tracking

# 手动同步时间
chronyc -a makestep

4. 检测时钟回拨

时钟回拨会导致Erlang虚拟机退出,在正式部署前可以先执行脚本检测以下,避免正式使用时出现这个问题

实测 5.8.5 版本在时钟回拨会退出程序,可以将EMQX版本退回到 5.0.26,这个版本不会因为时钟回拨而退出

  1. 监测时钟回拨脚本
# 写入脚本
cat > timestamp.sh << 'EOF'
#!/bin/bash

# 基础配置
LOG_DIR="$(dirname "$0")/logs" # 日志存储目录
RETENTION_DAYS=7 # 日志保留天数
MONITOR_INTERVAL=600 # 性能监控间隔(秒)
NTP_CHECK_INTERVAL=7200 # NTP检查间隔(秒)

# 初始化目录
mkdir -p "$LOG_DIR"

# 动态日志路径管理
current_date=$(date +"%Y-%m-%d")
time_log="${LOG_DIR}/time_info_${current_date}.log"
error_log="${LOG_DIR}/time_error_${current_date}.log"
perf_log="${LOG_DIR}/performance_${current_date}.log"

# 初始化跟踪变量
last_timestamp=0
last_ntp_check=$(date +%s)
last_perf_check=$(date +%s)

# 函数:日志切割检查
check_log_rotation() {
local today=$(date +"%Y-%m-%d")
if [ "$today" != "$current_date" ]; then
current_date="$today"
time_log="${LOG_DIR}/time_info_${current_date}.log"
error_log="${LOG_DIR}/time_error_${current_date}.log"
perf_log="${LOG_DIR}/performance_${current_date}.log"
echo "[INFO] Rotated log files to ${current_date}" >> "$time_log"
fi
}

# 函数:性能监控
monitor_performance() {
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
local process_stats=$(ps -p $$ -o %cpu,%mem --no-headers | tr -d ' ')
local loadavg=$(awk '{print $1,$2,$3}' /proc/loadavg)
local mem_info=$(free -m | awk '/Mem/{printf "%.2f%%", $3/$2*100}')

echo "[PERF] $timestamp | CPU: ${process_stats%%,*}% | MEM: ${process_stats##*,}% | Load: $loadavg | MemUsage: $mem_info" >> "$perf_log"
}

# 函数:清理旧日志
cleanup_old_logs() {
find "$LOG_DIR" -maxdepth 1 -type f \( -name "*.log" \) \
-mtime +$RETENTION_DAYS \
-exec rm -fv {} \; >> "$time_log" 2>&1
}

# 函数:NTP状态检测
check_ntp_status() {
# 优先检查ntpstat命令
if command -v ntpstat >/dev/null 2>&1; then
if ! ntpstat >/dev/null 2>&1; then
echo "[WARNING] NTP sync abnormal (ntpstat check failed)" >> "$ERROR_LOG_FILE"
fi
# 次选检查chrony服务
elif systemctl is-active chronyd >/dev/null 2>&1; then
if ! chronyc tracking >/dev/null 2>&1; then
echo "[WARNING] Chrony sync abnormal (chronyc check failed)" >> "$ERROR_LOG_FILE"
fi
# 最后检查ntpd服务
elif systemctl is-active ntpd >/dev/null 2>&1; then
ntpq -pn | grep -q '^+'
if [ $? -ne 0 ]; then
echo "[WARNING] NTPD sync abnormal (no reachable servers)" >> "$ERROR_LOG_FILE"
fi
else
echo "[WARNING] No NTP service found (chronyd/ntpd not running)" >> "$ERROR_LOG_FILE"
fi
}

# 主监控循环
while true; do
now=$(date +%s)

# ========== 日志切割检查 ==========
check_log_rotation

# ========== 时间记录与回拨检测 ==========
current_time=$(date +"%Y-%m-%d %H:%M:%S.%3N")
now_seconds=$(date +%s)
now_millis=$(date +%3N | sed 's/^0*//') # 移除前导零

# 关键修复:显式声明十进制数值
current_timestamp=$(( now_seconds * 1000 + 10#$now_millis ))

echo "[TIME] $current_time" >> "$time_log"

# 时钟回拨检测
if [ $last_timestamp -gt 0 ] && [ $current_timestamp -lt $last_timestamp ]; then
error_msg="[ERROR] Clock rollback - ${last_timestamp} - ${current_timestamp} ms | Details: $current_time"
echo "$error_msg" >> "$error_log"
fi
last_timestamp=$current_timestamp

# ========== 定期任务调度 ==========
# 性能监控(每10分钟)
if [ $((now - last_perf_check)) -ge $MONITOR_INTERVAL ]; then
monitor_performance
last_perf_check=$now
fi

# NTP检查(每2小时)
if [ $((now - last_ntp_check)) -ge $NTP_CHECK_INTERVAL ]; then
check_ntp_status
last_ntp_check=$now
fi

# 修复点:使用字符串比较代替数值比较
if [ "$(date +"%H%M")" = "0005" ]; then
cleanup_old_logs
sleep 10
fi

sleep 0.9
done

EOF

  1. 运行脚本,检查日志
# 运行脚本
chmod +x ./timestamp.sh
nohup ./timestamp.sh > /dev/null 2>&1 &

# 查看打印记录
tail -f ./logs/time_info_2025-03-13.log

tail -f ./logs/time_error_2025-03-13.log

tail -f ./logs/performance_2025-03-13.log

5. 试运行EMQX

  1. 下载运行EMQX
# 下载安装包
# curl https://packages.emqx.net/emqx-ce/v5.0.26/emqx-5.0.26-el8-amd64.rpm -o emqx-5.0.26-el8-amd64.rpm
curl https://packages.emqx.net/emqx-ce/v5.0.26/emqx-5.0.26-el8-amd64.tar.gz -o emqx-5.0.26-el8-amd64.tar.gz

# 安装
mkdir -p /opt/emqx && tar -zxvf emqx-5.0.26-el8-amd64.tar.gz -C /opt/emqx

# 控制台启动验证
cd /opt/emqx
./bin/emqx console

# 如果报错缺少 libatomic.so.1,需要手动安装
scp /root/lib/libatomic/* root@192.168.137.102:/root/lib/libatomic/

rpm -ivh /root/lib/libatomic/libatomic-8.5.0-4.el8_5.x86_64.rpm

  1. 查看EMQX状态
# 启动停止命令
./bin/emqx start
./bin/emqx stop

# 查看服务运行状态
ps -ef | grep emqx

# 查看集群状态
./emqx/bin/emqx ctl cluster status

  1. 将EMQX注册为系统服务
cat /etc/systemd/system/emqx.service

cat > /etc/systemd/system/emqx.service << 'EOF'
[Unit]
Description=EMQX Broker
After=network.target

[Service]
Type=forking
ExecStart=/opt/emqx/bin/emqx start
ExecStop=/opt/emqx/bin/emqx stop
ExecReload=/opt/emqx/bin/emqx restart
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

EOF

# 设置开机自启
systemctl enable emqx

6. 集群配置

# 备份配置文件
cp ./etc/emqx.conf ./etc/emqx.conf.bakup

# 修改配置文件
vim ./etc/emqx.conf

# 修改以下内容
node {
name = "emqx@192.168.137.101"
}

cluster{
autoheal = true
discovery_strategy = static
static {
seeds = ["emqx@192.168.137.101", "emqx@192.168.137.102", "emqx@192.168.137.103"]
}
}

listeners.tcp.default {
bind = "0.0.0.0:1883"
max_connections = 102400

proxy_protocol = true
}

rm -rf /opt/emqx/etc/emqx.conf

cat > /opt/emqx/etc/emqx.conf << 'EOF'
## NOTE:
## This config file overrides data/configs/cluster.hocon,
## and is merged with environment variables which start with 'EMQX_' prefix.
##
## Config changes made from EMQX dashboard UI, management HTTP API, or CLI
## are stored in data/configs/cluster.hocon.
## To avoid confusion, please do not store the same configs in both files.
##
## See https://docs.emqx.com/en/enterprise/v5.0/configuration/configuration.html
## Configuration full example can be found in emqx.conf.example

node {
name = "emqx@192.168.137.101"
cookie = "emqxsecretcookie"
data_dir = "data"
}

cluster {
name = emqxcl
autoheal = true
discovery_strategy = static
static {
seeds = ["emqx@192.168.137.101", "emqx@192.168.137.102", "emqx@192.168.137.103"]
}
}

listeners.tcp.default {
bind = "0.0.0.0:1883"
max_connections = 102400

proxy_protocol = true
}

dashboard {
listeners {
http.bind = 18083
# 5.0.26 不能有以下配置
# https.bind = 18084
# https {
# ssl_options {
# certfile = "${EMQX_ETC_DIR}/certs/cert.pem"
# keyfile = "${EMQX_ETC_DIR}/certs/key.pem"
# }
# }
}
}

authorization {
deny_action = ignore
no_match = allow
cache = { enable = true }
}

EOF

7. 启动EMQX服务

配置完成后启动服务

yum install -y emqx-5.8.5-el8-amd64.rpm

systemctl start emqx

systemctl status emqx

mv /etc/emqx/emqx.conf /etc/emqx/emqx.conf.bakup

mv ./emqx/etc/emqx.conf /etc/emqx/emqx.conf

cat /etc/emqx/emqx.conf

tail -f /var/log/emqx/emqx.log.1

systemctl restart emqx

访问 Dashboard

  • 账号密码 admin /public

六、验证集群发布与订阅

  1. 打开MQTTX客户端,连接到集群的各个节点

  2. 每个节点订阅相同的Topic

  3. 从一个节点发出测试消息,可以看到三个节点都收到了订阅消息

七、Nginx网关负载均衡

负载均衡(Load Balancing)用于均衡多个网络组件的负载,从而优化资源的使用,避免由于组件过载造成故障。负载均衡虽然不是集群中的必备组件,但是能给集群带来一些非常有用的特性,例如当配置在 EMQX 集群中时,将能带来如下优势:

  • 均衡 EMQX 的负载,避免出现单节点过载的情况;
  • 简化客户端配置,客户端只需连接到负载均衡器上,无需关心集群内部伸缩变化;
  • TLS/SSL 终结,减轻 EMQX 集群的负担;
  • 提高安全性,有了负载均衡在集群前端,能够通过设置阻止不需要的流量,保护 EMQX 集群免受恶意攻击。

本节我们选择用 Nginx 来作为 EMQX 集群的网关,最终搭建的效果如下所示:

1. 环境准备

1. 安装编译依赖库

# 安装nginx必备依赖库
yum install -y gcc glibc-devel pcre pcre-devel zlib zlib-devel openssl openssl-devel

# 下载依赖包
yumdownloader --resolve --destdir=/root/lib/nginx/make make
yumdownloader --resolve --destdir=/root/lib/nginx/gcc gcc
yumdownloader --resolve --destdir=/root/lib/nginx/glibc-devel glibc-devel
yumdownloader --resolve --destdir=/root/lib/nginx/pcre pcre
yumdownloader --resolve --destdir=/root/lib/nginx/pcre-devel pcre-devel
yumdownloader --resolve --destdir=/root/lib/nginx/zlib zlib
yumdownloader --resolve --destdir=/root/lib/nginx/zlib-devel zlib-devel
yumdownloader --resolve --destdir=/root/lib/nginx/openssl openssl
yumdownloader --resolve --destdir=/root/lib/nginx/openssl-devel openssl-devel


# 安装依赖
rpm -ivh /root/lib/nginx/make/*
rpm -ivh /root/lib/nginx/gcc/*
rpm -ivh /root/lib/nginx/glibc-devel/*
rpm -ivh /root/lib/nginx/pcre/*
rpm -ivh /root/lib/nginx/pcre-devel/*
rpm -ivh /root/lib/nginx/zlib/*
rpm -ivh /root/lib/nginx/zlib-devel/*
rpm -ivh /root/lib/nginx/openssl/*
rpm -ivh /root/lib/nginx/openssl-devel/*

2. 开放代理所需端口

firewall-cmd --zone=public --add-port=80/tcp --permanent
firewall-cmd --zone=public --add-port=443/tcp --permanent

firewall-cmd --zone=public --add-port=1883/tcp --permanent
firewall-cmd --zone=public --add-port=8883/tcp --permanent

firewall-cmd --zone=public --add-port=1080/tcp --permanent
firewall-cmd --zone=public --add-port=1443/tcp --permanent

firewall-cmd --reload

firewall-cmd --list-port

端口描述
80Nginx HTTP端口
443Nginx HTTPS端口
1883反向代理 MQTT端口
8883反向代理 MQTT SSL端口
1080反向代理 MQTT WebSocket端口
1443反向代理 MQTT WebSocket SSL端口

2. 下载安装Nginx

1. 下载

# 下载
curl https://nginx.org/download/nginx-1.26.3.tar.gz -o nginx-1.26.3.tar.gz

# 解压
tar -zxvf nginx-1.26.3.tar.gz
cd nginx-1.26.3

2. 编译配置

# 查看configure 支持的参数,其中包含指定某些路径、开启某些模块、编译中特殊参数
./configure --help | more

# 编译配置
./configure \
--with-threads \
--with-http_stub_status_module \
--with-http_ssl_module \
--with-http_realip_module \
--with-stream \
--with-stream_ssl_module \
--prefix=/usr/local/nginx

# 输出各种文件的路径
Configuration summary
+ using threads
+ using system PCRE2 library
+ using system OpenSSL library
+ using system zlib library

nginx path prefix: "/usr/local/nginx"
nginx binary file: "/usr/local/nginx/sbin/nginx"
nginx modules path: "/usr/local/nginx/modules"
nginx configuration prefix: "/usr/local/nginx/conf"
nginx configuration file: "/usr/local/nginx/conf/nginx.conf"
nginx pid file: "/usr/local/nginx/logs/nginx.pid"
nginx error log file: "/usr/local/nginx/logs/error.log"
nginx http access log file: "/usr/local/nginx/logs/access.log"
nginx http client request body temporary files: "client_body_temp"
nginx http proxy temporary files: "proxy_temp"
nginx http fastcgi temporary files: "fastcgi_temp"
nginx http uwsgi temporary files: "uwsgi_temp"
nginx http scgi temporary files: "scgi_temp"

  • --with-http_ssl_module 参数用于添加 SSL 功能支持,
  • --with-stream--with-stream_ssl_module 参数用于添加 TCP 反向代理支持
  • --prefix=/nginx 指定安装目录

3. 安装

# 编译
make

# 安装
make install

# 创建 Nginx 用户和组
groupadd -r nginx
useradd -s /sbin/nologin -r -g nginx nginx

4. 将Nginx注册为服务

cat > /etc/systemd/system/nginx.service << 'EOF'
[Unit]
Description=The Nginx HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

EOF

5. 启动服务并设置开机自启

# 启动
service nginx start

# 停止
service nginx stop

# 状态
service nginx status

# 开机自启
systemctl enable nginx

6. 前端页面访问

  1. 浏览器打开网页 http://192.168.137.110

  2. nginx.conf 中加入监控配置

# NGINX 状态监控
location /stats {
stub_status on;
access_log off;
}

浏览器打开网页 http://192.168.137.110/stats

3. 配置Nginx

1. 创建配置文件夹,生成SSL证书

mkdir -p /usr/local/nginx/{conf.d,stream,certs}
cd /usr/local/nginx/certs

# 生成私钥,需要输入密码 123456
openssl genrsa -des3 -out emqx.pass.key 2048
# 删除私钥中的密码
openssl rsa -in emqx.pass.key -out emqx.key
# 生成CSR
openssl req -new -key emqx.key -out emqx.csr -subj "/C=CN/ST=Shanghai/L=Shanghai/O=PISX/OU=Diginn/CN=*.example.com"
# 生成证书
openssl x509 -req -days 3650 -in emqx.csr -signkey emqx.key -out emqx.pem

2. 主配置文件 nginx.conf

#user  nobody;
worker_processes 1;

#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;

#pid logs/nginx.pid;


events {
worker_connections 1024;
}


http {
include mime.types;
default_type application/octet-stream;

#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';

#access_log logs/access.log main;

sendfile on;
#tcp_nopush on;

#keepalive_timeout 0;
keepalive_timeout 65;

server {
listen 80;
server_name localhost;

#charset koi8-r;

#access_log logs/host.access.log main;

location / {
root html;
index index.html index.htm;
}

# NGINX 状态监控
location /stats {
stub_status on;
access_log off;
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}

include /usr/local/nginx/conf.d/*.conf;
}

stream {

log_format proxy '$remote_addr - [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time - "$upstream_addr" '
'"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';

access_log logs/tcp_access.log proxy;
error_log logs/tcp_error.log;

# 引入 stream 配置
include /usr/local/nginx/stream.d/*.conf;

}

3. MQTT协议配置文件 /usr/local/nginx/stream/mqtt.conf

upstream emqx_servers {
server 192.168.137.101:1883;
server 192.168.137.102:1883;
server 192.168.137.103:1883;
}

server {
listen 1883;
proxy_pass emqx_servers;

# 启用此项时,对应后端监听器也需要启用 proxy_protocol
proxy_protocol on;
proxy_connect_timeout 10s;
# 默认心跳时间为 10 分钟
proxy_timeout 60s;
proxy_buffer_size 3M;
tcp_nodelay on;
}

server {
listen 8883 ssl;

ssl_session_cache shared:TCPSSL:10m;
ssl_session_timeout 10m;
ssl_certificate /usr/local/nginx/certs/emqx.pem;
ssl_certificate_key /usr/local/nginx/certs/emqx.key;
ssl_verify_depth 2;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;

# 添加 CA 证书及开启验证客户端证书参数即可启用双向认证
# ssl_client_certificate /usr/local/NGINX/certs/ca.pem;
# ssl_verify_client on;
# ssl_verify_depth 1;

proxy_pass emqx_servers;

# 启用此项时,对应后端监听器也需要启用 proxy_protocol
proxy_protocol on;
proxy_connect_timeout 10s;
# 默认心跳时间为 10 分钟
proxy_timeout 1800s;
proxy_buffer_size 3M;
tcp_nodelay on;
}

4. WebSocket协议配置文件 /usr/local/nginx/conf.d/websocket.conf

upstream emqx_websocket_servers {
server 192.168.137.101:8083;
server 192.168.137.102:8083;
server 192.168.137.103:8083;
}

server {
listen 1080;
server_name mqtt.example.com;

location /mqtt {
proxy_pass http://emqx_websocket_servers;

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";

# 禁用缓存
proxy_buffering off;

proxy_connect_timeout 10s;
# WebSocket 连接有效时间
# 在该时间内没有数据交互的话 WebSocket 连接会自动断开,默认为 60s
proxy_send_timeout 3600s;
proxy_read_timeout 3600s;

# 反向代理真实 IP
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

server {
listen 1443 ssl;
server_name mqtt.example.com;

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_certificate /usr/local/nginx/certs/emqx.pem;
ssl_certificate_key /usr/local/nginx/certs/emqx.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;

# 添加 CA 证书及开启验证客户端证书参数即可启用双向认证
# ssl_client_certificate /usr/local/NGINX/certs/ca.pem;
# ssl_verify_client on;

location /mqtt {
proxy_pass http://emqx_websocket_servers;

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";

# 禁用缓存
proxy_buffering off;

proxy_connect_timeout 10s;
# WebSocket 连接有效时间
# 在该时间内没有数据交互的话 WebSocket 连接会自动断开,默认为 60s
proxy_send_timeout 3600s;
proxy_read_timeout 3600s;

# 反向代理真实 IP
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

}

5. 重启服务

# 检测配置文件是否正常
/usr/local/nginx/sbin/nginx -t

# 重启Nginx
service nginx restart

4. 验证负载均衡

  1. 使用MQTTX启动四个客户端,分别连接到 Nginx 1883 8883 1080 1443端口

  2. 可以看到,四个连接分别路由到了 101 102 103

  3. 订阅和Topic情况如下

  4. 发送一条测试消息,四个客户端都正常收到

注意 由于使用的是自签名证书,使用 mqtts wss连接时需要单独配置证书信息

八、HAProxy负载均衡

1. 环境准备

1. 安装Lua运行环境

# 下载
curl https://www.lua.org/ftp/lua-5.4.7.tar.gz -o lua-5.4.7.tar.gz

# 解压
tar -zxvf lua-5.4.7.tar.gz -C /usr/local/

cd /usr/local/lua-5.4.7

# 编译
make linux test

# 测试
src/lua -v

2. 修改系统配置

  1. 修改进程句柄限制
vim /etc/security/limits.conf

# 打开 /etc/security/limits.conf 文件,最后面追加:

* soft nofile 120000
* hard nofile 120000
* soft nproc 120000
* hard nproc 120000
  1. 修改系统配置文件
vim /etc/sysctl.conf

# 打开 /etc/sysctl.conf 文件,最后面追加:

net.ipv4.tcp_tw_reuse = 1 # 表示开启重用.允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 # 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭.
net.ipv4.tcp_syncookies=1 # 表示开启SYN Cookies.当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.conf.all.rp_filter = 1 # 过滤反向路由不通的数据包
net.ipv4.ip_local_port_range = 1024 65535 # 表示用于向外连接的端口范围.缺省情况下很小:32768到61000,改为1024到65000.
net.ipv4.tcp_max_syn_backlog = 50000 # 记录的那些尚未收到客户端确认信息的连接请求的最大值,可以容纳更多等待连接的网络连接数. 对于有128M内存的系统而言,缺省值是1024,小内存的系统则是128.
net.ipv4.tcp_max_tw_buckets = 400000 #timewait的数量,默认是180000.
net.ipv4.tcp_max_orphans = 60000 # 系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上.如果超过这个数字,孤儿连接将即刻被复位并打印出警告信息.这个限制仅仅是为了防止简单的DoS攻击,不能过分依靠它或者人为地减小这个值,更应该增加这个值(如果增加了内存之后).
net.ipv4.tcp_synack_retries = 3 # 为了打开对端的连接,内核需要发送一个SYN并附带一个回应前面一个SYN的ACK.也就是所谓三次握手中的第二次握手.这个设置决定了内核放弃连接之前发送SYN+ACK包的数量.
net.core.somaxconn = 50000 # web应用中listen函数的backlog默认会给我们内核参数的net.core.somaxconn限制到128
net.core.rmem_max = 16777216 # 系统套接字缓冲区(读), 包含三个整数值,分别是:min,default,max
net.core.wmem_max = 16777216 # 系统套接字缓冲区(写), 包含三个整数值,分别是:min,default,max
net.ipv4.tcp_rmem = 4096 87380 16777216 # TCP接收/发送缓冲区(读)
net.ipv4.tcp_wmem = 4096 65536 16777216 # TCP接收/发送缓冲区(写)
net.ipv4.tcp_no_metrics_save = 1 # 使metrics不保存每个单独的数据包
net.ipv4.tcp_moderate_rcvbuf = 1 # 表示打开了TCP内存自动调整功能
net.core.netdev_max_backlog = 50000 # 每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目.
fs.file-max=2048000 # 表示系统级别的能够打开的文件句柄①的数量.是对整个系统的限制,并不是针对用户的.

  1. 执行 sysctl -p 命令操作来使我们所做的变更生效.

  2. 开放端口

firewall-cmd --zone=public --add-port=8888/tcp --permanent

firewall-cmd --zone=public --add-port=1883/tcp --permanent
firewall-cmd --zone=public --add-port=8883/tcp --permanent

firewall-cmd --zone=public --add-port=1080/tcp --permanent
firewall-cmd --zone=public --add-port=1443/tcp --permanent

firewall-cmd --reload

firewall-cmd --list-port

端口描述
8888HAProxy 监控端口
1883反向代理 MQTT端口
8883反向代理 MQTT SSL端口
1080反向代理 MQTT WebSocket端口
1443反向代理 MQTT WebSocket SSL端口

2. 下载安装

1. 下载解压

# 下载解压
curl https://www.haproxy.org/download/3.1/src/haproxy-3.1.5.tar.gz -o haproxy-3.1.5.tar.gz
tar -zxvf haproxy-3.1.5.tar.gz

cd haproxy-3.1.5

# 查看安装方法
less INSTALL

less Makefile

2. 编译安装

make clean

# 编译
make ARCH=x86_64 TARGET=linux-glibc USE_PCRE=1 USE_OPENSSL=1 USE_ZLIB=1 USE_SYSTEMD=1 USE_CPU_AFFINITY=1 USE_LUA=1 LUA_INC=/usr/local/lua-5.4.7/src/ LUA_LIB=/usr/local/lua-5.4.7/src/ PREFIX=/usr/local/haproxy

# 安装
make install PREFIX=/usr/local/haproxy

# 查看目录结构
tree /usr/local/haproxy

# 测试
/usr/local/haproxy/sbin/haproxy -V

# 创建配置目录 日志目录
mkdir -p /usr/local/haproxy/{conf,conf.d,logs,certs}

# 将Nginx证书 key合成一个
cat /usr/local/nginx/certs/emqx.pem /usr/local/nginx/certs/emqx.key > /usr/local/haproxy/certs/server.pem

3. 配置HAProxy

1. 主配置文件

cat > /usr/local/haproxy/conf/haproxy.cfg << 'EOF'
global
log 127.0.0.1 local3 info
daemon
maxconn 10000 # FD limit = maxconn * 2 + 18

defaults
log global
mode tcp
option tcplog
#option dontlognull
timeout connect 10000
# timeout > mqtt's keepalive * 1.2
timeout client 240s
timeout server 240s
maxconn 20000

frontend stats
mode http
bind *:8888 # 监控访问端口
stats enable
stats uri /stats # 监控访问路径
stats hide-version # 隐藏版本号
stats refresh 10s
stats realm HAProxy Manager
stats auth admin:123456 # 设置管理员帐号和密码
log 127.0.0.1 local3 err # [err warning info debug]

EOF

2. mqtt协议配置

cat > /usr/local/haproxy/conf.d/mqtt.cfg << 'EOF'
backend mqtt_backend
mode tcp
# 创建粘性会话表
stick-table type string len 32 size 100k expire 30m

# 使用客户端 ID 作为键
stick on req.payload(0,0), mqtt_field_value(connect,client_identifier)

# 增加 send-proxy 会把真实带给 EMQX,对应后端监听器需要启用 proxy_protocol
# server emqx1 emqx1-cluster.emqx.io:1883 check send-proxy-v2-ssl-cn
server emqx1 192.168.137.101:1883 check send-proxy-v2-ssl-cn
server emqx2 192.168.137.102:1883 check send-proxy-v2-ssl-cn
server emqx3 192.168.137.103:1883 check send-proxy-v2-ssl-cn

frontend mqtt_frontend
bind *:1883
mode tcp
# 等待缓冲区填满,以便解析 MQTT 报文
tcp-request inspect-delay 10s
# 拒绝非 MQTT 连接
tcp-request content reject unless { req.payload(0,0), mqtt_is_valid }
default_backend mqtt_backend

backend mqtt_tls_backend
mode tcp
balance roundrobin

# 增加 send-proxy 会把真实 IP 带给 EMQX,对应后端监听器需要启用 proxy_protocol
server emqx1 192.168.137.101:1883 check-send-proxy send-proxy-v2-ssl-cn
server emqx2 192.168.137.102:1883 check-send-proxy send-proxy-v2-ssl-cn
server emqx3 192.168.137.103:1883 check-send-proxy send-proxy-v2-ssl-cn

frontend mqtt_tls_frontend
bind *:8883 ssl crt /usr/local/haproxy/certs/server.pem
# 双向认证
# bind *:8883 ssl ca-file /etc/haproxy/certs/cacert.pem crt /etc/haproxy/certs/server.pem verify required
mode tcp
default_backend mqtt_tls_backend

EOF

3. websocket协议配置

cat > /usr/local/haproxy/conf.d/websocket.cfg << 'EOF'
backend mqtt_ws_backend
mode tcp
balance roundrobin
server emqx1 192.168.137.101:8083 check
server emqx2 192.168.137.102:8083 check
server emqx3 192.168.137.103:8083 check

frontend mqtt_ws_frontend
bind *:1080
mode tcp
default_backend mqtt_ws_backend

backend mqtt_ws_tls_backend
mode tcp
balance roundrobin
server emqx1 192.168.137.101:8083 check
server emqx2 192.168.137.102:8083 check
server emqx3 192.168.137.103:8083 check

frontend mqtt_ws_tls_frontend
bind *:1443 ssl crt /usr/local/haproxy/certs/server.pem
mode tcp
default_backend mqtt_ws_tls_backend

EOF

4. 注册运行

1. 将HAProxy注册为服务

cat > /usr/lib/systemd/system/haproxy.service << 'EOF'
[Unit]
Description=HAProxy Load Balancer
After=syslog.target network.target

[Service]
# 支持多配置文件读取,类似于从侧面是实现配置文件的include功能。
ExecStartPre=/usr/local/haproxy/sbin/haproxy -f /usr/local/haproxy/conf/haproxy.cfg -f /usr/local/haproxy/conf.d -c -q
ExecStart=/usr/local/haproxy/sbin/haproxy -Ws -f /usr/local/haproxy/conf/haproxy.cfg -f /usr/local/haproxy/conf.d -p /usr/local/haproxy/logs/haproxy.pid
ExecReload=/bin/kill -USR2 $MAINPID
LimitNOFILE=100000

[Install]
WantedBy=multi-user.target

EOF

2. 运行

service haproxy start

service haproxy stop

service haproxy status

systemctl enable haproxy

3. 访问

浏览器打开监控面板

  • 账号密码 admin / 123456

九、集群网关对比

经过测试,两种网关都能实现EMQX集群节点断开后的自动重连;但是对于已订阅的Topic都不会重新订阅,需要在重连后重新订阅Topic,否则客户端无法收到消息。

Topic订阅属于客户端自身的行为,需要在客户端实现重连之后重新订阅Topic。

相对来说:

  • Nginx 拥有更多的用户,更完善的社区文档,上手门槛更低,但是对于MQTT的支持较弱。
  • HAProxy 在性能以及高可用上更胜一筹,对MQTT的支持也更好,但是相对用户量少,门槛更高,运维成本高。

十、其他问题

1. 共享订阅

在不使用共享订阅时,每个客户端都会收到全量的消息,造成消息的重复消费,同时也会导致客户端消息处理能力的下降。

MQTT在5.0之后新增了共享订阅特性,很好的避免了这个问题,使用共享订阅,可以实现在扩展客户端的同时,充分利用每个客户端的消息处理能力,同时也避免了消息的重复消费

共享订阅在EMQX中时默认开启的,只需要在订阅topic时添加对应的前缀即可实现,Web配置位置如下图所示

示例前缀组别topic
$share/{ShareName}/{topic-name}$share{ShareName}{topic-name}
$share/pisx/A2-F1/电压表$shareluxA2-F1/电压表
$share/yunyi/01_混配/1#罐流量计$shareyunyi01_混配/1#罐流量计
$queue/01_混配/1#罐流量计$queue-01_混配/1#罐流量计

Note: 除了使用$share/<group-name> 这种共享订阅方式之外,EMQX还提供了不带组别的共享订阅 $queue,详情见不带组别的共享订阅

  1. 四个客户端连接到集群,分别采用相同的共享订阅前缀订阅 topic testtopic,带前缀的topic地址为 $share/pisx/testtopic

  2. 其中一个客户端向 topic testtopic 发送消息,可以看到只有一个客户端收到消息

  3. 再次发送消息,仍然是一个客户端收到消息

  4. 继续发送,剩余的两个客户端也分别收到了消息

共享订阅有多种策略,默认的策略为随机,具体配置项见共享订阅配置参数

2. 客户端断开重连丢失已订阅Topic

搭建EMQX集群是为了实现EMQX服务的高可用,需要考虑部分服务端节点意外中断的场景,在此场景下,使用Nginx或者HAProxy可以实现客户端的自动重连,对服务端的切换无感,但是由于消息订阅属于客户端的行为,集群并不能保证连接到新的节点时自动订阅相同的Topic。

经测试,目前平台在订阅消息后缓存当前客户端所有的Topic,重连时继续重新订阅这些Topic可以达到此目的,无需额外的开发工作,注意配置即可

订阅断开时会抛出异常,在异常处理中执行重新连接逻辑

重连成功后再次订阅之前缓存的Topic

但是由于 断开 - 重连 - 重新订阅 这个过程会消耗一定的时间,在这个过程中可能会有部分点位数据的丢失,这个时间间隔大约为 15s。

十一、常见问题

1. 服务启动失败

使用 emqx console 命令启动,可以看到命令行打印如下日志

[root@emqx-01 emqx]# ./bin/emqx console
WARNING: Default (insecure) Erlang cookie is in use.
WARNING: Configure node.cookie in /opt/emqx/etc/emqx.conf or override from environment variable EMQX_NODE__COOKIE
WARNING: NOTE: Use the same cookie for all nodes in the cluster.
Kernel pid terminated (application_controller) ("{application_start_failure,kernel,{{shutdown,{failed_to_start_child,on_load,{on_load_function_failed,quicer_nif,{error,{load_failed,\"Failed to load NIF library /opt/emqx/lib/quicer-0.1.11/priv/libquicer_nif: 'libatomic.so.1: cannot open shared object file: No such file or directory'\"}}}}},{kernel,start,[normal,[]]}}}")

Crash dump is being written to: /opt/emqx/log/erl_crash.dump...done

解决方法

关于此错误,可以参考这个问题

此类异常通常是缺少依赖包,此处是缺少 libatomic,安装后再次运行即可

yum install -y libatomic

2. 服务启动后过几分钟回自动停止

使用 emqx console 命令启动,可以看到命令行打印如下日志

[root@emqx-01 emqx]# ./bin/emqx console
WARNING: Default (insecure) Erlang cookie is in use.
WARNING: Configure node.cookie in /opt/emqx/etc/emqx.conf or override from environment variable EMQX_NODE__COOKIE
WARNING: NOTE: Use the same cookie for all nodes in the cluster.
Erlang/OTP 26 [erts-14.2.5.2] [emqx] [64-bit] [smp:4:4] [ds:4:4:8] [async-threads:4] [jit:ns]

Listener tcp:default on 0.0.0.0:1883 started.
Listener ssl:default on 0.0.0.0:8883 started.
Listener ws:default on 0.0.0.0:8083 started.
Listener wss:default on 0.0.0.0:8084 started.
Listener http:dashboard on :18083 started.
EMQX 5.8.5 is running now!
Restricted Eshell V14.2.5.2 (press Ctrl+G to abort, type help(). for help)
v5.8.5(emqx@192.168.137.101)1> OS monotonic time stepped backwards! Maybe add '+c false' in vm.args
Previous time: 1019146450559
Current time: 1019146449647
[os_mon] cpu supervisor port (cpu_sup): Erlang has closed
[os_mon] memory supervisor port (memsup): Erlang has closed
已放弃 (核心已转储)

解决方法

关于此错误,可以参考这个问题

对于部分版本的 EMQX,在 VirtualBox 中运行时会出现时钟回拨,导致 Erlang 虚拟机退出运行,从而导致 EMQX 退出,使用 5.0.24 版本重试

3. 集群节点连接失败

使用 emqx console 命令启动,可以看到命令行打印如下日志

2025-03-12T10:53:53.661061+08:00 [info] Ekka(AutoCluster): discovered nodes are not responding: ['emqx@192.168.137.101']

在排除服务原因和网络原因后,检查配置文件中 node.name 是否包含数组,如果包含数字,开放的端口需要将这个数字加上

注意: 如果节点名称中有数字,则 Erlang 传输端口RPC 端口需要加上数字,例如: 节点名为 emqx-01 则需要开放 43715371 端口

4. HAProxy 启动报错

[NOTICE]   (15572) : Initializing new worker (15574)
[NOTICE] (15574) : haproxy version is 3.1.5-076df02
[NOTICE] (15574) : path to executable is /usr/local/haproxy/sbin/haproxy
[ALERT] (15574) : [haproxy.main()] Cannot raise FD limit to 2048043, limit is 1024.
[NOTICE] (15572) : haproxy version is 3.1.5-076df02
[NOTICE] (15572) : path to executable is /usr/local/haproxy/sbin/haproxy
[WARNING] (15572) : Failed to load worker!

解决方法

系统里的能打开的文件描述符太小,程序里的比较大,需要调整程序的 maxconn 或系统的 ulimit -n