必须用独立容器+自定义bridge网络,go服务监听0.0.0.0:$port而非127.0.0.1,且go进程须为pid 1并正确处理sigterm以实现优雅退出。

必须用独立容器 + 自定义 bridge 网络,否则连接会互相干扰、SIGTERM 无法透传、健康检查失败。
Go服务监听地址必须是 0.0.0.0:$PORT,不是 127.0.0.1:$PORT
容器内 127.0.0.1 指向自身,外部(包括反向代理、其他容器、curl)根本连不上。Docker bridge 网络靠的是容器 IP + 端口,不是 localhost。
- 错误写法:
http.ListenAndServe("127.0.0.1:8080", nil)→docker logs看不到连接日志,curl直接超时 - 正确写法:
http.ListenAndServe(":8080", nil)或更明确的net.Listen("tcp", ":8080")+http.Serve - 若用
websocket.Upgrader,也要确保http.Server.Addr是:8080,不能绑定到 loopback
多个实例必须走不同宿主机端口 + 同一自定义网络
默认 bridge 网络 DNS 不稳定;host 模式绕过防火墙且端口冲突风险高;错开宿主机端口才能并行暴露。
- 在
docker-compose.yml中定义一个自定义网络,例如conn_net,所有长连接服务都接入它 - 每个服务用不同宿主机端口映射:
- "9001:8080"、- "9002:8080",避免bind: address already in use - 服务间通过
container_name或service名互访(如conn-01:8080),不依赖 IP - 禁止在容器内改
iptables或做端口转发——会破坏 WebSocket/TCP keepalive 的连接状态跟踪
docker stop 时 Go 进程必须收到 SIGTERM 并优雅退出
如果 docker stop 后连接立刻断开、没走 http.Server.Shutdown(),说明 PID 1 不是你主程序,信号被拦截了。
- 错误做法:
sh -c "./server & wait"或supervisord——sh成为 PID 1,SIGTERM不透传 - 正确做法:确保 Go 二进制是 PID 1;
main()中监听os.Interrupt和syscall.SIGTERM - 调用
srv.Shutdown()时务必加 context 超时,例如ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - 验证方式:
docker stop container-name后查docker logs container-name,应看到 “shutting down…” 日志
最容易被忽略的是:每个容器必须真正隔离——不能共享 PID namespace,不能共用监听地址,也不能让任意一个服务意外成为其他服务的网络跳板。一旦用了自定义网络和 0.0.0.0 监听,还要确保 Go 进程是 PID 1,这三个条件缺一不可。











