续我的上篇博文:https://mp.csdn.net/postedit/88744072
一、Docker网络虚拟化原理
Docker 的网络实现其实就是利用了 Linux 上的网络命名空间和虚拟网络设备(特别是 veth pair)。
首先,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)来收发数据包;此外,如果不同子网之间要进行通信,需要路由机制。
Docker 中的网络接口默认都是虚拟的接口。虚拟接口的优势之一是转发效率较高。 Linux 通过在内核中进行数据复制来实现虚拟接口之间的数据转发,发送接口的发送缓存中的数据包被直接复制到接收接口的接收缓存中。对于本地系统和容器内系统看来就像是一个正常的以太网卡,只是它不需要真正同外部网络设备通信,速度要快很多。
Docker 容器网络就利用了这项技术。它在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通(这样的一对接口叫做 veth pair)。
netns 是在 linux 中提供网络虚拟化的一个项目,使用 netns 网络空间虚拟化可以在本地虚拟化出多个网络环境,目前 netns 在 lxc 容器中被用来为容器提供网络。使用 netns 创建的网络空间独立于当前系统的网络空间,其中的网络设备以及 iptables 规则等都是独立的,就好像进入了另外一个网络一样。
Docker 创建一个容器的时候,会执行如下操作:
- 创建一对虚拟接口,分别放到本地主机和新容器中;
- 本地主机一端桥接到默认的 docker0 或指定网桥上,并具有一个唯一的名字,如 veth0;
- 容器一端放到新容器中,并修改名字作为 eth0,这个接口只在容器的命名空间可见;
- 从网桥可用地址段中获取一个空闲地址分配给容器的 eth0,并配置默认路由到桥接网卡 veth0。
完成这些之后,容器就可以使用 eth0 虚拟网卡来连接其他容器和其他网络。
通过 –net 参数来指定容器的网络配置,有4个可选值:
- --net=bridge 这个是默认值,连接到默认的网桥。
- --net=host 告诉 Docker 不要将容器网络放到隔离的命名空间中,即不要容器化容器内的网络。此时容器使用本地主机的网络,它拥有完全的本地主机接口访问权限。容器进程可以跟主机其它 root 进程一样可以打开低范围的端口,可以访问本地网络服务比如 D-bus,还可以让容器做一些影响整个主机系统的事情,比如重启主机。因此使用这个选项的时候要非常小心。如果进一步的使用 --privileged=true,容器会被允许直接配置主机的网络堆栈
- --net=container:NAME_or_ID 让 Docker 将新建容器的进程放到一个已存在容器的网络栈中,新容器进程有自己的文件系统、进程列表和资源限制,但会和已存在的容器共享 IP 地址和端口等网络资源,两者进程可以直接通过 lo 环回接口通信。
- --net=none 让 Docker 将新容器放到隔离的网络栈中,但是不进行网络配置。之后,用户可以自己进行配置。
二、网络虚拟化部署(在 none 网络模式下为容器分配固定 ip)
前期准备:
在为容器分配固定ip之前,我们需要先生成/var/run/netns目录(netns目录本身是不存在的)
#生成/var/run/netns目录的方法一:用mkdir建一个该目录即可#
[root@server1 ~]# mkdir /var/run/netns
#生成/var/run/netns目录的方法二:利用ip netns命令先增加一个虚拟命名空间,然后再删除该虚拟命名空间即可。#
[root@server1 ~]# ip netns add test #增加虚拟命名空间test。该命令会在/var/run/netns目录下创建test网络命名空间名(其中/var/run/netns目录是自动生成的)
[root@server1 ~]# ip netns list #显示所有的虚拟网络命名空间,也可通过查看/var/run/netns目录下的文件来列出,结果一样。([root@server1 ~]# ls /var/run/netns/)
[root@server1 ~]# ip netns del test #删除虚拟命令空间。该命令会在/var/run/netns目录下删除test网络命名空间名
[root@server1 ~]# ip netns list #此时再次查看虚拟命名空间,发现test已经不存在了。也可以通过查看/var/run/netns目录下的文件来列出,结果一样。([root@server1 ~]# ls /var/run/netns/)
1、在本地主机查找容器vm3的进程 id,并为它创建网络命名空间。
[root@server1 ~]# docker inspect vm4 | grep Pid #查看vm4容器的Pid号,用其来生成一个虚拟网络命名空间
"Pid": 24341,
"PidMode": "",
"PidsLimit": 0,
[root@server1 ~]# ln -s /proc/24341/ns/net /var/run/netns/24341 #增加虚拟网络命名空间24341。值的注意的是这里不能通过"ip netns add 24341"来增加虚拟网络命名空间,这是因为用软链接的方式是有vm3的数据的;而用"ip netns add 24341是没有vm3的数据的"。
[root@server1 ~]# ip netns list #显示所有的虚拟网络命名空间(或者使用命令"ls /var/run/netns/"),以查看24341这个虚拟网络命名空间是否添加成功
2、创建一对 “veth pair” 接口 veth0 和 veth1,激活veth0和vrth1,绑定 veth0 到网桥 docker0,
[root@server1 ~]# ip link add name veth0 type veth peer name veth1 #增加一对veth虚拟网卡(网卡的名字分别是veth0,veth1)。以便将一块网卡连接docker0,一块网卡连接需要增加ip的容器vm4
[root@server1 ~]# ip addr #查看虚拟网卡veth0和veth1是否已经生成。我们可以看到这两个虚拟网卡没有被激活
260: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether 2a:6a:45:96:69:da brd ff:ff:ff:ff:ff:ff
261: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether d6:dd:99:81:ed:c2 brd ff:ff:ff:ff:ff:ff
[root@server1 ~]# ip link set up veth0 #激活虚拟网卡veth0
[root@server1 ~]# ip link set up veth1 #激活虚拟网卡veth1
[root@server1 ~]# ip addr #再次查看虚拟网卡veth0和veth1是否已经被激活。我们可以看到两块虚拟网卡已经被激活
260: veth1@veth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000
link/ether 2a:6a:45:96:69:da brd ff:ff:ff:ff:ff:ff
inet6 fe80::286a:45ff:fe96:69da/64 scope link
valid_lft forever preferred_lft forever
261: veth0@veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000
link/ether d6:dd:99:81:ed:c2 brd ff:ff:ff:ff:ff:ff
inet6 fe80::d4dd:99ff:fe81:edc2/64 scope link
valid_lft forever preferred_lft forever
[root@server1 ~]# brctl addif docker0 veth0 #将一块虚拟网卡veth0(当然也可以是veth1)连接docker0。即将veth0添加为docker0的网络接口
[root@server1 ~]# brctl show docker0 #查看docker0的桥接情况,以确保veth0已经成功连接docker0
bridge name bridge id STP enabled interfaces
docker0 8000.02426b0d90ce no veth0
veth8aa2220
3、将veth1放到容器的网络命名空间,命名为 eth0,启动它。
[root@server1 ~]# ip link set veth1 netns 24341 #将另一块虚拟网卡veth1添加到24341虚拟网络环境中
[root@server1 ~]# docker attach vm4 #连接容器vm4,我们发现多了一个网卡veth1,但是该网卡的状态是DOWN,所以需要激活该网卡
root@dd42b8e24eeb:/# ip a
valid_lft forever preferred_lft forever
262: veth1@if263: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 76:6f:e2:d8:cf:03 brd ff:ff:ff:ff:ff:ff
root@dd42b8e24eeb:/# ip link set up veth1 #在容器内激活veth1网卡,发现操作不允许
RTNETLINK answers: Operation not permitted
[root@server1 ~]# ip netns exec 24341 ip link set veth1 name eth0 #连接虚拟网络命名空间24341,将容器内的网卡veth1改名为eth0(这步可做,也可不做)
[root@server1 ~]# ip netns exec 24341 ip link set up eth0 #连接虚拟网络命名空间24341,激活网卡eth0
4、为eth0配置一个可用 IP(桥接网段)和默认网关。
[root@server1 ~]# docker attach vm4 #连接容器vm4,查看网卡名字是否修改成功,网卡是否激活成功。但是又会发现一个问题(网卡eth0没有ip地址)
root@dd42b8e24eeb:/#
root@dd42b8e24eeb:/# ip a
262: eth0@if263: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 76:6f:e2:d8:cf:03 brd ff:ff:ff:ff:ff:f
[root@server1 ~]# ip netns exec 24341 ip addr add 172.17.0.100/24 dev eth0 #连接24341虚拟网络命名空间,给容器内的eth0网卡添加ip地址
[root@server1 ~]# docker attach vm4 #连接容器vm4,我们可以看到eth0网卡上已经有设定的ip地址了
root@dd42b8e24eeb:/# ip a
262: eth0@if263: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 76:6f:e2:d8:cf:03 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.100/24 scope global eth0
valid_lft forever preferred_lft forever
root@dd42b8e24eeb:/# ping 172.17.0.2 #ping容器vm1的ip地址(能pin通,这是因为vm1桥接在docker0,而连接容器vm3的虚拟网络命名空间24341的一个网卡veth0也桥接在docker0上)
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.100 ms
root@dd42b8e24eeb:/# ping 172.17.0.1 #能ping通docker0的地址
PING 172.17.0.1 (172.17.0.1) 56(84) bytes of data.
64 bytes from 172.17.0.1: icmp_seq=1 ttl=64 time=0.076 ms
root@dd42b8e24eeb:/# ping 172.25.83.1 #能ping通虚拟机server1的地址(docker0可以跟虚拟机server1进行通信,容器vm3可以和docker0进行通信,所以容器vm3可以和虚拟机server1通信)
PING 172.25.83.1 (172.25.83.1) 56(84) bytes of data.
64 bytes from 172.25.83.1: icmp_seq=1 ttl=64 time=0.055 ms
root@dd42b8e24eeb:/# ping www.baidu.com #ping www,baidu.com。报错,这是因为没有解析
ping: unknown host www.baidu.com
[root@server1 ~]# ip netns exec 24341 ip route add default via 172.17.0.1 #ip netns exec 24341 ip route add default gw 172.17.0.1或连接虚拟网络命名空间24341,为位于该虚拟网络命名空间的容器添加网关。
root@dd42b8e24eeb:/# route -n #查看网关
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.17.0.1 0.0.0.0 UG 0 0 0 eth0
172.17.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
root@dd42b8e24eeb:/# cat /etc/resolv.conf #查看dns
nameserver 114.114.114.114
root@dd42b8e24eeb:/# ping www.baidu.com #能ping通www.baidu.com,这是因为虚拟机server1能够上网
PING www.a.shifen.com (220.181.111.37) 56(84) bytes of data.
64 bytes from 220.181.111.37: icmp_seq=1 ttl=50 time=28.8 ms
64 bytes from 220.181.111.37: icmp_seq=2 ttl=50 time=52.4 ms
以上,就是 Docker 配置网络的具体过程。
当容器vm4结束后,Docker 会清空容器,容器内的 eth0 会随网络命名空间一起被清除,veth0 接口也被自动从 docker0 卸载。
此外,用户可以使用 ip netns exec 命令来在指定网络命名空间中进行配置,从而配置容器内的网络。