在容器化的世界里,网络通信是核心,而域名系统(DNS)则是这座通信大厦的基石,Docker作为容器技术的领导者,其内置的DNS机制设计得既巧妙又强大,它使得容器之间可以通过服务名称进行透明通信,极大地简化了微服务架构的部署与管理,当网络问题出现时,DNS也常常是第一个被怀疑的对象,深入理解Docker的DNS工作原理、配置方式和排错技巧,对于每一位Docker使用者来说都至关重要。

核心机制:嵌入式DNS服务器
现代版本的Docker引入了一个革命性的特性:嵌入式DNS服务器,当您创建一个自定义网络并启动容器时,Docker会为每个容器提供一个内置的DNS解析器,其地址固定为0.0.11,这个地址会自动写入容器的/etc/resolv.conf文件中。
这个嵌入式DNS服务器扮演着双重角色:
- 内部服务发现:当容器尝试解析同一网络中另一个容器的名称时(一个名为
web-server的容器尝试连接database容器),该请求会被0.0.11直接拦截,Docker守护进程维护着一份网络内容器名称与其IP地址的映射表,因此它能立即返回正确的IP地址,实现了高效的内部服务发现。 - 外部域名转发:如果容器请求解析一个外部域名(如
www.google.com),嵌入式DNS服务器自身无法解析,它会扮演一个转发器的角色,将查询请求转发给上游的DNS服务器,这个上游服务器默认是宿主机的DNS配置,但也可以被自定义。 
这种设计巧妙地将容器间的通信与外部世界隔离开来,内部通信快速且自包含,外部通信则遵循传统的DNS解析路径。
DNS配置的层次结构
Docker的DNS配置遵循一个清晰的优先级层次,从高到低依次为:
| 配置级别 | 配置方法 | 描述 | 
|---|---|---|
| 容器级 | docker run --dns=8.8.8.8 ... | 
在启动容器时通过--dns标志直接指定DNS服务器,此配置拥有最高优先级,会覆盖所有其他设置。 | 
| 网络级 | docker network create --opt="com.docker.network.bridge.name=br0" ... | 
创建网络时可以设置特定选项,虽然不直接设置DNS,但网络的隔离性决定了DNS解析的范围。 | 
| 守护进程级 | /etc/docker/daemon.json | 
在Docker守护进程的配置文件中设置全局默认DNS,所有未指定--dns的容器都会继承此配置。 | 
| 宿主机继承 | 宿主机的/etc/resolv.conf | 
如果以上三级均未配置,Docker会从宿主机复制/etc/resolv.conf到容器中,这是默认的后备行为。 | 
在/etc/docker/daemon.json中,可以这样配置全局DNS:
{
  "dns": ["8.8.8.8", "114.114.114.114"],
  "dns-search": ["example.com"]
}
这里的dns数组指定了上游DNS服务器,dns-search则设置了域名搜索后缀,使得容器在解析host时会自动尝试host.example.com。

不同网络模式下的DNS行为
Docker的网络模式直接影响DNS的工作方式。
- Bridge模式(默认):这是嵌入式DNS服务器大显身手的主要场景,在默认的
bridge网络中,容器之间无法通过名称通信,但一旦创建自定义bridge网络并将容器连接起来,它们就可以通过容器名进行优雅的发现和通信。 - Host模式:使用
--net=host启动的容器将共享宿主机的网络命名空间,它不会获得0.0.11这个嵌入式DNS服务器,而是直接使用宿主机的/etc/resolv.conf,这意味着容器的DNS行为与宿主机完全一致。 - Overlay网络(Docker Swarm):在Docker Swarm集群中,Overlay网络实现了跨多个主机的容器通信,其DNS机制更为强大,它不仅能在整个集群内解析服务名称,还能实现负载均衡,当您ping一个服务名(如
my-web-app)时,DNS会返回该服务所有健康任务(容器)的虚拟IP(VIP),后续的请求会被透明地负载均衡到后端的某个容器上。 
Docker Compose中的DNS实践
Docker Compose极大地简化了多容器应用的编排,在docker-compose.yml文件中定义的服务,默认情况下会被放置于一个自定义网络中,这意味着,服务名就是主机名。
考虑以下简单的docker-compose.yml:
version: '3.8'
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    depends_on:
      - api
  api:
    image: nginx:alpine # 假设这是一个API服务镜像
在这个配置中,web服务可以直接通过http://api:80来访问api服务,无需关心其具体的IP地址,这就是Docker Compose利用内置DNS机制实现的便捷服务发现,Docker Compose会自动创建一个网络,并将web和api两个服务容器加入其中,同时将服务名注册到嵌入式DNS服务器。
小编总结与排错
理解Docker DNS的层层机制是高效排查网络问题的关键,当遇到“unknown host”或“name resolution failed”等错误时,可以遵循以下思路:
- 检查网络:确认两个需要通信的容器是否在同一个自定义网络中。
 - 检查
/etc/resolv.conf:进入容器内部(docker exec -it <container> sh),查看/etc/resolv.conf,确认DNS服务器指向是否正确(通常是0.0.11)。 - 检查上游DNS:如果外部域名无法解析,检查宿主机的DNS设置或Docker守护进程的
daemon.json配置。 - 使用
nslookup或dig:在容器内安装dnsutils或bind-tools,使用这些工具手动测试DNS解析,可以更清晰地看到解析路径和结果。 
Docker的DNS系统是一个精心设计的、自洽的生态系统,它将复杂的网络发现问题抽象为简单的服务名调用,掌握其内在原理,不仅能让我们在日常开发中事半功倍,更能在面对棘手的网络问题时,做到心中有数,手中有策。

相关问答FAQs
Q1: 我如何检查一个正在运行的容器具体使用了哪个DNS服务器?
A: 您可以通过docker exec命令进入容器并查看其/etc/resolv.conf文件,执行命令 docker exec <容器ID或名称> cat /etc/resolv.conf,如果输出中包含 nameserver 127.0.0.11,那么该容器正在使用Docker的嵌入式DNS服务器,如果显示的是其他IP地址(如8.8.8或您宿主机的DNS),则说明它直接使用了配置的上游DNS服务器,这通常是因为您在启动时使用了--dns参数,或者容器运行在host网络模式下。
Q2: Docker的嵌入式DNS服务器(127.0.0.11)和我宿主机上的DNS服务器有什么区别和联系?
A: 它们是协作关系,而非替代关系,嵌入式DNS服务器(127.0.0.11)是一个专门为容器环境设计的“智能代理”,它的主要区别和联系在于:
- 职责不同:嵌入式DNS服务器的首要职责是处理内部服务发现,即解析同一Docker网络中其他容器的名称,而宿主机DNS负责解析外部互联网域名。
 - 转发机制:当嵌入式DNS服务器收到一个它无法识别的域名查询(即外部域名)时,它会将这个请求转发给其上游DNS服务器,这个上游服务器默认就是宿主机配置的DNS服务器。
 - 隔离与效率:通过这种代理模式,Docker实现了内部通信的隔离和高效率,同时无缝地复用了宿主机的外部解析能力,容器无需知道宿主机的DNS配置,只需与
0.0.11交互即可。