在 daemon 没有主动配置 DNS 的情况下, 就是
copy 宿主机 /etc/resolv.conf
过滤 本地地址 比如 127 xx
默认情况下,endpoint 加入 default bridge 网络中, 比如 docker run --rm -it xx
当使用自定义网络时, 比如 compose 中自定义了 bridge 网络, endpoint 加入了 新的 隔离 bridge 中,,
两者 访问外部网络都依靠 主机 forward , nat
自定义网络, 可以支持容器名字,服务名字解析, 是中间实现了一个 dns
docker run --rm --network=test_cluster_default --privileged -it weibeld/ubuntu-networking /bin/bash
root@3e2e096e00c1:/# cat /etc/resolv.conf
nameserver 127.0.0.11
options timeout:1 attempts:2 ndots:0
root@3e2e096e00c1:/#
root@3e2e096e00c1:/#
root@3e2e096e00c1:/#
所以使用 自定义网络时,看到的dns地址都是 127.0.0.11
内部使用 iptables 重定向了 dns 到内部的服务上
-A OUTPUT -d 127.0.0.11/32 -j DOCKER_OUTPUT
-A POSTROUTING -d 127.0.0.11/32 -j DOCKER_POSTROUTING
-A DOCKER_OUTPUT -d 127.0.0.11/32 -p tcp -m tcp --dport 53 -j DNAT --to-destination 127.0.0.11:45981
-A DOCKER_OUTPUT -d 127.0.0.11/32 -p udp -m udp --dport 53 -j DNAT --to-destination 127.0.0.11:38095
-A DOCKER_POSTROUTING -s 127.0.0.11/32 -p tcp -m tcp --sport 45981 -j SNAT --to-source :53
-A DOCKER_POSTROUTING -s 127.0.0.11/32 -p udp -m udp --sport 38095 -j SNAT --to-source :53
内部dns 插入需要解析的内容,解析不到再 forward 到根据宿主机 /etc/resolv.conf 文件拿到的DNS地址
复制代码
这里有一个有趣的问题 "每一个容器都重定向了 DNS 的请求,实际请求都发到了 同一个 dockerd 服务, dockerd 服务在主机的 netns 下, 容器在自己的 netns 下, 数据怎么发过来的那?"
我们写一个 demo 程序,就能看明白了!
func main() {
// Lock the OS Thread so we don't accidentally switch namespaces
runtime.LockOSThread()
defer runtime.UnlockOSThread()
// Save the current network namespace
origns, _ := netns.Get()
defer origns.Close()
// Create a new network namespace
newns, _ := netns.GetFromName("testns2")
netns.Set(newns)
defer newns.Close()
fmt.Println("enter ns testns2")
// Do something with the network namespace
ifaces, e := net.Interfaces()
if e != nil {
fmt.Printf("get Interfaces error : %v\n", e)
}
fmt.Printf("Interfaces: %v\n", ifaces)
ln, err := net.Listen("tcp", ":9090")
if err != nil {
fmt.Printf("listen in ns error, %v", err)
return
}
// Switch back to the original namespace
netns.Set(origns)
fmt.Println("listen in original ns")
for {
conn, e := ln.Accept()
if e != nil {
continue
}
go handleConn(conn)
}
}
复制代码
在主机上,运行该程序, 我们查看 testns2 下的监听情况, 如下, 能正常看到监听的 socket, 连接也是正常的, (其实主机 ns 下是看不到的)
ip netns exec testns2 netstat -anp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp6 0 0 :::9090 :::* LISTEN 27417/main
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags Type State I-Node PID/Program name Path
ip netns exec testns2 telnet 127.0.0.1 9090
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
a
a
b
b
^]
复制代码
hack 点就是 socket listen,accept 在不通的 ns 下处理 ,这样就绕过了服务网络上的问题, docker 当中也是这么处理的
评论