天天看點

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),如果運作在其它地方還不一定能知道此問題,是以說失敗乃是成功之母,隻要踩過坑,并認真找原因,解決問題,我相信你一定能有收貨