构建 Docer 镜像失败
背景
有同事反馈发版平台线上推送失败,发版平台的推送包含3个步骤:
- 把包传给 deploy-dispatcher
- 构建 Docker 镜像
- 把镜像上传到 Harbor(Docker镜像仓库)
问题
问题出在第二步构建 Docker镜像,访问 Docker Daemon 报了个 could not find writer for content-type application/tar。
排查
-
这个异常来自 resteasy 的 httpClient。resteasy 是 jaxrs 其中一个实现,另一个常用的实现是 jersey。之前一直用的 jersey 没有报过这个异常,实现突然变成了 resteasy,判断中间一定发了版。
-
把代码拉下来看了依赖,有一个名为 creep 的日志组件依赖了 resteasy,所以 classpath 中有一个 resteasy-client,导致 SPI 选择了 resteasy 实现。
-
排除了 resteasy-client 依赖,发了个版上去观察,遇到新的异常:Caused by: org.apache.http.ProtocolException: The server failed to respond with a valid HTTP response。
-
看表面是 docker daemon 没有返回 符合 http 协议的报文,于是登陆 springboot-admin 打开了 org.apache.http.wire 的 DEBUG 日志级别,观测到 client 发出了完整的 http请求,收到了乱码的返回报文。
-
截图反馈给运维,运维查看了 docker daemon 的日志,显示 TLS 握手异常。于是让运维抓一个包 prod.cap,咋一看没发现问题:
-
由于开发环境可以正常构建镜像,于是让运维在开发环境抓一个包 dev.cap,比对一看,明显发现线上的 docker client 没有做 TLS握手
-
经过排查,是有同事在升级的过程中把 docker client 的密钥归档到另外一个目录,这边 docker client 没有检测到密钥也不报错,直接用 HTTP 访问,服务端 docker daemon 拒绝访问。
总结
官方的 Docker client 终究是个黑盒,出了问题也不要慌,大多数开源的工具都会好好打日志,根据异常栈,一个个打开 DEBUG 级别日志看,总能找到线索。
不得不说 resteasy 健壮性实属一般,虽然是 jboss出品,但依赖一上来就好几个冲突,于是当初文件网关选型的时候选了 jersey。
山穷水尽的时候再抓包吧,但不是每个运维都会这般配合就是了。