凌晨三点,我对着屏幕上那行 Connection refused
发呆。两个Docker容器,明明都在同一台机器上,却像失联了一样无法通信。我打开第六罐红牛,心想这事儿不应该这么复杂。事实证明,它确实不复杂,只是藏了很多坑等着你往里跳。🌃
问题的起点:两个世界的容器
看起来很简单的需求
我要做的事情听起来直白:让 LangBot 和 WeChatPadPro 两个服务互相通信。一个负责AI对话,一个对接微信消息,逻辑上它们应该是天然的搭档。
但现实给了我一巴掌。这两个服务分别跑在不同的 docker-compose
项目里,它们之间的距离,比我想象的要远。
第一次尝试的错误是这样的:
HTTPConnectionPool(host='127.0.0.1', port=1238): Max retries exceeded
Connection refused
我检查了端口占用,看了防火墙规则,确认服务在运行。一切看起来都正常,但就是连不上。就像你明明看见对方在线,消息就是发不出去,对方也收不到。
Docker重新定义了"本地"
在没有容器的时代,127.0.0.1
就是本机,同一台机器上的服务通过端口就能互相访问。Docker改变了这个规则。
每个容器都是一个独立的运行环境。当你在容器内部访问 127.0.0.1
时,你访问的是容器自己,不是宿主机,更不是其他容器。这就像你站在自己房间里喊"有人吗",隔壁邻居是听不见的。
更麻烦的是,不同的 docker-compose
项目会创建各自的网络:
# WeChatPadPro 项目创建的网络
deploy_wechatpadpro-network
# LangBot 项目创建的网络
docker_langbot-network
这两个网络就像两条平行线,永远不会相交。除非你做点什么。🔌
配置过程:一场细节的战争
第一步:创建一座桥梁
解决方案的核心思路是:创建一个外部网络,让两个项目的容器都加入进来。
docker network create langbot-network
这个命令创建了一个独立于任何项目的网络。它不属于 WeChatPadPro,也不属于 LangBot,它是中立的公共区域。
如果执行时提示网络已存在,说明之前创建过,直接用就行:
Error response from daemon: network with name langbot-network already exists
这不是错误,反而是好消息——网络已经在那里了。
第二步:修改 WeChatPadPro 配置
打开 docker-compose.yml
,找到网络配置部分。这里是最容易出错的地方。
错误的写法:
networks:
langbot-network:
driver: bridge # ❌ 这会创建新网络,不是使用外部网络
正确的写法:
networks:
wechatpadpro-network:
driver: bridge
langbot-network:
external: true # ✅ 标记为外部网络
然后在服务配置里指定网络:
services:
wechatpadpro:
networks:
- wechatpadpro-network # 内部网络,访问数据库
- langbot-network # 外部网络,对外通信
mysql:
networks:
- wechatpadpro-network # 只在内部网络
redis:
networks:
- wechatpadpro-network # 只在内部网络
注意这个设计:MySQL 和 Redis 只在内部网络,外部容器无法直接访问它们。这是有意为之的安全设计。
第三步:修改 LangBot 配置
同样的道理,修改 LangBot 的配置:
services:
langbot_plugin_runtime:
networks:
- langbot-network
langbot:
networks:
- langbot-network
networks:
langbot-network:
external: true # 不能少这行
关键点:网络名必须完全一致。langbot-network
和 langbot_network
是两个不同的网络,连字符和下划线的差异会让你的配置彻底失效。
第四步:重建容器
配置改完要生效,必须重建容器:
# WeChatPadPro 目录
docker-compose down
docker-compose up -d
# LangBot 目录
docker-compose down
docker-compose up -d
启动时注意观察日志。正确的日志应该是:
Network langbot-network External ✓
如果看到这样的日志:
Network deploy_langbot-network Created ✗
说明配置还是错的,Docker 又创建了一个新网络,而不是使用外部网络。
第五步:验证网络
检查网络中的容器:
docker network inspect langbot-network
应该看到输出里包含三个容器:
- wechatpadpro
- langbot
- langbot_plugin_runtime
然后测试连通性:
docker exec -it wechatpadpro ping langbot
能 ping 通就说明网络配置成功了。如果报错 ping: executable file not found
,不用担心,这只是镜像里没装 ping 工具,不代表网络不通。换个方式:
docker exec -it langbot curl http://wechatpadpro:8080
那些让人抓狂的细节
127.0.0.1 的惯性思维
LangBot 的配置文件里最初写的是:
wechatpad_url: http://127.0.0.1:1238
在容器里,这个地址指向的是容器自己,不是 wechatpadpro。必须改成容器名:
wechatpad_url: http://wechatpadpro:1238
Docker 的内置 DNS 会自动解析容器名到对应的 IP。这是容器间通信的标准做法。
external: true 的必要性
如果配置文件里只写:
networks:
langbot-network:
driver: bridge
Docker 会认为这是一个项目内的网络,会创建一个带项目前缀的新网络,比如 deploy_langbot-network
。
只有明确标记 external: true
,Docker 才会去寻找已存在的外部网络:
networks:
langbot-network:
external: true
这一行,不能省。
网络名称的字符陷阱
Docker 网络名区分大小写,也区分连字符和下划线:
网络名 | 是否相同 |
---|---|
langbot-network | 基准 |
Langbot-Network | ❌ 不同(大小写) |
langbot_network | ❌ 不同(下划线) |
langbot-network | ✅ 相同 |
我就在这个细节上卡了半小时。两个配置文件,一个用连字符,一个用下划线,看起来差不多,但 Docker 认为它们是两个完全不同的网络。
容器重启的必要性
修改 docker-compose.yml
后,必须重建容器才能生效。简单的 docker-compose restart
是不够的,因为网络配置在容器创建时就确定了。
必须用:
docker-compose down # 删除容器
docker-compose up -d # 重新创建
从网络问题到应用问题
错误信息的进化
配置完网络后,我以为一切搞定了。但 LangBot 还是报错:
500 Server Error: Internal Server Error for url:
http://wechatpadpro:1238/login/GetLoginStatus
但这次的错误性质变了:
- 之前是
Connection refused
—— 网络不通 - 现在是
500 Server Error
—— 网络通了,但接口报错
这说明网络层面已经没问题了,问题转移到了应用层。
WeChatPadPro 的真正问题
查看 wechatpadpro 的日志:
docker logs wechatpadpro --tail=50
发现了真相:
GET Connection locfree Failed by e36bdea0-f39a-4252-8fdc-913cf026e535
❌ 登录过程中发生panic
❌ 用户登录失败
登录初始化完成: 0/1 用户成功登录
原来问题不在网络,而在微信登录失败。WeChatPadPro 服务本身启动了,但微信账号没有成功登录,所以接口返回 500 错误。
分层排查的重要性
这个过程给我的启示是:问题要分层看。
层级 | 问题表现 | 排查方法 |
---|---|---|
网络层 | Connection refused | 检查网络配置、容器连通性 |
应用层 | 500/404 等HTTP错误 | 查看应用日志、检查服务状态 |
业务层 | 逻辑错误、数据异常 | 检查配置、验证业务流程 |
我的问题经历了这样的演进:
- 网络层:Connection refused → 配置外部网络解决
- 应用层:500 Server Error → 需要先完成微信登录
网络通了不代表服务能用,这是两个层面的问题。
未解决的部分
当前的状态
截至现在:
- ✅ Docker 网络配置正确
- ✅ 容器间可以互相访问
- ✅ LangBot 能够请求 WeChatPadPro 的接口
- ❌ WeChatPadPro 的微信登录失败
- ❌ 整个服务链路还没跑通
问题从"网络不通"变成了"服务未就绪"。这是进步,但还没到终点。
下一步要做的事
根据 WeChatPadPro 的文档,需要:
- 访问 Swagger UI:
http://localhost:8080
- 填入 adminKey:从日志中获取
- 获取 token:调用
/admin/GanAuthKey
接口 - 扫码登录微信:调用
/login/GetLoginQrCodeNew
接口 - 在 LangBot 中配置:填入正确的 token 和 wxid
这些步骤都是业务层面的操作,和 Docker 网络无关了。但如果网络不通,连这些步骤都无法开始。
真实的技术工作
技术工作就是这样。你解决了一个问题,往往会发现另一个问题。问题不会消失,只会转移。
网络配置花了我两个小时,定位到微信登录问题又花了一个小时。但至少现在我知道:
- 网络没问题
- Docker 配置没问题
- 问题在 WeChatPadPro 的业务层
这就够了。定位问题比解决问题更重要,因为只有知道问题在哪儿,才能知道该往哪儿使劲。💪
配置检查清单
如果你遇到类似的 Docker 网络问题,按这个清单检查:
网络配置
- [ ] 创建了外部网络
docker network create xxx
- [ ] 两个 docker-compose.yml 的网络名完全一致
- [ ] 网络配置都标记了
external: true
- [ ] 服务配置中引用了正确的网络名
容器状态
- [ ] 执行了
docker-compose down
和up -d
- [ ] 启动日志显示
External
而不是Created
- [ ] 用
docker ps
确认所有容器都在运行 - [ ] 用
docker network inspect
确认容器在同一网络
应用配置
- [ ] 配置文件里用容器名替换了
127.0.0.1
- [ ] 端口号正确(容器内部端口,不是映射的宿主机端口)
- [ ] 相关的 token、密钥等配置正确
验证方法
- [ ]
docker exec
能在容器间通信 - [ ] 接口返回的是业务错误而非连接错误
- [ ] 查看了双方容器的日志
当网络层通了但服务还不工作时,停止折腾网络配置,去看应用日志。别在已经解决的问题上继续浪费时间。
天快亮了,外面传来早班公交车的声音。问题还没完全解决,但至少知道了问题在哪儿。有时候技术工作就是这样,你花大半夜时间,可能只是排除了一个方向,定位到了真正的问题。
但这就是进步。从漫无目的的摸索,到明确知道下一步要做什么,这就是价值。🌅
网络通了,剩下的就是业务问题了。那是另一个故事,也是明天的工作。现在,我需要睡觉。