20
0
0

Docker 部署 RabbitMQ:队列还在,消息没了

2026-10-29
2026-10-30
文章摘要
|

背景与目标

RabbitMQ 是最常用的消息中间件之一,用来做异步任务、解耦服务、削峰填谷。Windows 上原生安装要配 Erlang 环境,版本对不上就起不来,容器化省掉了这层麻烦。

但它的容器化有个别处没有的特点:RabbitMQ 是 Erlang 节点,节点名长这样——rabbit@rabbitmq,后半段就是容器的 hostname。数据目录里记着这个身份,Erlang cookie 也绑在同一个目录。所以会出现两类完全不同的"数据丢失":

  • 节点层:卷挂错、hostname 变了,容器重建后认不出自己的旧数据,队列和用户直接"消失";

  • 消息层:节点好好的,队列也在,但消息没落盘,重启后只剩空队列。

本文给你一份把两层都配好的 compose,并把这两层各自的坑拆开讲。

提示:宿主 Windows (Docker Desktop),镜像走华为云。管理界面由 -management 镜像提供,基础镜像里没有。

步骤

第 0 步:确认 Docker Desktop 已就绪

docker version
docker compose version

输出版本号即 OK。

第 1 步:准备目录

mkdir rabbitmq && cd rabbitmq

第 2 步:写 docker-compose.yaml

新建 docker-compose.yaml,整段复制:

services:
  rabbitmq:
    image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/rabbitmq:4-management
    container_name: rabbitmq
    hostname: rabbitmq
    environment:
      RABBITMQ_DEFAULT_USER: "admin"
      RABBITMQ_DEFAULT_PASS: "YourStrong!Passw0rd"
      RABBITMQ_DEFAULT_VHOST: "/"
      TZ: "Asia/Shanghai"
    ports:
      - "5672:5672"
      - "15672:15672"
    volumes:
      - ./rabbitmq-data:/var/lib/rabbitmq
    restart: unless-stopped

要点:

  • hostname: rabbitmq 不能省。节点名由此决定,重建容器时 hostname 一旦变化,旧数据就"不属于"新节点。

  • 卷挂的是 /var/lib/rabbitmq 整个目录,不是下面的 mnesia 子目录。.erlang.cookie 和 mnesia/ 都在这一层。

  • RABBITMQ_DEFAULT_USER/PASS:首次初始化时创建的管理账号,避开受限的 guest。

  • 端口:5672 是 AMQP 协议端口,程序连这个;15672 是管理界面,浏览器开这个。

  • 单节点适合开发与个人使用;集群与高可用不在本文范围。

第 3 步:启动

docker compose up -d

首次启动要初始化节点,稍等。看日志:

docker compose logs -f rabbitmq

看到 Server startup complete 即就绪。

第 4 步:连接验证

先确认节点活着:

docker compose exec rabbitmq rabbitmq-diagnostics -q ping

返回 Ping succeeded 即 broker 正常。

再用浏览器打开管理界面:

http://localhost:15672

用 admin / YourStrong!Passw0rd 登录,然后走一遍收发闭环:

  1. Queues → Add a new queue,Name 填 demo,Durability 选 Durable;

  2. 进 demo,Publish message 随便填个 payload 发一条;

  3. 在队列页 Get messages,能取到刚才那条。

能登录、能建队列、能发能收,说明 5672 与 15672 都通了。

验证

ScreenShot_2026-10-29_102301_982.png
ScreenShot_2026-10-29_102309_524.png

踩坑与解决

1. 卷挂到了 mnesia 子目录:重建后用户和队列全没

  • 问题:容器重建后,之前建的队列、用户、权限都没了。

  • 复现:volumes 写成 ./rabbitmq-data:/var/lib/rabbitmq/mnesia,docker compose down 再 up,管理界面回到初始状态。

  • 根因:Erlang cookie(.erlang.cookie)在 /var/lib/rabbitmq 这一层,mnesia/ 只放元数据。只挂子目录时 cookie 留在容器可写层,随容器销毁重新生成,新 cookie 与旧数据记录的节点身份对不上。

  • 解决:挂父目录 /var/lib/rabbitmq(本文写法)。已经挂错的,改成父目录后 docker compose down -v 重建,接受一次数据清空。

  • 验证:重建后管理界面里的 queues / users 仍在。

