Docker中如何让*.localhost指向同网络容器而非127.0.0.1
我在Docker环境中使用*.localhost格式域名(比如front.localhost、sdk.localhost),这类域名会被浏览器自动指向127.0.0.1,不用手动修改hosts。但遇到了麻烦:前端应用(Next/Nuxt)部署在front.localhost,需要同时从浏览器(正常工作)和容器内部调用sdk.localhost,但容器内调用时,sdk.localhost会被解析为127.0.0.1,根本连不上目标容器。
要求浏览器和后端用同一套API代码,不能换内部主机名。试过别名、external_links、多网络、Traefik反向代理,都没解决问题。
复现示例
Hans Killian提供的docker-compose.yml:
version: '3' services: frontend: image: debian command: tail -f /dev/null backend: image: debian command: tail -f /dev/null networks: default: aliases: - backend.localhost
在frontend容器内执行命令的结果:
ping解析正常
root@1aa125e575eb:/# ping backend.localhost PING backend.localhost (172.18.0.3) 56(84) bytes of data.
curl解析错误
root@1aa125e575eb:/# curl -v backend.localhost * Trying 127.0.0.1:80... * connect to 127.0.0.1 port 80 failed: Connection refused * Trying [::1]:80... * Immediate connect fail for ::1: Cannot assign requested address * Failed to connect to backend.localhost port 80 after 0 ms: Couldn't connect to server * Closing connection 0 curl: (7) Failed to connect to backend.localhost port 80 after 0 ms: Couldn't connect to server
问题原因
这是因为基于glibc的系统(比如Debian)有个硬编码规则:所有.localhost后缀的域名会被强制解析到127.0.0.1,优先级高于DNS解析。虽然ping会用Docker DNS解析到容器IP,但curl这类调用getaddrinfo的工具会触发glibc的这个规则,直接返回127.0.0.1。
解决方法
方法1:调整容器内的DNS解析顺序
修改容器的/etc/nsswitch.conf,让DNS解析优先于本地规则:
echo 'hosts: dns files myhostname' > /etc/nsswitch.conf
可以直接写到docker-compose.yml的启动命令里:
services: frontend: image: debian command: > sh -c "echo 'hosts: dns files myhostname' > /etc/nsswitch.conf && tail -f /dev/null"
这样curl就会优先使用Docker DNS解析backend.localhost到对应的容器IP。
方法2:用extra_hosts强制映射域名
在前端容器的配置里添加extra_hosts,把backend.localhost直接映射到后端服务名(Docker会自动解析服务名到容器IP):
services: frontend: image: debian command: tail -f /dev/null extra_hosts: - "backend.localhost:backend"
这个方法绕过了glibc的.localhost解析规则,直接把域名指向后端容器。
方法3:自定义DNS服务器(适合多域名场景)
如果有大量.localhost域名需要处理,可以搭建一个自定义DNS服务(比如dnsmasq),让它优先解析Docker内部的.localhost域名,其他未匹配的.localhost再指向127.0.0.1。不过这个方案配置稍复杂,适合大规模部署场景。
内容的提问来源于stack exchange,提问作者Fabrice Reynaud

