写点什么

Docker 网络原理分析

作者:kof11321
  • 2022 年 3 月 04 日
  • 本文字数:2169 字

    阅读完需:约 7 分钟

我们在 docker 网络中,经常会遇到各种各样的网络问题:

  1. 为什么我的网络不能 ping 通外网,怎么排查

  2. docker 启动的--icc 标志,又是如何实现容器之间不能互相访问的

原理分析

首先,docker 容器的各种隔离,依赖于各种 namespace。而网络的隔离,则是由 linux network namespace 和 netfilter 共同完成的。

当我们启动一个容器后,容器的 1 号进程(init 进程)就会被放到对应的独立的 namespace 下,后续由 1 号进程创建的其他子进程,也就自然而然的归属于相同的 namespace 了

# 怎么知道docker容器属于那个进程docker inspect '--format={{ .State.Pid }}' 856a8f0529d0 #容器ID20998 #容器的process id, 也就是容器的namespace了将pid加入到环境变化,方便后续执行命令pid=$(docker inspect '--format={{ .State.Pid }}' 856a8f0529d0 )
复制代码

第一步:找到容器的 namespace

默认 Docker 创建的网络 namespace 不在默认路径下 ,ip netns 看不到,所以需要 ln 软链接一下。链接完毕以后,我们就可以通过 ip netns 命令操作了。

# 如果没有/var/run/netns 目录,手动创建一个 mkdir /var/run/netnsrm -f /var/run/netns/20998ln -s /proc/20998/ns/net /var/run/netns/20998
# 此命令等价于在容器内执行ip addr命令,即docker exec -it 856a8f0529d0 ip addr# 区别:在host主机执行的命令,可以使用主机的各种工具命令,例如tcpdump命令,因为某些容器由于比较轻量级,未安装各种调试工具ip netns exec 20998 ip addr
复制代码

第二步:容器发包怎么发

先来看看容器的网络拓扑

要研究网络包怎么发出的,需要在四个检查点进行 tcpdump 分析

例如在容器内进行 ping 操作

ip netns exec $pid ping 39.106.233.176

数据从容器发起:容器的 vth0 网卡

先来看看容器内网卡的配置,ip 为 172.17.0.4,默认网关 172.17.0.1。从 ip addr 命令的输出可以看到,配对的网卡是 if515(eth0@if515 后的 @if515 表示配对的网卡)

ip netns exec $pid ip route #容器内默认网络路由,可以看到网络包默认从172.17.0.1出去default via 172.17.0.1 dev eth0172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.4
ip netns exec $pid ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever514: eth0@if515: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever
复制代码

tcpdump 命令执行后保存抓包结果

ip netns exec $pid tcpdump -i eth0 host 39.106.233.176 -w /root/veth0.pcap
复制代码

wireshark 查看结果,source mac 为 docker eth0 的 mac,目标 mac 为 docker0 的 mac。source ip 为容器 ip,目标 ip 为外网 ip

数据到达:eth0 的配对网卡 veth1

先来看看 veth1 的配置

# 该网卡mac为36:e7:fa:79:ce:30,无IP,挂载到docker0这个bridge上(master docker0)515: veth4ae7c44@if514: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 36:e7:fa:79:ce:30 brd ff:ff:ff:ff:ff:ff link-netnsid 2
inet6 fe80::34e7:faff:fe79:ce30/64 scope link
valid_lft forever preferred_lft foreve
复制代码

抓包结果 tcpdump -i veth4ae7c44 host 39.106.233.176 -w pair.pcap

可以观察到,该数据报与容器内的数据报完全一致,数据报的 mac 地址,并没有换成配对的 515 网卡的 mac 地址。

总结:配对网卡,仅仅是起到了数据摆渡的功能,盲目的将数据转发出去。为什么需要这一步?因为容器内的数据发送,是在容器网络空间,无法发送到另一个网络空间。而配对网卡在主机的 namespace 下,可以将数据发送到 docker0 这个 bridge 下

数据到达 docker0

linux 上的 bridge,图中画的是 br0,实际上应该是 docker0 这个 bridge。

先来看看 docker0 的配置

docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:3f:4a:a5:4e brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:3fff:fe4a:a54e/64 scope link
复制代码

抓包结果:tcpdump -i docker0 host 39.106.233.176 -w docker0.pcap

总结:数据报的目标 mac 地址是 docker0,所以 docker0 收到该数据后,进行三层路由的功能,将该数据发送到物理网卡中。linux 的 docker0 的与普通 bridge 的差别:有 IP 172.17.0.1,可以进行三层路由的功能

数据转发,iptables 发挥作用

可以看到,所有从 172.17.0.0/16 的数据报,都会被 MASQUERADE,具体被篡改成什么,由实际的物理网卡的 IP 来决定


数据到达:实际物理网卡

抓包结果:tcpdump -i ens192 host 39.106.233.176 -w ens192.pcap(虚拟机中,我的对外网卡编号是 ens12),可以看到数据的源 IP,已经变更为虚拟机的 ip,192.168.0.209,目标地址不变。source mac 为本机 ens192 的 mac 地址,目标 mac 为路由器的 mac 地址

到这里,容器的网络包,就完成了他的旅程,从实际网卡发送出去了


用户头像

kof11321

关注

还未添加个人签名 2018.08.14 加入

还未添加个人简介

评论

发布
暂无评论
Docker网络原理分析_容器_kof11321_InfoQ写作平台