当支付回调、订单处理、邮件发送或 AI 任务都直接塞在一次 HTTP 请求里,任何一个下游变慢都会拖垮接口。RabbitMQ 的作用是把生产者和消费者解耦:应用先把消息交给 Broker,后台 Worker 再按自己的速度消费,并通过确认、重试和死信队列处理失败任务。
本文以 RabbitMQ 4.3.5、Docker Compose、Caddy 和 Ubuntu 24.04 为基准,在 VPS 上搭建一个可用于小型生产环境的单节点消息队列。内容覆盖管理后台、AMQP TLS、虚拟主机与最小权限、资源告警、Prometheus 指标、definitions 与消息数据备份、升级及常见连接故障。版本与官方文档按 2026 年 9 月 15 日核对,部署时仍应再次检查当前补丁版本和升级路径。
RabbitMQ 是消息 Broker,不是数据库,也不是定时任务面板。它适合需要可靠异步处理和明确路由规则的业务:
- Web 请求只负责投递消息,邮件、图片处理和报表由 Worker 异步完成;
- 订单、库存、支付等服务通过 Exchange 与 Queue 解耦;
- 用 acknowledgment、publisher confirm 和 dead-letter exchange 控制失败重试;
- 多个消费者竞争消费同一队列,按负载水平扩容;
- 使用 AMQP 0-9-1、AMQP 1.0、MQTT、STOMP 或 Stream Protocol 接入不同客户端;
- 通过管理 UI、HTTP API 和 Prometheus 观察连接、积压、吞吐与资源告警。
如果需求只是每分钟运行一个脚本,systemd timer 或 cron 更简单;如果主要处理超长事件流与日志留存,Kafka 一类日志平台更合适。RabbitMQ 更擅长业务任务、灵活路由、低延迟交付和应用级确认。
| 架构 | 优点 | 局限 | 适合场景 |
|---|---|---|---|
| 单节点 RabbitMQ | 部署简单、成本低、故障定位直接 | VPS 或磁盘故障会中断服务 | 开发、低风险任务、可接受恢复时间的小业务 |
| 同一台 VPS 多容器 | 看似有多个节点 | 仍共享主机、磁盘和网络,不是真正高可用 | 不推荐用于生产 HA |
| 3 个独立节点 + Quorum Queue | 可容忍 1 个节点故障,能形成明确多数派 | 成本、网络和运维复杂度更高 | 关键业务、不能接受单机中断 |
RabbitMQ 官方推荐集群使用 1、3、5 等奇数节点,并明确不建议两节点集群。Quorum Queue 真正获得容错能力至少需要 3 个 RabbitMQ 节点;把三个容器放在同一台 VPS 上无法抵御主机故障。
本文聚焦单节点安全部署,并把固定节点名、备份和恢复流程做完整。业务升级为关键基础设施后,应迁移到三个独立故障域,而不是继续在单机上叠容器。
消息数量不是唯一容量指标。连接数、通道数、队列数量、消息体大小、积压时长、确认模式和磁盘延迟都会影响资源消耗。
| 使用规模 | VPS 建议 | 说明 |
|---|---|---|
| 开发与功能验证 | 2 vCPU、2GB 内存、30GB SSD | 不保存重要积压,允许停机恢复 |
| 小型生产、短消息低并发 | 2–4 vCPU、4GB 内存、60GB NVMe | 给 Broker、系统缓存和监控留出余量 |
| 大量连接或长时间积压 | 4 vCPU、8GB 以上、独立 NVMe | 先压测消息大小、确认延迟和磁盘吞吐 |
端口按用途分离:
| 端口 | 用途 | 本文暴露方式 |
|---|---|---|
5671/TCP | AMQP over TLS | 默认回环;远程客户端仅绑定私网 IP |
5672/TCP | 明文 AMQP | 禁用 |
15672/TCP | 管理 UI 与 HTTP API | 只绑定 127.0.0.1,由 Caddy 提供 HTTPS |
15692/TCP | Prometheus 指标 | 只绑定 127.0.0.1 |
4369/TCP | epmd | 不映射到宿主机 |
25672/TCP | 节点间通信 | 单节点不映射,集群时仅允许节点私网 |
不要直接把 5672、15672、4369 或 25672 开给全网。Docker 发布端口可能绕过部分主机防火墙规则,远程 AMQP 应同时使用指定私网监听地址、云安全组来源限制和 TLS。可结合 数据库与中间件不要直接暴露公网的安全方法检查边界。
以下命令以 Ubuntu 24.04 为例。Docker 建议从官方仓库安装;如果服务器已有 Docker Compose v2,不要重复运行第三方一键脚本。
sudo apt update
sudo apt full-upgrade -y
sudo apt install -y ca-certificates curl openssl caddy
docker --version
docker compose version
sudo systemctl enable --now caddy
生产环境还应限制日志大小、设置健康检查并设计回滚,可参考 Docker Compose 生产环境配置清单。
先准备只允许管理员访问的目录:
sudo install -d -m 0750 -o "$USER" -g "$USER" /opt/rabbitmq
cd /opt/rabbitmq
install -d -m 0750 data tls
install -d -m 0700 pki backups
umask 077
创建 .env。默认只把 AMQPS 绑定到回环地址;远程客户端应改成 WireGuard 或其他私网接口地址:
{
printf 'RABBITMQ_ADMIN_USER=opsadmin\n'
printf 'RABBITMQ_ADMIN_PASS=%s\n' "$(openssl rand -hex 32)"
printf 'RABBITMQ_DEFAULT_VHOST=production\n'
printf 'RABBITMQ_BIND_IP=127.0.0.1\n'
} > .env
chmod 600 .env
不要把 .env 提交到 Git,也不要把连接字符串贴进日志和工单。RabbitMQ 的默认用户环境变量只在空数据目录第一次初始化时生效;已经产生数据后修改 .env 不会自动修改现有用户密码,应使用管理命令主动轮换。
下面生成一个内部 CA 和服务器证书,示例域名为 mq.example.com:
cd /opt/rabbitmq/pki
openssl genrsa -out ca.key 4096
openssl req -x509 -new -sha256 -days 3650 \
-key ca.key \
-subj '/CN=RabbitMQ Internal CA' \
-out ca.crt
openssl genrsa -out server.key 4096
openssl req -new -sha256 \
-key server.key \
-subj '/CN=mq.example.com' \
-out server.csr
printf '%s\n' \
'subjectAltName=DNS:mq.example.com' \
'extendedKeyUsage=serverAuth' > server.ext
openssl x509 -req -sha256 -days 825 \
-in server.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-extfile server.ext \
-out server.crt
install -m 0644 ca.crt server.crt /opt/rabbitmq/tls/
install -m 0600 server.key /opt/rabbitmq/tls/
ca.key 只能离线或加密保管,不能挂进 RabbitMQ 容器,也不能分发给客户端。客户端只需要信任 ca.crt。正式环境可换成企业内部 CA;无论使用哪种 CA,证书 SAN 必须与客户端连接的域名一致。
创建 /opt/rabbitmq/rabbitmq.conf:
listeners.tcp = none
listeners.ssl.default = 5671
ssl_options.cacertfile = /etc/rabbitmq/tls/ca.crt
ssl_options.certfile = /etc/rabbitmq/tls/server.crt
ssl_options.keyfile = /etc/rabbitmq/tls/server.key
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = false
management.tcp.ip = 0.0.0.0
management.tcp.port = 15672
prometheus.tcp.ip = 0.0.0.0
prometheus.tcp.port = 15692
vm_memory_high_watermark.relative = 0.5
disk_free_limit.absolute = 2GB
这里完全关闭明文 AMQP,只启用 5671 TLS。verify_peer 会验证客户端提交的证书,但 fail_if_no_peer_cert=false 仍允许仅使用用户名密码的 TLS 客户端;如果要强制双向 TLS,应签发客户端证书并改为 true,同时先验证全部客户端兼容。
RabbitMQ 默认大约在内存达到可用量 60% 时触发资源告警并阻止发布者;本文主动设为 50%,给同机系统和故障诊断保留空间。disk_free_limit 必须结合最大积压与磁盘容量调整,2GB 只是小型实例的起点。
启用管理后台和 Prometheus 插件,创建 /opt/rabbitmq/enabled_plugins:
[rabbitmq_management,rabbitmq_prometheus].
创建 /opt/rabbitmq/compose.yml:
services:
rabbitmq:
image: rabbitmq:4.3.5-management
container_name: rabbitmq
hostname: rabbitmq-1
restart: unless-stopped
environment:
RABBITMQ_DEFAULT_USER: ${RABBITMQ_ADMIN_USER}
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_ADMIN_PASS}
RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_DEFAULT_VHOST}
ports:
- "${RABBITMQ_BIND_IP:-127.0.0.1}:5671:5671"
- "127.0.0.1:15672:15672"
- "127.0.0.1:15692:15692"
volumes:
- ./data:/var/lib/rabbitmq
- ./rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro
- ./enabled_plugins:/etc/rabbitmq/enabled_plugins:ro
- ./tls:/etc/rabbitmq/tls:ro
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
interval: 30s
timeout: 10s
retries: 5
start_period: 40s
mem_limit: 2g
cpus: 2.0
ulimits:
nofile:
soft: 65536
hard: 65536
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
固定 hostname: rabbitmq-1 很重要:RabbitMQ 的节点名与数据目录恢复相关,使用 Quorum Queue 或 Stream 时不能随意改名。不要用 latest,固定补丁版本才能知道一次更新到底改变了什么。
渲染配置时只做静默校验,避免把管理员密码打印到终端记录:
cd /opt/rabbitmq
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps
首先检查节点状态和监听器:
cd /opt/rabbitmq
docker compose exec rabbitmq rabbitmq-diagnostics -q ping
docker compose exec rabbitmq rabbitmq-diagnostics -q listeners
docker compose exec rabbitmq rabbitmq-diagnostics -q check_local_alarms
docker compose logs --tail=200 rabbitmq
从宿主机验证证书链和主机名:
openssl s_client \
-connect 127.0.0.1:5671 \
-servername mq.example.com \
-CAfile /opt/rabbitmq/tls/ca.crt \
-verify_return_error </dev/null
输出应包含 Verify return code: 0 (ok)。再检查 Prometheus 指标:
curl -fsS http://127.0.0.1:15692/metrics | head
至少监控节点不可用、连接数、未确认消息、ready 消息积压、发布/消费速率、内存告警和磁盘告警。长期告警与图表可接入 Prometheus + Grafana 监控方案。
管理后台使用单独域名,例如 mq-admin.example.com。编辑 /etc/caddy/Caddyfile:
mq-admin.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:15672
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
}
}
验证并重载:
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
curl -fsSI https://mq-admin.example.com/
Caddy 只负责管理 UI 的 HTTPS,不代理 AMQP。生产环境最好让管理域名只通过 VPN、零信任访问或固定管理 IP 使用;至少要启用强密码、限制来源并及时安装补丁。更多多域名和证书排查方法可参考 Caddy 自动 HTTPS 与反向代理指南。
管理员账号不应写进业务应用。为每个环境或服务创建独立用户、独立 vhost 和可轮换密码。下面创建只能访问 production vhost、且只能操作 app. 前缀资源的用户:
cd /opt/rabbitmq
APP_USER=worker
APP_PASS="$(openssl rand -hex 32)"
docker compose exec rabbitmq rabbitmqctl add_user "$APP_USER" "$APP_PASS"
docker compose exec rabbitmq rabbitmqctl set_permissions \
-p production "$APP_USER" '^app\.' '^app\.' '^app\.'
install -m 0600 /dev/null app-client.env
printf 'RABBITMQ_USER=%s\nRABBITMQ_PASSWORD=%s\n' \
"$APP_USER" "$APP_PASS" > app-client.env
unset APP_USER APP_PASS
三个权限正则依次是 configure、write 和 read。示例要求 Queue、Exchange 等资源以 app. 开头;如果客户端要声明其他名称,应精确调整正则,而不是直接给 .*。业务用户不需要 administrator、monitoring 或 management 标签。
客户端使用 amqps://,信任内部 CA,并把密码放在密钥管理或受限环境变量中。若密码含有 URI 保留字符,必须进行 URL 编码;本文用十六进制随机值是为了减少连接字符串转义错误。
把 Queue 声明为 durable,并不等于消息永不丢失。生产者和消费者还应同时处理:
- 消息标记为 persistent;
- 生产者开启 publisher confirms,并处理 nack 或超时;
- 消费者完成业务后再手动 ack,失败时明确重入队或进入死信队列;
- 设置 prefetch,避免一个消费者拿走过多未确认消息;
- 对重试次数、TTL 和 dead-letter exchange 建立统一策略;
- 使用业务幂等键,防止网络重试造成重复扣款或重复发货。
RabbitMQ 保证的是消息传递机制,不会替业务自动解决重复消费。对于关键事件,应以“至少一次投递 + 消费端幂等”为基线,并用真实故障测试确认重连和积压恢复行为。
RabbitMQ 的 definitions 包括用户、vhost、权限、策略、Queue、Exchange 和 Binding,但不包含 Queue 中正在等待的消息。运行中的节点可以安全导出 definitions:
cd /opt/rabbitmq
BACKUP_DIR="backups/$(date -u +%Y%m%dT%H%M%SZ)"
install -d -m 0700 "$BACKUP_DIR"
docker compose exec rabbitmq rabbitmqctl export_definitions \
/var/lib/rabbitmq/definitions-backup.json
docker compose cp \
rabbitmq:/var/lib/rabbitmq/definitions-backup.json \
"$BACKUP_DIR/definitions.json"
docker compose exec rabbitmq rm -f \
/var/lib/rabbitmq/definitions-backup.json
test -s "$BACKUP_DIR/definitions.json"
chmod 600 "$BACKUP_DIR/definitions.json"
definitions 中包含用户密码哈希,仍应作为敏感数据加密保存。恢复拓扑时可在新节点执行:
docker compose cp definitions.json \
rabbitmq:/var/lib/rabbitmq/definitions-restore.json
docker compose exec rabbitmq rabbitmqctl import_definitions \
/var/lib/rabbitmq/definitions-restore.json
如果还要备份消息,官方明确不建议复制运行中节点的数据目录,因为持续写入会产生不一致快照。单节点需要安排维护窗口,停止生产者和消费者,再停止 RabbitMQ:
cd /opt/rabbitmq
FULL_BACKUP="backups/rabbitmq-full-$(date -u +%Y%m%dT%H%M%SZ).tar.gz"
docker compose stop rabbitmq
sudo tar -czf "$FULL_BACKUP" \
data .env compose.yml rabbitmq.conf enabled_plugins tls pki
sudo chown "$USER":"$USER" "$FULL_BACKUP"
chmod 600 "$FULL_BACKUP"
docker compose start rabbitmq
test -s "$FULL_BACKUP"
docker compose exec rabbitmq rabbitmq-diagnostics -q ping
完整数据恢复必须使用原节点名 rabbit@rabbitmq-1 和兼容版本,且应在隔离环境演练。备份只放在同一台 VPS 上不能抵御磁盘损坏或主机丢失,必须再复制到异机或对象存储。
升级前先阅读目标版本的升级文档与 Erlang 兼容要求,并完成 definitions 和完整数据备份。不要跳过不受支持的版本序列。
单节点升级会产生停机:
cd /opt/rabbitmq
docker compose exec rabbitmq rabbitmqctl await_startup
docker compose exec rabbitmq rabbitmqctl export_definitions \
/var/lib/rabbitmq/pre-upgrade-definitions.json
# 修改 compose.yml 中固定的镜像补丁版本后再执行
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose exec rabbitmq rabbitmqctl await_startup
docker compose exec rabbitmq rabbitmq-diagnostics -q ping
docker compose exec rabbitmq rabbitmq-diagnostics -q check_local_alarms
docker compose logs --tail=200 rabbitmq
三节点集群应按官方滚动升级顺序逐台处理,确认版本兼容与 Queue 可用性后再继续下一节点。不要把“容器能启动”当成升级成功,还要验证生产者确认、消费者 ack、积压下降、管理 API 和 Prometheus 告警。
先确认监听、宿主机映射和云安全组:
ss -lnt | grep ':5671'
docker compose ps
docker compose exec rabbitmq rabbitmq-diagnostics -q listeners
openssl s_client -connect 127.0.0.1:5671 \
-servername mq.example.com \
-CAfile tls/ca.crt </dev/null
如果本机正常而远程失败,检查 RABBITMQ_BIND_IP、私网路由、云安全组和 Docker 端口规则。可按 端口、防火墙、安全组与监听地址排查指南逐层定位,不要改回公开的 5672 明文连接。
客户端连接的域名必须出现在服务器证书 SAN 中,并由客户端信任的 CA 签发。不要用关闭证书验证来“修复”生产连接;应修正私网 DNS、证书 SAN、系统时间和 CA 文件路径。
确认用户名、vhost 和三类权限:
docker compose exec rabbitmq rabbitmqctl list_users
docker compose exec rabbitmq rabbitmqctl list_vhosts
docker compose exec rabbitmq rabbitmqctl list_permissions -p production
连接 URI 中的 vhost 需要正确编码。默认 / 在 URI 中通常写成 %2F,本文的 production 则直接使用 /production。
检查内存和磁盘资源告警:
docker compose exec rabbitmq rabbitmq-diagnostics -q check_local_alarms
docker stats rabbitmq --no-stream
df -h /opt/rabbitmq/data
du -sh /opt/rabbitmq/data
内存或磁盘达到阈值时,RabbitMQ 会阻止发布者以保护节点。不要只提高阈值;应先控制积压、恢复消费者、扩容磁盘并检查异常消息大小。
开发环境可从 2 vCPU、2GB 内存开始。小型生产更建议 2–4 vCPU、4GB 内存和 60GB NVMe;真实需求取决于连接数、消息大小、Queue 数量、积压时间与确认模式,必须用业务负载压测。
不建议。5672 是明文 AMQP,本文直接禁用;15672 管理后台只绑定回环并经 Caddy HTTPS 访问。远程客户端应使用 5671 TLS、私网或 VPN,并在云安全组限制来源。
durable 只保证 Queue 定义在重启后保留。消息还需要标记 persistent,生产者使用 publisher confirms,消费者正确 ack;未确认的网络中消息和错误的业务重试仍可能丢失或重复。
技术上可以声明,但单节点没有容错收益。Quorum Queue 至少需要 3 个独立 RabbitMQ 节点才能在一个节点故障时继续形成多数派;两节点和同机多容器都不能替代真正的三节点架构。
不包含。definitions 只保存用户、vhost、权限、策略与拓扑。消息位于节点数据目录;备份消息必须先停止写入并停止节点,再对完整数据目录做一致性备份。
默认用户环境变量只在空数据目录初始化时生效。已有数据卷时需要使用 rabbitmqctl change_password 或管理 API 主动轮换,并同步更新应用密钥后验证重连。
