Bendi新闻
>
拿捏Docker 网络 bridge 驱动

拿捏Docker 网络 bridge 驱动

7月前


dokcer0 网桥

在 Ubuntn 上安装 docker 后,宿主机上默认被创建了一个名为 docker0 的网卡,其 IP 为 172.17.0.1/16:

有了这个网卡,宿主机还会在内核的路由表中添加一条到达相应网络的静态路由记录:

这条路由信息表示所有目的 IP 为 172.17.0.0/16 的数据包都会从 docker0 网卡发出。接下来我们创建一个名为 mycon 的容器,并观察其网络配置:

在 mycon 容器内可以看到两块网卡:lo 和 eth0。其中 lo 是容器的回环网卡,eth0 是容器与外界通信的网卡,eth0 的 IP 信息为 172.17.0.2/16,和宿主机上的网卡 bridge0 在同一网段中。查看 mycon 的路由信息:

mycon 容器的默认网关正是宿主机的 docker0 网卡。通过 ping 命令测试与外网的连通性,此时容器 mycon 是可以连通外网的,这就说明 mycon 的 eth0 网卡与宿主机的 docker0 网卡是连通的。

下面我们来查看宿主机的网络设备:

发现多了一个以 "veth" 开头的网卡,这是一个 veth 设备。而 veth 设备总是成对出现的,那么与 veth7537a16 配对的就应该是 mycon 容器中的 eth0 了。既然 mycon 容器中的 eth0 是与 docker0 连通的,那么 veth7537a16 也应该是与 docker0 连通的。因此 docker0 并不是一个简单的网卡设备,而是一个网桥!下图展示了 docker bridge 网络模式的拓扑图:

事实上,docker 创建了 docker0 网桥,并以 veth pair 连接各个容器的网络,容器中的数据通过 eth0 发送到 docker0 网桥上,并由 docker0 网桥完成转发。这里网桥的概念等同于交换机,为连在其上的设备转发数据帧。网桥上的 veth 网卡设备相当于交换机上的端口,可以将多个容器连接在它们上面,这些端口工作在二层,所以是不需要配置 IP 信息的。上图中的 docker0 网桥就为连在其上的容器转发数据帧,使得同一台宿主机上的 docker 容器之间可以相互通信。既然 docker0 是二层设备,那么它为什么还需要 IP 呢?其实,docker0 是一个普通的 linux 网桥,是可以为它配置 IP 的,我们可以认为它的内部有一个可以用于配置 IP 的网卡。Docker0 的 IP 地址作为所连接的容器的默认网关地址

docker0 网桥是在 docker daemon 启动时自动创建的,其默认 IP 为 172.17.0.1/16,之后通过 bridge 驱动创建的容器都会在 docker0 的子网范围内选取一个未占用的 IP 使用,并连接到 docker0 网桥上。Docker daemon 提供了如下参数可以帮助用户自定义 docker0 的设置。

  • --bip=CIDR:设置 docker0 的 IP 地址和子网范围,使用 CIDR 格式,如 192.168.1.0/24。这个参数仅仅是配置 docker0 的,对用户自定义的网桥无效。

  • --fixed-cidr=CIDR:限制 docker 容器可以获取的 IP 地址范围。Docker 容器默认可以获取的 IP 范围为 docker 网桥的整个子网范围,此参数可以将其缩小到某个子网范围内,所以这个参数必须在 docker 网桥的子网范围内。

  • --mtu=BYTES:指定 docker0 网桥的最大传输单元(MTU)。

除了使用 docker0 网桥外,用户还可以使用自定义的网桥,然后通过 --bridge=BRIDGE 参数传递给 docker daemon。比如我们可以创建一个自定义网桥 br0:

$ sudo ip link add name br0 type bridge$ sudo ifconfig br0 188.18.0.1

然后在启动 docker daemon 时设置参数 --bridge=br0 即可。

iptables 规则

在安装 docker 时,会默认在宿主机中添加一些 iptables 规则,用于 docker 容器之间已经容器与外界的通信。我们可以通过 iptables-save 命令查看到 nat 表上 POSTROUTING 链上的有这么一条规则:

-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
这条规则关系着 docker 容器与外界的通信,其含义是将不是从网卡 docker0 发出的且源地址为 172.17.0.0/16 的数据包(容器中发出的数据包)做 SNAT。这样一来,从 docker 容器中访问外网的流量,在外部看来就是从宿主机上发出的,外部感觉不到 docker 容器的存在。

当外界想要访问 docker 容器运行的服务时该怎么办呢?接下来我们将启动一个简单的 web 服务器:

$ docker run -d -p 3000:3000 --name=myweb ljfpower/nodedemo
然后观察 iptables 规则的变化:


$ sudo iptables-save*nat-A DOCKER ! -i docker0 -p tcp -m tcp --dport 3000 -j DNAT --to-destination 172.17.0.3:3000*filter-A DOCKER -d 172.17.0.3/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 3000 -j ACCEPT


可以看到,在 nat 表和 filter 表中的 DOCKER 链中分别增加来一条规则,这两条规则将访问宿主机 3000 端口的请求转发到 172.17.0.3 的 3000 端口上(提供服务的 docker 容器的 IP 和端口),所以外界访问 docker 容器是通过 iptables 做 DNAT 实现的。
Docker 默认的 forward 规则允许所有的外部 IP 访问容器,我们可以通过在 filter 的 DOCKER 链上添加规则来对外部的 IP 访问做出限制,比如只允许源 IP 为 192.168.21.212(笔者是在局域网内演示的)的数据包访问容器,添加的规则如下:

