问题描述
在容器中运行一个websocket client时,连接websocket server失败,并且无报错信息。
建立websocket连接的代码为:
// var wsAdapterEndpoint = "wss://openbot.xxxx-inc.com/ws"
func (l *GuardBot) setupWebsocketConn() error {
c, rsp, err := websocket.DefaultDialer.Dial(wsAdapterEndpoint, nil)
if err != nil {
logger.Fatalf("failed to connect ws, endpoint: %v, error: %v", wsAdapterEndpoint, rsp)
return err
}
l.conn = c
return nil
}
报错信息如下:
setupWebsocketConn() failed to connect ws, endpoint: wss://openbot.xxxx-inc.com/ws, error: <nil>
问题分析
- 该程序在本地host上直接运行是正常的,可以成功连到websocket server。
- 容器中启动该程序时,连接失败。使用
telnet openbot.xxxx-inc.com命令发现其IP和本地解析出来的的IP地址是一样的,排除了网络域名解析问题。
由于缺乏error日志,只能一点点排查了。在容器内使用命令apt-get install curl后,直接启动执行文件竟然可以连接成功websocket server了。看了下apt-get install curl的日志,发现有openssl和ca证书相关组件也被安装了。恰好我们使用的websocket连接是加密过的(wss连接),极大可能就是由这个引起的。
于是重启了一把容器,在容器内手动安装了openssl和ca工具包,手动执行程序发现果然可以连接websocket server成功。
问题解决
Dockerfile中构建镜像时,添加openssl和证书相关工具包即可。启动容器发现可以正常连接到websocket server。
FROM debian:buster-slim
RUN apt-get update && apt-get install -y openssl ca-certificates
...
其他
没想到debian的基础镜像连ca证书包都精简掉了,刚好碰到github.com/gorilla/websocket包连接wss缺少ca时没有报错信息。这次问题也算是瞎猫碰到死耗子。
不过还是可以靠排除法来逐步缩小范围,譬如把websocket server端不使用tls,直接用ws连接,这样容器内应该就能连上websocket,从而判断出是tls、证书相关问题。
有时间还是要看一下github.com/gorilla/websocket包的源码,分析下为啥这种场景没有error日志。