不推荐单容器运行多个go长连接实例。应使用独立容器+自定义bridge网络,监听0.0.0.0:$port,确保go进程为pid 1并正确处理sigterm以实现优雅退出。

不推荐在单个容器里跑多个 Go 长连接实例。长连接服务对信号处理、资源隔离、日志追踪和优雅退出要求高,混在一个容器里极易相互干扰。
Go 长连接服务必须监听 0.0.0.0:$PORT 而非 127.0.0.1:$PORT
容器网络模型下,127.0.0.1 指向容器自身,外部(包括反向代理、健康检查探针、其他容器)根本连不上。Docker 的 bridge 网络靠的是容器 IP + 端口,不是 localhost。
- 错误写法:
http.ListenAndServe("127.0.0.1:8080", nil)→ 容器外无法访问,docker logs看不到连接建立日志,curl直接超时 - 正确写法:
http.ListenAndServe("0.0.0.0:8080", nil)或更安全的net.Listen("tcp", ":8080")+http.Serve - 如果用
websocket.Upgrader,也要确保http.Server.Addr是:8080,而非绑定到 loopback
多个实例必须用独立容器 + 自定义 bridge 网络
用 docker-compose.yml 启动多个长连接服务时,别用默认 bridge,也别用 host 模式——前者 DNS 不稳定,后者端口冲突且绕过防火墙。
- 先创建网络:
docker network create my-longconn-net - 每个服务声明相同网络,并通过
container_name或service名互相发现(如conn-01、conn-02) - 对外暴露端口必须错开:
- "9001:8080"、- "9002:8080",避免bind: address already in use - 不要在容器内做
iptables或端口转发,那会破坏连接状态跟踪(尤其 WebSocket / TCP keepalive)
docker stop 时 Go 进程收不到 SIGTERM?检查你的 ENTRYPOINT
长连接服务最怕强制 kill。如果 docker stop 后连接立刻断开、没走 http.Server.Shutdown(),大概率是 PID 1 不是你主程序。
- 错误做法:用
sh -c "./server & wait"或supervisord——sh成为 PID 1,SIGTERM 不自动透传给子进程 - 正确做法:直接
ENTRYPOINT ["./server"],确保 Go 二进制是 PID 1;并在main()中监听os.Interrupt和syscall.SIGTERM - 务必调用
srv.Shutdown()并等待活跃连接关闭(例如加ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)) - 验证方式:
docker stop -t 20 container-name,然后docker logs container-name查看是否输出 “shutting down…”
真正麻烦的不是启动多个实例,而是让它们各自维持住自己的连接生命周期、不抢端口、不共用 stdout、不互相杀进程。拆成独立容器后,每个实例的资源限制(--memory、--cpus)、健康检查(healthcheck)、重启策略(restart: on-failure)才能单独控制——这点在长连接场景下比“省一个容器”重要得多。











