容器網絡原理分析:veth 和 network namespace


1. Liunx veth-pair 和 network namespace

Docker 中容器的訪問須要依賴 veth-pair 和 network namespace 等技術。network namespace(網絡命名空間)表明的是獨立的網絡協議棧,不一樣的網絡命令空間相互隔離,沒法訪問。而 veth-pair 能夠打破這種限制,實現不一樣網絡命令空間的相互訪問。
 
構建包含 veth-pair 和 network namespace 的示意圖以下:
 
建立網絡命名空間 ns1 和 ns2:
[root@lianhua netns]$ ip netns add ns1
[root@lianhua netns]$ ip netns add ns2
[root@lianhua netns]$ ip netns list
ns2
ns1
 
默認狀況下建立的網絡命名空間只有一個 loopback 接口,將它 up 起來:
[root@lianhua netns]$ ip netns exec ns1 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
 
[root@lianhua netns]$ ip netns exec ns1 ip link set dev lo up
[root@lianhua netns]$ ip netns exec ns1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    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 forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
 
建立 veth-pair 設備 veth-ns1-a 和 veth-ns2-b,並將 veth-pair 分別加到對應的 network namespace 中:
[root@lianhua netns]$ ip link add veth-ns1-a type veth peer name veth-ns2-b
[root@lianhua netns]$ ip link set veth-ns1-a netns ns1
[root@lianhua netns]$ ip link set veth-ns2-b netns ns2
 
[root@lianhua netns]$ ip netns exec ns1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    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 forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
307: veth-ns1-a@if306: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 8a:a2:45:23:aa:28 brd ff:ff:ff:ff:ff:ff link-netnsid 1
 
[root@lianhua netns]$ ip netns exec ns2 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
306: veth-ns2-b@if307: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 5e:a9:4e:d9:e2:5d brd ff:ff:ff:ff:ff:ff link-netnsid 0
 
veth 設備在網絡命名空間中的表現是一個網絡接口,能夠把它們想象成一根「網線」,網線一頭連在 ns1,一頭連在 ns2。將接口 up 起來:
[root@lianhua netns]$ ip netns exec ns1 ip link set dev veth-ns1-a up
[root@lianhua netns]$ ip netns exec ns2 ip link set dev veth-ns2-b up
[root@lianhua netns]$ ip netns exec ns1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    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 forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
307: veth-ns1-a@if306: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 8a:a2:45:23:aa:28 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::88a2:45ff:fe23:aa28/64 scope link
       valid_lft forever preferred_lft forever
 
爲接口配置 ip:
[root@lianhua netns]$ ip netns exec ns1 ip addr add 162.0.0.1/24 dev veth-ns1-a
[root@lianhua netns]$ ip netns exec ns2 ip addr add 162.0.0.2/24 dev veth-ns2-b
[root@lianhua netns]$ ip netns exec ns1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    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 forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
307: veth-ns1-a@if306: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 8a:a2:45:23:aa:28 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet 162.0.0.1/24 scope global veth-ns1-a
       valid_lft forever preferred_lft forever
    inet6 fe80::88a2:45ff:fe23:aa28/64 scope link
       valid_lft forever preferred_lft forever
 
進入到網絡命名空間 ns1 中訪問 ns2:
[root@lianhua netns]$ ip netns exec ns1 ping 162.0.0.2
PING 162.0.0.2 (162.0.0.2) 56(84) bytes of data.
64 bytes from 162.0.0.2: icmp_seq=1 ttl=64 time=0.066 ms
64 bytes from 162.0.0.2: icmp_seq=2 ttl=64 time=0.027 ms
 
--- 162.0.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.027/0.046/0.066/0.020 ms
[root@lianhua netns]$ ip netns exec ns1 ip route
162.0.0.0/24 dev veth-ns1-a proto kernel scope link src 162.0.0.1
 
能夠看到,veth-pair 實現了從網絡命令空間 ns1 到 ns2 的訪問。
 
值得注意的是,在真實的 docker 網絡模式下 veth-pair 打通的是容器 network namespace 和宿主機 root namespace 之間互相訪問的限制,veth-pair 的一頭在宿主機,一頭在容器中(在容器中的一頭會被 docker 更改接口名爲 eth0)。

2. 容器網絡原理

Docker 容器經過 namespace 作資源隔離,其中經過 network namespace 作網絡資源隔離,docker 容器實質上可看做一個 network namespace。基於此,構建容器網絡示意圖以下:
 
建立網絡命名空間 ns5,veth-pair 和網橋 lxcbr1:
[root@lianhua ~]$ ip netns add ns5
[root@lianhua ~]$ ip link add veth5.1 type veth peer name veth5.2
[root@lianhua ~]$ brctl addbr lxcbr1
[root@lianhua ~]$ brctl stp lxcbr1 off
 
將 veth5.2 添加到 ns5 內,veth5.1 連到網橋 lxcbr1:
[root@lianhua ~]$ ip link set veth5.2 netns ns5
[root@lianhua ~]$ ip netns exec ns5 ip link set dev veth5.2 up
 
