在最近的一次部署中,我遇到一個奇怪的問題:Go 程序在運行時不使用 /etc/resolv.conf
中的 DNS 設置,導致服務無法正常訪問域名。這篇文章記錄下完整的排查過程和最終的解決方案。
1. 問題現象
我有一個部署在 KVM 虛擬機內的 Go 應用,主要功能依賴某些內網域名解析(如 api.service.internal
)。而在主機中使用 dig
和 curl
等命令都能正常解析,但 Go 程序卻報錯類似如下:
lookup api.service.internal on 127.0.0.53:53: server misbehaving
查看主機resolv.conf
文件內容如下:
$ cat /etc/resolv.conf
options timeout:1 rotate
search dns.koudai.com host.idcvdian.com
nameserver 10.27.0.84
nameserver 10.27.0.126
這些配置本應該能保證正常解析才對。
2. 初步懷疑:Go DNS 解析邏輯
Go 的 DNS 默認行為與傳統 C 庫不同。Go 在構建時是否啟用 CGO
會直接影響其 DNS 解析邏輯:
構建方式 | DNS 實現 | 使用 /etc/resolv.conf |
---|---|---|
CGO_ENABLED=0 | Go 自帶 DNS 實現 | 只使用 127.0.0.1、Google DNS 等默認配置 |
CGO_ENABLED=1 | 調用系統 libc | 遵循 /etc/nsswitch.conf 和 resolv.conf |
Go 默認靜態構建時不會使用系統的 resolv.conf
進行 DNS 解析,而是使用 Go 內置的 DNS Resolver。
3. 構建方式調整
為了讓 Go 程序在運行時讀取主機的 /etc/resolv.conf
并使用正確的 DNS,我嘗試啟用 CGO:
CGO_ENABLED=1 go build -o myapp main.go
注意,啟用 CGO_ENABLED=1
后會變成動態鏈接,依賴 libc 等系統庫。此時執行 ldd
命令應能看到依賴:
$ ldd ./myapplinux-vdso.so.1 => (0x00007ffc6fdff000)libc.so.6 => /lib64/libc.so.6 (0x00007f624af5a000)...
而如果是靜態構建(CGO_ENABLED=0
),ldd
則會提示:
$ ldd ./myappnot a dynamic executable
4. 報錯:找不到動態庫
由于我的 KVM 虛擬機是一個極簡化系統,嘗試 CGO_ENABLED=1
構建后,運行程序提示找不到 libc.so.6
等依賴。解決方案:
sudo yum groupinstall "Development Tools"
sudo yum install glibc-devel
安裝過程較大(可能需要幾百 MB),可以只安裝gcc相關
sudo yum install -y gcc glibc-devel
5. 最終構建命令模板
CGO_ENABLED=1 \
GOOS=linux GOARCH=amd64 \
go build -o ./bin/myapp main.go
6. 附加:檢測是否啟用 CGO
go env CGO_ENABLED
輸出 1
表示啟用了 CGO