在容器中碰到一个怪的现象,一个域名配置了host解析,但是程序调用中,tcp连接成了其它IP
看如下操作: 通过nslookup解析域名 test.datakit.com
返回的IP不是hosts文件中配置的IP
# nslookup test.datakit.comServer: 127.0.0.11 Address: 127.0.0.11:53 Non-authoritative answer: test.datakit.com canonical name = server.datakit.com Non-authoritative answer: test.datakit.com canonical name = server.datakit.com Name: server.datakit.com Address: 46.105.51.17
# cat /etc/hosts 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 172.16.5.9 test.datakit.com 172.26.0.14 7707e6a4e86f
通过docker logs 获取容器中应用的日志,发现请求该域名,最后连接的IP也不是hosts文件中的IP,而是DNS解析的IP
Datadog Tracer v1.29.0 ERROR: lost 1 traces: Post "http://test.datakit.com:9529/v0.4/traces": dial tcp 46.105.51.17:9529: i/o timeout (occurred: 29 Mar 21 02:32 UTC) Datadog Tracer v1.29.0 ERROR: lost 1 traces: Post "http://test.datakit.com:9529/v0.4/traces": dial tcp 46.105.51.17:9529: i/o timeout (occurred: 29 Mar 21 02:33 UTC)
遇到的问题:
问题1: 为什么host配置了解析,不使用,这个不是优先使用吗
是因为这个容器镜像bash:4.4,没有/etc/nsswitch.conf 解析顺序文件,导致没有优先使用host解析,而使用了dns
问题2: 解析test.datakit.com,为什么返回个server.datakit.com的IP
dnslookup返回了非权威的应答,是一个缓存中的结果,所以自己随便定义域名,也不能和其它能解析的域名相关联,例如: datakit.com 顶级域名,你随便查看其它的a.datakit.com,b.datakit.com 也是这样的结果
解决此问题:
方法一:增加/etc/nsswitch.conf解析顺序文件,从其它系统拷贝到容器中/etc/目录下面
docker cp /etc/nsswitch.conf b753da9b566e:/etc/
方法二:更换域名绑定,如果dns解析失败,那么就会走host解析
# nslookup test.jiangyd.com Server: 127.0.0.11 Address: 127.0.0.11:53 ** server can't find test.jiangyd.com: NXDOMAIN ** server can't find test.jiangyd.com: NXDOMAIN # cat /etc/hosts |grep test 172.16.5.9 test.jiangyd.com
问题到这里就结束了吗,以上的问题虽然是解决,但其实还有2个疑惑不能答疑?
在使用ping命令过程中,是正确的,能匹配到/etc/hosts的配置
# ping test.datakit.com PING test.datakit.com (172.16.5.9): 56 data bytes 64 bytes from 172.16.5.9: seq=0 ttl=64 time=0.095 ms 64 bytes from 172.16.5.9: seq=1 ttl=64 time=0.076 ms
在使用curl调用过程中,是正确的,能匹配到/etc/hosts的配置
# curl http://test.datakit.com:9529/stats { "inputs_status": [ { "name": "aliyunobject", "category": "/v1/write/object", "frequency": "20.23/min", ...
难道我还是没理解?,不同的程序使用解析还不一样,ping ,curl,自己写的程序(go二进制)
怎么办,这就要放弃?,好吧,再尝试下使用tcpdump抓包看看吧
ping命令抓的包,直接就是IP了,IP是在/etc/hosts中配置的
curl 抓的包,直接就是IP了,IP是在/etc/hosts中配置的
程序(程序部署在容器中,映射出端口18090,容器内部端口18080,在宿主机上访问程序 http://ip:18090,然后程序调用test.datakit.com)
看抓包该IP不是hosts解析配置的,而是通过DNS解析的(非权威应答)
我怀疑程序是不是有问题,因为程序使用了第三方的包,我来个最简单的demo看看
packagemainimport ( "fmt""io/ioutil""net/http") funcmain() { c:=http.Client{} req,err:=http.NewRequest("GET","http://test.datakit.com:9529/stats",nil) iferr!=nil{ fmt.Println(err) } resp,err:=c.Do(req) iferr!=nil{ fmt.Println(err) } data,_:=ioutil.ReadAll(resp.Body) fmt.Println(string(data)) }
编译后执行,同样的是,tcp连接的IP不是我hosts配置文件中的,那么显然问题与这个程序有关系了
抓包中显示,query 查了A记录,显示经过了DNS,这下搞懂了原因了,只是不明白为啥go程序会先查询下DNS
最后在网上找到了答案,go默认使用纯go的域名解析,还有一种cgo的方式
可以临时设置下环境变量尝试一下执行
bash-4.4# export GODEBUG=netdns=cgobash-4.4# ./demo{ "inputs_status": [ { ...
为什么go程序默认使用纯go的域名解析呢,因为当DNS解析阻塞时,内置Go解析器只是阻塞了一个goroutine,而cgo的解析器则是阻塞了一个操作系统级别的线程.
这也是巧合,把程序运行在容器镜像bash4.4(没有/etc/nsswitch.conf),如果运行在其它地方还不一定能知道此问题,所以说失败乃是成功之母,只要踩过坑,并认真找原因,解决问题,我相信你一定能有收货