閱讀對象
假設閱讀者了解 docker,docker-compose以及 go 的語法
問題描述
我有三個應用分別叫做mysql逢并,goApp,javaApp。 他們的依賴關系如下圖所示:
goApp 通過調用 javaApp 的服務完成邏輯立肘。
javaApp 直接和 mysql 數(shù)據(jù)庫打交道。
為了讓他們三個很容易的在 docker 容器里跑起來我使用了 docker-compose名扛。具體的配置文件如下:
version: '2'
services:
mysql:
container_name: mysql
image: mysql:5.7
restart: always
hostname: mysql
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
goApp:
container_name: goApp
image: goApp:0.1
hostname: goApp
ports:
- "8080:8080"
javaApp:
container_name: javaApp
image: javaApp:0.1
hostname: javaApp
ports:
- "6031:6031"
這個結構很順暢的跑起來了谅年,而且一直很穩(wěn)定。突然有一天肮韧,我給我的服務器修改了一個 hostname 修改為 app.crop.cn融蹂,然后就跑不起來了旺订。具體的現(xiàn)象如下:
javaApp 的程序能夠正常訪問。能夠訪問到mysql 數(shù)據(jù)庫超燃。
goApp 無法通過 javaApp 訪問到 javaApp 這個應用区拳。
進入 javaApp和 goApp 這個容器,能夠互相 ping 通意乓。
問題分析
現(xiàn)象中有兩個關鍵點:
在容器里樱调,所有 ping 都是能互通的。說明 docker 內部的 dns 是能工作的届良。
只有 go 的程序不能根據(jù) hostname 找到對應的容器笆凌。
根據(jù)第二疑點猜測:go 語言的 dns 解析機制和java 的不一樣。沿著這條路我我找到了士葫,最后在官方文檔中找到如下內容乞而。
On Unix systems, the resolver has two options for resolving names.
It can use a pure Go resolver that sends DNS requests directly to
the servers listed in /etc/resolv.conf, or it can use a cgo-based
resolver that calls C library routines such as getaddrinfo and
getnameinfo.
也就是說 go 自己實現(xiàn)了一套請求 dns 解析的方法。其他程序應該使用基礎的c 標準庫getaddrinfo. 所以他們的表現(xiàn)不一樣慢显。還說啥啊爪模,去看代碼吧。在dnsconfig_unix.go 文件中找到了實現(xiàn)荚藻。
func dnsDefaultSearch() []string {
hn, err := getHostname()
if err != nil {
// best effort
return nil
}
if i := byteIndex(hn, '.'); i >= 0 && i < len(hn)-1 {
return []string{ensureRooted(hn[i+1:])}
}
return nil
}
func ensureRooted(s string) string {
if len(s) > 0 && s[len(s)-1] == '.' {
return s
}
return s + "."
}
通過代碼可以看出屋灌,默認的 Search 是獲取的當前主機的 hostname,第一個點(“.”) 后面部分的內容应狱。例如 我上面把主機名修改成了 app.crop.cn, 那么他獲取到的默認的 search 就是 crop.cn. 了声滥。所以當我請求 javaApp 的時候,他會像 docker 內建的 dns 請求 javaApp.crop.cn 這個域名侦香。當然就請求不到了落塑。
問題解決方案
找到原因以后下面就比較簡單了。只需要在啟動 容器的是時候設定上 把dns-search 這個參數(shù)設定成 “.”就可以了罐韩。
最后修改 docker-compose.yml 為:
version: '2'
services:
mysql:
container_name: mysql
image: mysql:5.7
restart: always
hostname: mysql
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
goApp:
container_name: goApp
image: goApp:0.1
dns-search: .
hostname: goApp
ports:
- "8080:8080"
javaApp:
container_name: javaApp
image: javaApp:0.1
hostname: javaApp
ports:
- "6031:6031"
就這樣憾赁,這個看上去奇葩的問題被搞定了。