Docker 嵌入式 DNS 解析器如何工作?

问题描述 投票:0回答:2

我知道 Docker 有一个嵌入式 Dns 解析器。
当我在自己的桥中运行容器时:

$ docker run -it --rm --privileged --network=mybridge xxx bash

root@18243bfe6b50:/# cat /etc/resolv.conf  
nameserver 127.0.0.11  
options ndots:0  

root@18243bfe6b50:/# netstat -anop  
Active Internet connections (servers and established)  
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name Timer  
tcp        0      0 127.0.0.11:45997        0.0.0.0:*               LISTEN      -                off (0.00/0/0)  
udp        0      0 127.0.0.11:49614        0.0.0.0:*        

it shows there is a dns resolver, and iptables help do a port transfer.  

root@18243bfe6b50:/# iptables -nvL -t nat  
.....  
Chain DOCKER_OUTPUT (1 references)  
 pkts bytes target     prot opt in     out     source               destination  
    0     0 DNAT       tcp  --  *      *       0.0.0.0/0            127.0.0.11           tcp dpt:53 to:127.0.0.11:45997  
    0     0 DNAT       udp  --  *      *       0.0.0.0/0            127.0.0.11           udp dpt:53 to:127.0.0.11:49614  

Chain DOCKER_POSTROUTING (1 references)  
 pkts bytes target     prot opt in     out     source               destination  
    0     0 SNAT       tcp  --  *      *       127.0.0.11           0.0.0.0/0            tcp spt:45997 to::53  
    0     0 SNAT       udp  --  *      *       127.0.0.11           0.0.0.0/0            udp spt:49614 to::53  

but, which process is the dns resolver? I guess it is dockerd?  but dockerd is running in host network namespace, obviously it is different with the container network namespace, also, I can not find dockerd has dns port listening in host:  

root@test:~# netstat -tnop |grep dockerd  
tcp        0      0 10.5.79.50:59540        10.5.79.50:2377         ESTABLISHED 3332/dockerd     off (0.00/0/0)  
tcp        0      0 127.0.0.1:35792         127.0.0.1:2377          ESTABLISHED 3332/dockerd     off (0.00/0/0)  
tcp6       0      0 10.5.79.50:2377         10.5.79.70:45934        ESTABLISHED 3332/dockerd     off (0.00/0/0)  
tcp6       0      0 127.0.0.1:2377          127.0.0.1:35792         ESTABLISHED 3332/dockerd     off (0.00/0/0)  
tcp6       0      0 10.5.79.50:2377         10.5.79.50:59540        ESTABLISHED 3332/dockerd     off (0.00/0/0)  

一个进程(dockerd)如何暴露主机命名空间中的一些端口和其他命名空间(容器)中的一些端口?我读了一些代码,但仍然无法弄清楚,有人可以帮忙解答吗?

谢谢。

docker namespaces
2个回答
45
投票

也许您已经发现 Docker(又名 Moby)内部使用

libnetwork
来配置和启用嵌入式 DNS 解析器。 Libnetwork 将解析器绑定到容器的环回接口,以便可以将
127.0.0.11
处的 DNS 查询路由(通过 iptables)到 Docker 引擎中的“后端 DNS 解析器”。请参阅 libnetwork 类型 和实际的 ResolveName() 代码。每个容器的沙箱允许通过网络命名空间路由 DNS 查询。

关于一个进程如何公开主机上和容器内部的端口的问题:在这种情况下,将处理程序线程绑定到接口将是一个更合适的表达。 Docker引擎创建一个容器并配置其网络命名空间,因此它还可以通过iptables配置容器的网络接口和数据包路由。将解析器绑定到容器的内部接口是您在主机上找不到任何监听端口 53 的进程的原因。


5
投票
谢谢@gesellix

Docker 是做什么的:

    嵌入式 DNS 解析器 (dockerd) 将网络 NS 切换到容器(使用
  1. setns
     系统调用)
  2. 监听 127.0.0.11:xxxx,并返回其 Origin net NS。
  3. 使用iptables DNAT和SNAT规则,将端口53转发到xxxxx。
  4. 使用
  5. /etc/resolv.conf
    nameserver 127.0.0.11
    
    

我猜是因为dockerd不在容器的pid_namespace中,所以你在容器中看不到PID/程序名称。

查看 dockerd 监听 127.0.0.11:

    获取容器ID:
  1. docker ps
    
    
  2. 获取容器PID:
  3. docker inspect --format "{{.State.Pid}}" 89c249fc51f6
    
    
  4. 在容器网络 NS 中运行命令:
  5. nsenter -n -t 3794 ss -lnutp
    (或者:
    nsenter -n -t 3794 netstat -anop
你会看到类似的东西:

Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port udp UNCONN 3072 0 127.0.0.11:43418 0.0.0.0:* users:(("dockerd",pid=1348,fd=63)) tcp LISTEN 0 128 127.0.0.11:34643 0.0.0.0:* users:(("dockerd",pid=1348,fd=65))

    查看 iptables 规则:
  1. nsenter -n -t 3794 iptables -nvL -t nat
    
    
(在某些 Linux 版本中,使用

iptables-legacy

 代替 
iptables

Chain DOCKER_OUTPUT (1 references) pkts bytes target prot opt in out source destination 0 0 DNAT tcp -- * * 0.0.0.0/0 127.0.0.11 tcp dpt:53 to:127.0.0.11:34643 4 242 DNAT udp -- * * 0.0.0.0/0 127.0.0.11 udp dpt:53 to:127.0.0.11:43418 Chain DOCKER_POSTROUTING (1 references) pkts bytes target prot opt in out source destination 0 0 SNAT tcp -- * * 127.0.0.11 0.0.0.0/0 tcp spt:34643 to::53 0 0 SNAT udp -- * * 127.0.0.11 0.0.0.0/0 udp spt:43418 to::53


关于libnetwork代码:

解析器中

// SetupFunc() provides the setup function that should be run // in the container's network namespace.
在此处

调用 InvokeFunc if err := sb.osSbox.InvokeFunc(sb.resolver.SetupFunc(0));

def 
InvokeFunc

func (n *networkNamespace) InvokeFunc(f func()) error { return nsInvoke(n.nsPath(), func(nsFD int) error { return nil }, func(callerFD int) error { f() return nil }) }

nsInvoke

你会看到 netns.Set ... netns.GetFromPath(path) ... netns.Set(newNs) ...

前往
https://github.com/vishvananda/netns

查看功能详情

© www.soinside.com 2019 - 2024. All rights reserved.