go程序在容器中域名解析的困惑

在容器中碰到一个怪的现象,一个域名配置了host解析,但是程序调用中,tcp连接成了其它IP


看如下操作: 通过nslookup解析域名 ​test.datakit.com 返回的IP不是hosts文件中配置的IP

# nslookup test.datakit.com
Server:     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中配置的

go程序在容器中域名解析的困惑

curl 抓的包,直接就是IP了,IP是在/etc/hosts中配置的

go程序在容器中域名解析的困惑

程序(程序部署在容器中,映射出端口18090,容器内部端口18080,在宿主机*问程序 http://ip:18090,然后程序调用test.datakit.com

看抓包该IP不是hosts解析配置的,而是通过DNS解析的(非权威应答)

go程序在容器中域名解析的困惑


我怀疑程序是不是有问题,因为程序使用了第三方的包,我来个最简单的demo看看

package main

import (
   "fmt"
   "io/ioutil"
   "net/http"
)
func main()  {
   c:=http.Client{}
   req,err:=http.NewRequest("GET","http://test.datakit.com:9529/stats",nil)
   if err!=nil{
      fmt.Println(err)
   }
   resp,err:=c.Do(req)
   if err!=nil{
      fmt.Println(err)
   }
   data,_:=ioutil.ReadAll(resp.Body)
   fmt.Println(string(data))
}

编译后执行,同样的是,tcp连接的IP不是我hosts配置文件中的,那么显然问题与这个程序有关系了

go程序在容器中域名解析的困惑

抓包中显示,query 查了A记录,显示经过了DNS,这下搞懂了原因了,只是不明白为啥go程序会先查询下DNS

go程序在容器中域名解析的困惑


最后在网上找到了答案,go默认使用纯go的域名解析,还有一种cgo的方式

可以临时设置下环境变量尝试一下执行

bash-4.4# export GODEBUG=netdns=cgo
bash-4.4# ./demo
{
    "inputs_status": [
        {
            ...


为什么go程序默认使用纯go的域名解析呢,因为当DNS解析阻塞时,内置Go解析器只是阻塞了一个goroutine,而cgo的解析器则是阻塞了一个操作系统级别的线程.


这也是巧合,把程序运行在容器镜像bash4.4(没有/etc/nsswitch.conf),如果运行在其它地方还不一定能知道此问题,所以说失败乃是成功之母,只要踩过坑,并认真找原因,解决问题,我相信你一定能有收货

上一篇:c语言基本函数


下一篇:以应用为中心的微服务PaaS平台---企业级分布式应用服务 EDAS