[root@lianhua ~]$ ip link set dev veth5.1 up
[root@lianhua ~]$ brctl addif lxcbr1 veth5.1
 
在網橋 lxcbr1 上添加 ip,該 ip 將做爲 ns5 網絡接口的網關:
[root@lianhua ~]$ ifconfig lxcbr1 172.11.0.1/24 up
 
# 相似 docker 機制,將 veth5.2 更名爲 eth0
[root@lianhua ~]$ ip netns exec ns5 ip link set dev veth5.2 down
[root@lianhua ~]$ ip netns exec ns5 ip link set dev veth5.2 name eth0
[root@lianhua ~]$ ip netns exec ns5 ip link set dev eth0 up
 
[root@lianhua ~]$ ip netns exec ns5 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    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 forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
312: eth0@if313: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether ea:23:3f:8f:9d:07 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::e823:3fff:fe8f:9d07/64 scope link
       valid_lft forever preferred_lft forever
[root@lianhua ~]$ ip netns exec ns5 ifconfig eth0 172.11.0.2/24 up
 
爲 network namespace 配置默認路由:
[root@lianhua ~]$ ip netns exec ns5 route add default gw 172.11.0.1
[root@lianhua ~]$ ip netns exec ns5 ip route
default via 172.11.0.1 dev eth0
172.11.0.0/24 dev eth0 proto kernel scope link src 172.11.0.2
 
進入 ns5 內訪問宿主機:
[root@lianhua ~]$ ip netns exec ns5 ping 192.168.0.69
PING 192.168.0.69 (192.168.0.69) 56(84) bytes of data.
64 bytes from 192.168.0.69: icmp_seq=1 ttl=64 time=0.041 ms
64 bytes from 192.168.0.69: icmp_seq=2 ttl=64 time=0.026 ms
 
--- 192.168.0.69 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.026/0.033/0.041/0.009 ms

3. 容器到 network namespace 的映射

前面介紹了,容器訪問其實是 network namespace 中 veth 設備的訪問。那麼咱們在真實建立的容器中查看對應的 network namesapce:
[root@lianhua ~]$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              NAMES
459df1132c4b        caps1371            "/bin/bash"         6 days ago          Up 6 days           test2
94a3abcf7e54        caps1371            "/bin/bash"         6 days ago          Up 6 days           test1
[root@lianhua ~]$ ip netns list
 
宿主機上有兩個容器,可是爲何看不到容器對應的 network namespace?
這是由於 ip nets 沒法查看 docker 建立的 network namespace,進行以下適配便可看到容器對應的 network namespace。
 
查找容器對應的進程號:
[root@lianhua ~]$ docker inspect --format '{{ .State.Pid }}' test1
43163
[root@lianhua ~]$ docker inspect --format '{{ .State.Pid }}' test2
43265
 
創建容器到 network namespace 的映射:
# 建立 /var/run/netns 目錄,ip netns 會查找該目錄下的 network namespace
[root@lianhua ~]$ mkdir -p /var/run/netns
[root@lianhua ~]$ ln -s /proc/43163/ns/net /var/run/netns/test1
[root@lianhua ~]$ ln -s /proc/43265/ns/net /var/run/netns/test2
 
show network namespace:
[root@lianhua ~]$ ip netns list
test2 (id: 3)
test1 (id: 2)
 
[root@lianhua ~]$ ip netns exec test1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    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 forever
298: eth0@if299: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:19:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.25.0.2/16 brd 172.25.255.255 scope global eth0
       valid_lft forever preferred_lft forever
302: eth1@if303: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:1a:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.26.0.3/16 brd 172.26.255.255 scope global eth1
       valid_lft forever preferred_lft forever
 
[root@lianhua ~]$ ip netns exec test2 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    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 forever
300: eth0@if301: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:1a:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.26.0.2/16 brd 172.26.255.255 scope global eth0
       valid_lft forever preferred_lft forever
 
在 network namespace 中 show 出了容器的網絡接口。掌握了 veth-pair,network namespace 和容器的實現機制,那麼咱們就能夠給正在運行的容器添加接口(veth-pair)了。
進一步的,查看 test1 和 test2 的網絡 id 是否一致:
[root@lianhua ~]$ ip netns exec test1 ls -la /proc/self/ns/
total 0
...
lrwxrwxrwx 1 root root 0 Jan 12 00:42 net -> net:[4026532403]
 
[root@lianhua ~]$ ip netns exec test2 ls -la /proc/self/ns/
total 0
...
lrwxrwxrwx 1 root root 0 Jan 12 00:42 net -> net:[4026532502]
 
net:[] 中括號內是 network namespace 的 id,相同的 network namespace 具備相同的 id,能夠看到 test1 和 test2 的 id 是不同的,驗證了它們處於不一樣的 network namespace 中。
相關文章
相關標籤/搜索