项目背景
目标:在阿里云 Linux 服务器上用 Docker 部署 NapCat(QQ 机器人框架),并安装 QQ Chat Exporter (QCE) 插件导出聊天记录。
第一阶段:为什么要从裸机部署改为 Docker 部署
最初的问题:二维码转圈,扫不了码
现象: NapCat 页面能打开,但二维码一直在转圈加载,登录不了 QQ。
根本原因: NapCat 底层依赖 QQ 的 Electron 客户端来显示登录二维码。Electron 是一个桌面应用框架,它需要显卡(GPU)来渲染界面。但云服务器是"无头"环境,没有显示器,也没有 GPU,所以日志里反复报:
GPU 进程崩溃 → QQ 内部的登录 UI 流程卡死 → 二维码数据根本没生成 → WebUI 一直转圈。
为什么换 Docker 能解决? Docker 的官方 NapCat Framework 镜像(mlikiowa/napcat-framework-docker)内置了 Xvfb(虚拟显示服务器)和 NoVNC(网页版远程桌面),专门为无 GPU 的服务器环境设计。
第二阶段:Docker 部署过程
Bug 1:Docker Compose 模板格式错误
现象: 宝塔面板报"模板内容格式错误"。
原因: 宝塔面板对 Docker Compose 语法有额外限制,add-hosts(正确写法应是 extra_hosts)、注释、引号等都可能触发解析报错。
解决: 放弃 Compose 模板,改用纯命令行 docker run 直接部署,彻底绕开面板的格式校验。
Bug 2:容器内网络完全断开,无法访问任何外网
现象: 在容器内执行 curl https://www.baidu.com 报 Could not resolve host,连百度都访问不了,导致 QQ 登录时报"网络连接失败"。
根本原因: 这是阿里云 + Docker + Firewalld 三者的经典冲突。
Docker 运行时需要在操作系统底层写入两类 iptables 规则:
- IP 转发规则:允许服务器把容器的网络包转发出去
- NAT MASQUERADE 规则:把容器的内网 IP 转换成服务器的公网 IP 发送
但阿里云服务器默认启用了 firewalld(系统防火墙),每次执行 firewall-cmd --reload 都会清空所有 iptables 规则,连 Docker 自己注入的也一起清掉了。清完之后容器就变成了"断网孤岛"。
修复过程:
- 开启系统 IP 转发(允许数据包过路):
bash echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf sysctl -p - 让 firewalld 永久允许 NAT 转发:
bash firewall-cmd --permanent --add-masquerade firewall-cmd --reload - 重启 Docker,让它重新注入所有网络规则:
bash systemctl restart docker - 重启后发现 SSL 证书文件缺失(
error setting certificate file),这说明网络其实通了,只是容器内没有 CA 证书包。安装即可:bash apt-get install -y ca-certificates && update-ca-certificates
Bug 3:容器重启后二维码又不出来了,VNC 也断了
现象: 每次 docker restart 之后,之前手动在容器里执行的所有操作(mkdir、chmod、启动 x11vnc、启动 websockify)都消失了。
根本原因: Docker 容器的文件系统是"临时层",容器重启后所有手动写入容器内部的修改都会丢失。只有通过 -v 挂载的宿主机目录才能持久保存。
这意味着之前的启动命令没有挂载所有必要目录,导致:
plugins目录丢失 → 插件不加载qce-v4-tool前端目录丢失 → 网页 404/.qq-chat-exporter数据目录丢失 → QCE 插件报权限错误
第三阶段:最终正确的部署命令
把所有教训汇总,最终的完整启动命令是:
每个参数的意义:
| 参数 | 作用 | 为什么需要 |
|---|---|---|
--network host | 容器直接使用服务器网络 | 彻底解决 Docker 网桥的 NAT 转发冲突问题 |
--dns=8.8.8.8 | 指定 DNS 服务器 | 防止容器内 DNS 解析失败 |
-e VNC_PASSWD=... | 设置 VNC 远程桌面密码 | 首次扫码登录 QQ 时需要通过 VNC 操作 |
-v qqdata:/app/.config/QQ | 持久化 QQ 登录状态 | 重启容器后不需要重新扫码 |
-v plugins:/app/napcat/plugins | 持久化插件目录 | 重启后插件不丢失 |
-v qce-v4-tool:/app/napcat/static/qce-v4-tool | 持久化 QCE 前端文件 | 重启后网页不 404 |
-v qce-data:/.qq-chat-exporter | 持久化 QCE 数据目录 | 解决权限问题 + 导出文件不丢失 |
第四阶段:QCE 插件报错修复
Bug 4:QCE 插件启动时报权限错误
原因: QCE 插件需要在容器根目录下创建数据文件夹,但容器内的进程没有权限往 / 根目录写文件。
解决: 在宿主机上提前创建好这个目录并赋予权限,然后通过 -v 挂载进容器。这样容器启动时目录已经存在且有写权限,插件就不需要自己创建了:
第五阶段:首次登录流程
容器启动后,VNC 服务不会自动对外暴露,需要手动启动:
然后在浏览器访问 http://服务器公网IP:6081,输入 VNC 密码,就能看到 QQ 的登录界面,用手机扫码登录。
登录成功后,这个步骤只需做一次。因为 QQ 数据已经挂载到宿主机,以后重启容器会自动保持登录状态。
整体问题根源总结
回顾整个过程,所有问题都可以归结为三个根本矛盾:
矛盾一:图形化应用 vs 无头服务器 QQ 是桌面应用,依赖 GPU 渲染,但云服务器没有显卡。解决方法是用 Xvfb 模拟一个虚拟显示器。
矛盾二:容器网络 vs 系统防火墙 Docker 需要控制 iptables 来做 NAT 转发,但 firewalld 会定期清空这些规则。解决方法是用 --network host 让容器直接用宿主机网络,彻底绕开这个冲突。
这是一个相当普遍的问题,但不是所有服务器都会遇到,取决于操作系统和云服务商的默认配置组合。
具体来说,你遇到这个问题是以下几个因素叠加导致的:
你的服务器的具体情况(阿里云 + CentOS/Alibaba Cloud Linux) 阿里云的 Linux 镜像默认启用 firewalld,而 CentOS/RHEL 系(包括 Alibaba Cloud Linux)也是默认开 firewalld 的。firewalld 和 Docker 同时管理 iptables,就容易产生冲突。这个组合在国内云服务商(阿里云、腾讯云、华为云)上非常常见,所以你遇到这个问题的人其实很多。
不会遇到这个问题的情况 如果服务器用的是 Ubuntu,默认使用 ufw 而不是 firewalld,Docker 和 ufw 的共存方式不同,通常不会清空 Docker 的 iptables 规则,所以 Ubuntu 上部署 Docker 一般不需要这些额外操作。大多数 Docker 教程默认读者用 Ubuntu,这也是为什么很多教程里完全没有提到这个问题。
结论 这个问题不是你服务器的个例,但也不是所有服务器都有——它是"阿里云 + CentOS 系 + Docker + firewalld"这个特定组合下的常见坑。如果你下次用 Ubuntu 镜像开服务器,部署 Docker 会顺很多。
矛盾三:容器临时性 vs 数据持久化需求 容器重启后所有修改都消失,必须通过 -v 挂载将所有需要持久保存的目录(QQ 数据、插件、配置、导出文件)映射到宿主机上。