$ sudo iptables -I DOCKER -i docker0 ! -s 192.168.21.212 -j DROP
不仅仅是与外界通信,docker 容器之间相互通信也受到 iptables 规则的限制。同一台宿主机上的 docker 容器默认都连在 docker0 网桥上,它们属于同一个子网,这是满足通信的第一步。同时,docker daemon 会在 filter 表的 FORWARD 链中增加一条  ACCEPT 的规则(--icc=true):
-A FORWARD -i docker0 -o docker0 -j ACCEPT
这是满足容器间相互通信的第二步。当 docker daemon 的启动参数 -icc(icc 参数表示是否允许容器间相互通信) 设置为 false 时,上面的规则被设置为 DROP,容器间的相互通信就被禁止了,这时如果想让两个容器通信就需要在 docker run 命令中使用 --link 选项。

在 docker 容器和外界的通信过程中,还涉及了数据包在多个网卡之间的转发(比如从 docker0 网卡到宿主机 eth0 网卡),这需要内核将 ip forward 功能打开,就是把内核参数 ip_forward 设置为 1。Docker daemon 在启动的时候会执行这个操作,我们可以通过下面的命令进行检查:

$ cat /proc/sys/net/ipv4/ip_forward

返回的结果为 1,表示内核的 ip forward 功能已经打开。

容器的 DNS 和主机名(hostname)

使用同一个 docker 镜像可以创建很多个 docker 容器,但是这些容器的 hostname 并不相同,也就是说 hostname 并没有被写入到镜像中。实际上容器中的 /etc 目录下有 3 个文件是在容器启动后被虚拟文件覆盖掉的,分别是 /etc/hostname、/etc/hosts 和 /etc/resolv.conf,通过在容器中运行 mount 命令可以看到它们:

# mount/dev/mapper/ubuntu--vg-root on /etc/resolv.conf type ext4 (rw,relatime,errors=remount-ro,data=ordered)/dev/mapper/ubuntu--vg-root on /etc/hostname type ext4 (rw,relatime,errors=remount-ro,data=ordered)/dev/mapper/ubuntu--vg-root on /etc/hosts type ext4 (rw,relatime,errors=remount-ro,data=ordered)

这种方式既能解决主机名的问题,同时也能让 DNS 及时更新(改变 resolv.conf)。由于这些文件的维护方法会随着 docker 版本的升级而不断变化,所以尽量不要修改这些文件,而是通过 docker 提供的相关参数进行设置,其参数配置方式如下。

  • -h HOSTNAME 或者 --hostname=HOSTNAME:设置容器的 hostname,此名称会写入到 /etc/hostname 和 /etc/hosts 文件中,也可以在容器的 bash 提示符中看到。

  • --dns=IP_ADDRESS…:为容器配置 DNS,会被写入到 /etc/resolv.conf 文件中。

这两个参数都是针对容器的需要在创建容器时进行设置。比如下面的 demo:

$ docker run -it --name mycon -h lion --dns=8.8.8.8 ubuntu:14.04

总结

本文主要通过演示 docker0 网桥相关的功能来探索 docker 网络中的 bridge 驱动的实现机制。从本文中不难看出,linux 系统中,docker 的 bridge 驱动是依赖于系统的 ip forward 以及 iptables 等核心功能的。因此在学习 docker 的过程中,适当的补充 linux 相关的知识也是十分必要的!

链接:https://www.cnblogs.com/sparkdev/p/9217310.html

(版权归原作者所有,侵删)

微信扫码关注该文公众号作者

来源:马哥Linux运维

相关新闻

读完China Daily这50篇文章,轻松拿捏用英语讲中国文化!(送笔记+文创)被孙颖莎拿捏的男人,年轻时颜值逆天,如今甘当“男保姆”,背后的贤妻是最大的功臣必看!PI3K/Akt、TGF-β/Smad、JAK/STAT、MAPK等等通路全都搞定!狠狠拿捏!被孙颖莎拿捏的男人,年轻时颜值逆天,如今为贤妻甘当“男保姆”…读完China Daily这50篇文章,轻松拿捏用英语讲中国文化!苹果赢麻了:“小小巴黎奥运!拿捏!”记者刁钻提问想拿捏全红婵,反被拿捏!“留学生有多懂人情世故??”老外已被轻松拿捏!!小小年薪30万,拿捏!夏天不用闷死在厨房,好吃又减脂的快手餐被我拿捏了“牛剑和藤校我全都要!”英美混申的秘诀,被轻松拿捏了!宗馥莉没那么容易被拿捏让你一键拿捏澳洲白人职场——含泪分享Woolworths就职经验跟着C人,拿捏gap hour的快乐!拿捏夏日氛围感!轻松get人间度假风中产妈和育儿嫂相互拿捏?访谈30位职场妈妈后,我发现这个认知误区是源头年入22亿!广东人的“相亲天堂”,被羽毛球拿捏了拿捏90%的分子机制研究!一次性搞懂AI+信号通路!这届年轻人,被3块5的冰杯“拿捏”了《花少好友记》新加坡行程大曝光!明星同款路线轻松拿捏,city不city?细读《三国演义》,我才知道高手是如何拿捏人性的拿捏年轻人的宠物沟通师,真能月入三五万?一次性搞懂AI+信号通路!拿捏90%的分子机制研究!Andrej Karpathy提出未来计算机2.0构想: 完全由神经网络驱动!网友炸锅了
logo
联系我们隐私协议©2024 bendi.news
Bendi新闻
Bendi.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Bendi.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。