由于容器运行在自己单独的network namespace里面,所以都有自己单独的协议栈。dockerd 会创建一个linux 虚拟网络设备:docker0。在启动每一个容器的时候,都会创建一种虚拟网络设备 veth-pair。
veth-pair 是什么
顾名思义,veth-pair 就是一对的虚拟设备接口,它都是成对出现的。一端连着协议栈,一端彼此相连着。正因为有这个特性,它常常充当着一个桥梁,连接着各种虚拟网络设备,典型的例子像“两个 namespace 之间的连接”,而这正是被docker拿来管理网络用了。
动手体验下namespace之间的连通性
1. 直连模式
namespace 是 Linux 2.6.x 内核版本之后支持的特性,主要用于资源的隔离。有了 namespace,一个 Linux 系统就可以抽象出多个网络子系统,各子系统间都有自己的网络设备,协议栈等,彼此之间互不影响。
如果各个 namespace 之间需要通信,怎么办呢,答案就是用 veth-pair 来做桥梁。
给 veth-pair 配置 IP,测试连通性:
-
创建 两个 namespace
ip netns a ns1 ip netns a ns2
-
创建一对 veth-pair veth0 veth1
ip l a veth0 type veth peer name veth1
-
将 veth0 veth1 分别加入两个 ns
ip l s veth0 netns ns1 ip l s veth1 netns ns2
-
给两个 veth0 veth1 配上 IP 并启用
ip netns exec ns1 ip a a 10.1.1.2/24 dev veth0 ip netns exec ns1 ip l s veth0 up ip netns exec ns2 ip a a 10.1.1.3/24 dev veth1 ip netns exec ns2 ip l s veth1 up
-
从 veth0 ping veth1
[root@localhost ~]# ip netns exec ns1 ping 10.1.1.3 PING 10.1.1.3 (10.1.1.3) 56(84) bytes of data. 64 bytes from 10.1.1.3: icmp_seq=1 ttl=64 time=0.073 ms 64 bytes from 10.1.1.3: icmp_seq=2 ttl=64 time=0.068 ms --- 10.1.1.3 ping statistics --- 15 packets transmitted, 15 received, 0% packet loss, time 14000ms rtt min/avg/max/mdev = 0.068/0.084/0.201/0.032 ms
2 通过 Bridge 相连
同样给 veth-pair 配置 IP,测试其连通性:
-
首先创建虚拟网络设备,虚拟网桥 br0
bridge是一个虚拟网络设备,所以具有网络设备的特征,可以配置IP、MAC地址等;其次,bridge是一个虚拟交换机,和物理交换机有类似的功能。ip l a br0 type bridge ip l s br0 up
-
然后创建两对 veth-pair
ip l a veth0 type veth peer name br-veth0 ip l a veth1 type veth peer name br-veth1
-
分别将两对 veth-pair 加入两个 ns 和 br0
ip l s veth0 netns ns1 ip l s br-veth0 master br0 ip l s br-veth0 up ip l s veth1 netns ns2 ip l s br-veth1 master br0 ip l s br-veth1 up
-
给两个 ns 中的 veth 配置 IP 并启用
ip netns exec ns1 ip a a 10.1.1.2/24 dev veth0 ip netns exec ns1 ip l s veth0 up ip netns exec ns2 ip a a 10.1.1.3/24 dev veth1 ip netns exec ns2 ip l s veth1 up
-
veth0 ping veth1 (失败)
[root@localhost ~]# ip netns exec ns1 ping 10.1.1.3 PING 10.1.1.3 (10.1.1.3) 56(84) bytes of data.
为什么呢?我们看到除了本机需要开启ip_forward之外,iptables 规则中的FORWARD链 默认的转发策略是DROP。所以这里会一直等,没有任何消息。我们去查看一下当前的相关策略。
[root@VM-0-15-centos ~]# iptables -nvL FORWARD Chain FORWARD (policy DROP 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 37 3108 DOCKER-USER all -- * * 0.0.0.0/0 0.0.0.0/0 37 3108 DOCKER-ISOLATION-STAGE-1 all -- * * 0.0.0.0/0 0.0.0.0/0 0 0 ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED 0 0 DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0 0 0 ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0 0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
这里需要单独放行掉,从br0 过来的package(我们已经看到了docker0 的特殊设置)
-
设置iptables 策略
iptables -A FORWARD -i br0 -j ACCEPT
-
再来:我们看到网络已经可以正常访问。
[root@VM-0-15-centos ~]# ip netns exec ns1 ping 10.1.1.3 PING 10.1.1.3 (10.1.1.3) 56(84) bytes of data. 64 bytes from 10.1.1.3: icmp_seq=1 ttl=64 time=0.050 ms 64 bytes from 10.1.1.3: icmp_seq=2 ttl=64 time=0.058 ms ^C --- 10.1.1.3 ping statistics ---
总结
veth-pair 在虚拟网络中充当着桥梁的角色,连接多种网络设备构成复杂的网络。docker 的网络管理使用这种方式,来设置容器间的通讯。