首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

容器里面请求localhost竟然不是请求127.0.0.1地址,真相是这样

问题背景

某平台的组件,在创建流水线时,一直报错,日志显示请求一个地址连接失败。

Lark20201223221441

在平台流水线创建有关的组件日志里面,发现相同的错误。

image-20201130215353485

排查

从上面日志输出,很明显是程序去请求了10.74.223.115这个地址的443端口,但是连接失败。初始判断是程序本身配置错误引起,于是检查该组件的configmap信息,grep过滤这个IP地址,一无所获。在检查组件所在的k8s集群的service的IP信息,endpoint信息,secret信息等,都没有发现任何信息和这个IP地址关联。

根据日志记录,exec到此组件的pod里面,curlhttps://localhost:443/apis/,也是正常有返回结果。产研反馈创建流水线的代码逻辑,就是请求localhost的443地址。让该组件的研发,在源代码层面查找这个IP信息,也是没有结果,排查一时没有头绪了。

没有思路情况下,因为是个内网地址,k8s集群层面没有地址记录,于是反查这个IP是不是客户环境的。通过在本地,发现了一些端倪。如下图,10.74.223.115,是有域名解析的,解析地址为localhost.cloud.xxx.com的地址,域名也有个localhost,貌似有点关联性。

于是到组件pod检查 /etc/resolv.conf文件和/etc/hosts文件。在容器里面,ping localhost是有正常响应的,在/etc/hosts文件里面,也有localhost的记录。在/etc/resolv.conf里面的search域里面,确实有cloud.xxx.com的地址。同时pod里面ping localhost.cloud.xxx.com,是能ping通的,地址解析IP正是10.74.223.115这个地址。

image-20201223223914101

image-20201223224229951

排查pod里面的search域有cloud.xxx.com地址,排查为集群的coredns开启了foward选项,宿主机的 /etc/resolv.conf是有这个search域的,因此属于正常现象。结合组件日志报错,问题变成为何组件请求localhost地址时,没有请求127.0.0.1,而是请求localhost.cloud.xxx.com地址,/etc/hosts文件配置了localhost解析,组件为何不优先使用此记录,这是linux系统默认行为。

image-20201223224841054

linux系统,/etc/nsswitch.conf 可以控制dns和hosts文件解析顺序。然后观察到有问题的 Pod 用的 alpine 镜像,试试其它镜像后发现只有基于 alpine 的镜像才会有这个问题。

再一搜发现:musl libc 并不会使用 /etc/nsswitch.conf ,也就是说 alpine 镜像并没有实现用这个文件控制域名查找优先顺序,瞥了一眼 musl libc 的 gethostbyname 和 getaddrinfo 的实现,看起来也没有读这个文件来控制查找顺序,写死了先查 hosts,没找到再查 DNS。

这么说,那还是该先查 hosts 再查 DNS 呀,为什么这里抓包看到是先查的 DNS?(如果是先查 hosts 就能命中查询,不会再发起 DNS 请求)

另外这个异常的组件用 Go 写的,会不会是 Go 程序解析域名时压根没调底层 c 库的 gethostbyname 或 getaddrinfo?

搜一下发现果然是这样:go runtime 用 Go 实现了 Glibc 的 getaddrinfo 的行为来解析域名,减少了 c 库调用(应该是考虑到减少 cgo 调用带来的的性能损耗)。issue:net: replicate DNS resolution behaviour of getaddrinfo(glibc) in the go dns resolver所以虽然 alpine 用的 musl libc 不是 Glibc,但 Go 程序解析域名还是一样走的 Glibc 的逻辑,而 alpine 没有 /etc/nsswitch.conf 文件,所以DNS解析请求高于hosts文件。

Lark20201223223222

搜索到翻源码验证下:

Unix 系的 OS 下,除了 openbsd, go runtime 会读取 /etc/nsswitch.conf (net/conf.go):

img

hostLookupOrder 函数决定域名解析顺序的策略,Linux 下,如果没有 nsswitch.conf 文件就 DNS 比 hosts 文件优先(net/conf.go):

img

可以看到 hostLookupDNSFiles 的意思是 dns first(net/dnsclient_unix.go):

img

解决

重新做镜像,在Dockerfile中增加如下命令

参考

https://github.com/golang/go/issues/22846

https://www.jianshu.com/p/c47a08b3f22e

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20201230A0I65E00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券