net.LookupHost在内网域名下总返回空切片,因其仅查A记录且完全依赖系统resolver;若/etc/resolv.conf配置错误、容器镜像(如Alpine)跳过hosts或未指定内网DNS服务器,便会返回"nil error + 空切片"或"net.DNSError: no such host"。

net.LookupHost 在内网域名下为什么总返回空切片
因为 net.LookupHost 默认只查 A 记录,且完全依赖系统 resolver(/etc/resolv.conf 或 Windows DNS API),而多数内网域名(如 svc.cluster.local、redis.prod.internal)靠的是私有 DNS 服务器或 CoreDNS,不是公网递归 DNS。一旦容器里没配好 resolv.conf,或者用的是 Alpine/musl 镜像(跳过 hosts 文件),它就直接报 "no such host",连错误细节都藏在 *net.DNSError 里。
- 必须同时检查
err != nil和len(ips) == 0:前者是网络/配置失败,后者才是真没记录 - 内网域名常带自定义后缀(如
search prod.internal),但net.LookupHost不做搜索列表扩展,得手动拼全名再查 - 本地
/etc/hosts在某些镜像(如 distroless、Alpine)中不生效,别假设它会 fallback
用 net.Resolver 指定内网 DNS 服务器才可靠
想让 Go 程序稳定解析 api.vpc.internal 这类地址,不能靠系统配置,得显式构造 *net.Resolver 并指向你的内网 DNS(比如 CoreDNS 的 10.96.0.10:53)。
-
PreferGo: true必须设,否则仍走系统 resolver,自定义Dial就白写了 -
Dial函数必须返回net.Conn,不能传字符串地址;UDP 示例:func(ctx context.Context, _, _ string) (net.Conn, error) { return (&net.Dialer{Timeout: 2*time.Second}).DialContext(ctx, "udp", "10.96.0.10:53") } - 别漏掉 context 超时:
resolver.LookupIP(ctx, "ip", domain),否则 UDP 丢包时 goroutine 卡死几十秒 - CoreDNS 默认监听
:53,但 Kubernetes 中的10.96.0.10可能只响应集群内请求,确保网络策略放行
查内网服务要区分 IPv4/IPv6 和记录类型
内网环境常见双栈部署(比如 Istio sidecar 同时开 v4/v6),而 net.LookupHost 只返 IPv4 字符串,net.LookupIP 虽返两者但不带协议族信息——容易导致 net.Dial("tcp", ip.String()+":80") 在 IPv6 地址上拼出 [2001:db8::1]:80 这种非法格式。
- 优先用
resolver.LookupIPAddr(ctx, domain),它返回*net.IPAddr,含IP和Zone,可直接喂给net.Dial - 查服务发现类记录(如 SRV、TXT)必须用专用方法:
resolver.LookupSRV(ctx, "https", "tcp", domain),net.LookupIP完全不支持 - Kubernetes Headless Service 场景下,用
r.LookupIP(ctx, "ip", domain)拿全量 Pod IP;传"ip4"会漏 IPv6,传"ip"才安全
HTTP 客户端走内网 DNS 的坑
很多人以为改了 net.DefaultResolver 就能让 http.Client 自动走内网 DNS,其实不行——http.DefaultClient 完全不认这个变量,它底层用的是 net.Dialer 的默认行为。
立即学习“go语言免费学习笔记(深入)”;
- 必须自己构造
http.Transport,并在DialContext里调用你自己的resolver.LookupHost或LookupIPAddr - 别只写
Dial(旧签名),必须用DialContext,否则 TLS 握手阶段仍走系统 DNS - 若服务启用了 SNI,记得把原始域名传进
TLSConfig.ServerName,否则证书校验失败(IP 直连不带域名) - 内网 DNS 响应快,但可能没 TTL 缓存,
Transport.IdleConnTimeout设太短会导致频繁重查,建议配合自定义 resolver 做内存缓存
"no such host" 可能是域名真不存在、CoreDNS 没配转发、UDP 被防火墙拦截、甚至 resolv.conf 里写了 127.0.0.11 但宿主机没跑 Docker DNS。调试时先用 dig @10.96.0.10 api.vpc.internal 确认 DNS 层通不通,再看 Go 里 err.(*net.DNSError).IsNotFound() 和 .IsTemporary() 的返回值。


