2. hostname 变了,节点起不来或数据"回退"

  • 问题:不写 hostname 时,删容器重建后 RabbitMQ 启动报节点相关错误,或数据像被重置。

  • 复现:去掉 hostname: rabbitmq,执行 docker compose down && docker compose up -d,日志出现 unable to perform an operation on node 之类的报错。

  • 根因:节点名是 rabbit@<hostname>。不指定 hostname 时 Docker 用容器 ID,每次重建都变;数据目录里的旧节点名与新节点名不匹配。

  • 解决:固定 hostname: rabbitmq(本文已写),且不要随意改。真要改名,单机场景直接删卷重建最省事;集群场景才考虑 rabbitmqctl rename_cluster_node。

  • 验证:docker compose exec rabbitmq hostname 始终输出 rabbitmq,重建后不变。

3. 管理界面 15672 打不开:镜像不带插件

  • 问题:容器起来了,http://localhost:15672 却连不上。

  • 复现:镜像用的是 rabbitmq:4(不带 -management),15672 端口无响应。

  • 根因:management 是插件,基础镜像默认不启用,也不会监听 15672。

  • 解决:换成 rabbitmq:4-management(本文);或进容器手动启用:

    docker compose exec rabbitmq rabbitmq-plugins enable rabbitmq_management

    由于挂了 /var/lib/rabbitmq,插件启用状态会随数据目录保留。

  • 验证:浏览器能打开登录页。

4. guest 登录被拒:它只允许本机连接

  • 问题:想用默认的 guest/guest 登录管理界面,提示登录失败。

  • 复现:删掉 RABBITMQ_DEFAULT_* 后从宿主机浏览器登录,日志/客户端报 user 'guest' can only connect via localhost。

  • 根因:guest 是受限账号,只允许回环地址连接。容器里的"localhost"不是你的宿主机,所以从浏览器、其他容器、其他机器都连不上。

  • 解决:显式设 RABBITMQ_DEFAULT_USER / RABBITMQ_DEFAULT_PASS(本文),业务再用独立账号或 vhost 隔离;不要放开 guest 的 loopback 限制。

  • 验证:用 admin 能从宿主机登录,guest 依旧被拒。

5. 重启后队列还在,消息没了

  • 问题:管理界面里队列 demo 还在,但里面的消息空了。

  • 复现:发几条消息,docker compose down && docker compose up -d,消息数量归零。

  • 根因:Docker 卷只保住 broker 的元数据(队列定义、绑定、用户)。消息是否落盘,取决于三件事同时成立:队列声明为 durable、消息投递时设 delivery_mode=2(persistent)、以及等你收到 publisher confirm 才认为发送成功。缺任何一件,重启都可能丢。

  • 解决:生产消息时用持久化队列 + 持久化投递 + 开启 publisher confirm;只有明确可丢的消息(日志、指标)才用非持久化换吞吐。

  • 验证:发 10 条持久化消息,down 再 up,队列里仍是 10 条。

通用坑(与 SQL Server / MySQL / Redis / MongoDB / PostgreSQL / MinIO 篇相同,不再重复展开)

  • 端口被占:5672 或 15672 已被别的程序占用,报 port is already allocated。改外部端口,如 5673:5672、15673:15672,客户端连接串和浏览器地址同步改。

  • 数据丢了:没挂卷,容器销毁即丢失。本教程已挂 ./rabbitmq-data:/var/lib/rabbitmq。

  • 容器秒退:先看 docker compose logs rabbitmq,再对照上面第 1、2 条。

小结与参考

  • 用 Docker 部署 RabbitMQ 4,两个端口分工明确:5672 给程序,15672 给管理界面。

  • 节点身份靠两样固定:卷挂 /var/lib/rabbitmq 整个目录、hostname 写死不改。

  • 本篇核心矛盾:持久化分两层。卷 + 稳定 hostname 保住"节点",durable 队列 + persistent 消息 + publisher confirm 才保住"消息";只做一半,就会出现"队列在、消息没"。

  • 不要用 guest 做跨主机连接,首次就设 RABBITMQ_DEFAULT_USER/PASS。

  • 管理界面需要 -management 镜像或手动启用插件。

参考:

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或者给予支持!

Docker 部署 RabbitMQ:队列还在,消息没了
/archives/docker-bu-shu-rabbitmq-dui-lie-huan-zai-xiao-xi-mei-liao
作者
隔壁老陈
发布于
2026-10-29
许可协议
CC BY-NC-SA 4.0

评论