在上一篇文章中 《“深刻浅出”来解读Docker网络核心原理》 你们了解了Docker中libnetwrok提供的4种驱动,它们各有千秋,但实际上每一种方式都有必定的局限性。假设须要运营一个数据中心的网络,咱们有许多的宿主机,每台宿主机上运行了成千上万个Docker容器,若是使用4种网络驱动的话会是怎么样的呢,咱们来分析一下:linux
可见,为了实现数据中心大量容器间的跨主机网络通讯,为了更灵活地实现容器间网络的共享与隔离,也为了在管理成千上万个容器时能够更加自动化地进行网络配置,咱们须要来了解更高级的网络方案。git
本文及后期的文章将经过一些工具和额外的操做来突破Docker网络原有的限制,实现一些更高级的功能来知足实际运用中的复杂需求。github
在上一篇文章中已经介绍过了linux network namespace,在本文中咱们将从实践的角度来了解如何在linux系统下操做linux network namespace。docker
ip是linux系统下一个强大的网络配置工具,它不只能够替代一些传统的网络管理工具,如ifconfig、route等,还能够实现更丰富的功能。下面将介绍如何使用ip命令来管理network namespace。shell
ip netns命令是用来操做network namespace的指令,具体使用方法以下。安全
建立一个network namespace:bash
#建立一个名为net-test的network namespace [root@ganbing ~]# ip netns add net-test
列出系统中已存在的network namespace:服务器
[root@ganbing ~]# ip netns ls net-test
删除一个network namespace:网络
[root@ganbing ~]# ip netns delete net-test
#命令格式
ip netns exec <network nameapce name> <command>ide
#好比显示net-test namespace的网卡信息,路由信息
[root@ganbing ~]# ip netns exec net-test ip addr 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 [root@ganbing ~]# ip netns exec net-test route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface
其实,你若是以为ip netns exec 来执行命令比较麻烦,还可使用启动一个shell来配合:
#命令格式
ip netns exec <network nameapce name> bash
这样,就能够在上面执行命令,就好像使用者进入了这个network namespace中;若是要退出这个bash,则输入exit便可。
当使用ip netns add命令建立了一个network namespace后,就拥有了一个独立的网络空间,能够根据需求来配置该网络空间,如添加网卡,配置IP,设置路由等。下面以以前创建的名为net-test的network namespace为例来演示如何进行这些操做。
当使用ip命令建立一个network namespace时,会默认建立一个回环设备(loopback interface:lo)。该设备默认不启动,最好将其启动。
[root@ganbing ~]# ip netns exec net-test ip link set dev lo up
在主机上建立两张虚拟网卡veth-1 和 veth-2:
[root@ganbing ~]# ip link add veth-1 type veth peer name veth-2
将veth-2设备添加到net-test这个network namespace中,veth-1留在宿主机中:
[root@ganbing ~]# ip link set veth-2 netns net-test
如今net-test这个network namespace就有两块网卡了(lo和veth-2),验证看一下:
[root@ganbing ~]# ip netns exec net-test ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 222: veth-2@if223: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether 92:24:fd:44:c6:00 brd ff:ff:ff:ff:ff:ff link-netnsid 0
接下来能够为网卡分配IP并启动网卡:
#在主机上为veth-1配置IP并启动 [root@ganbing ~]# ip addr add 10.0.0.1/24 dev veth-1 [root@ganbing ~]# ip link set dev veth-1 up #为net-test中的veth-2配置IP并启动 [root@ganbing ~]# ip netns exec net-test ip addr add 10.0.0.2/24 dev veth-2 [root@ganbing ~]# ip netns exec net-test ip link set dev veth-2 up
给两张网卡配置了IP后,会在各自的network namespace中生成一条路由,用ip route 或者 route -n查看:
#在主机中查看路由 [root@ganbing ~]# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface ... 10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 veth-1 ... #在net-test中查看路由 [root@ganbing ~]# ip netns exec net-test route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 veth-2
上面这两条路由代表的意义是目的地址 10.0.0.0/24网络的IP包分别从veth-1和veth-2发出。
如今net-test这个network namespace有了本身的网卡、IP地址、路由表等信息,就至关于成了一台小型的“虚拟机”了。测试一下它的连通性,来检查配置是否正确。
从主机的veth-1网卡ping net-test的veth-2网卡:
从net-test的veth-2网卡ping主机的veth-1网卡:
不少时候,想搭建一个复杂的网络环境来测试数据,每每受困于没有足够的资源来建立虚拟机。咱们掌握了配置network namespace后,即可以解决这个问题。能够在一台普通的机器上,以简单的方式建立多个相互隔离的network namespace,而后经过网卡、网桥等虚拟设备将它们链接起来,组成想要的网络拓扑。
下面咱们来演示一个简单的例子,将两个network namespace经过veth pair设备连起来。过程以下:
一、建立两个network namespace ns一、ns2,名称可自行定义:
[root@ganbing ~]# ip netns add ns1 [root@ganbing ~]# ip netns add ns2 [root@ganbing ~]# ip netns ls ns2 ns1
二、建立veth pair设备veth-a,veth-b:
[root@ganbing ~]# ip link add veth-a type veth peer name veth-b
三、将网卡分别放到两个network namespace中:
[root@ganbing ~]# ip link set veth-a netns ns1 [root@ganbing ~]# ip link set veth-b netns ns2
四、启动这两个网张:
[root@ganbing ~]# ip netns exec ns1 ip link set dev lo up [root@ganbing ~]# ip netns exec ns1 ip link set dev veth-a up [root@ganbing ~]# ip netns exec ns2 ip link set dev lo up [root@ganbing ~]# ip netns exec ns2 ip link set dev veth-b up
五、分配IP:
[root@ganbing ~]# ip netns exec ns1 ip addr add 10.0.0.1/24 dev veth-a [root@ganbing ~]# ip netns exec ns2 ip addr add 10.0.0.2/24 dev veth-b
六、验证连通
经过veth pair设备链接起来的两个network namespace就好像直接经过网线链接起来的两台机器,它的拓扑图以下所示:
你们想一下,若是有更多的network namespace须要链接怎么办?是否是就须要引入虚拟网桥了,就如同Docker网络同样。
在上一篇文章 <“深刻浅出”来解读Docker网络核心原理> 介绍过,Docker是使用Linux namespace技术进行资源隔离的,网络也是如此。当用默认网络模式(bridge模式)启动一个Docker容器时,必定是在主机上新建了一个Linux network namespace。咱们能够按照在network namespace中配置网络的方法来配置Docker 容器的网络。
首先,启动一个名为test1的Docker容器:
[root@ganbing ~]# docker run -itd --name test1 busybox
而后,使用ip netns list命令查看是否能够看到新建的network namespace。执行命令后发现并无看到新建的network namespace。这并不表明Docker容器没有建立network namespace,只是ip netns 命令没法查看而以,这个与ip netns命令工做方式有关。
当使用ip netns命令建立了两个network namespace(ns一、ns2)后,会在/var/run/netns目录下看到ns1和ns2:
[root@ganbing ~]# ls -la /var/run/netns/ total 0 drwxr-xr-x 2 root root 80 Mar 19 18:25 . drwxr-xr-x 40 root root 1240 Mar 19 15:08 .. -r--r--r-- 1 root root 0 Mar 19 18:22 ns1 -r--r--r-- 1 root root 0 Mar 19 18:22 ns2
ip netns list命令在/var/run/netns目录下查找network namespace。因为Docker建立的network namespace并不在此目录下建立任何选项,所以,须要一些额外的操做来使ip命令能够操纵Docker建立的network namespace。
Linux下的每个进程都会属于一个特定的network namespace,来看一下不一样network namespace环境中/pro/$PID/ns目录下有何区别。
#/proc/self 连接到当前正在运行的进程 [root@ganbing ~]# ls -la /proc/self/ns/ ...... lrwxrwxrwx 1 root root 0 Mar 19 19:17 net -> net:[4026531956] ...... #在ns1和ns2中 [root@ganbing ~]# ip netns exec ns1 ls -la /proc/self/ns ...... lrwxrwxrwx 1 root root 0 Mar 19 19:18 net -> net:[4026533018] ...... [root@ganbing ~]# ip netns exec ns2 ls -la /proc/self/ns lrwxrwxrwx 1 root root 0 Mar 19 19:18 net -> net:[4026533116]
从上面能够发现,不一样network namespace中的进程有不一样的net:[]号码发配。这些号码表明着不一样的network namespace,拥有相同net:[]号码的进程属于同一个network namesapce。只要将表明Docker建立的network namesapce的文件连接到/var/run/netns目录下,就可使用ip netns命令操做了,步骤方法以下:
一、用docker inspect查看test1容器的PID
[root@ganbing ~]# docker inspect --format '{{.State.Pid}}' test1 17037
二、若是/var/run/netns目录不存在,就要手工建立(通常都有),而后在/var/run/netns目录下建立软连接,指向test1容器的network namespace
[root@ganbing ~]# ln -s /proc/17037/ns/net /var/run/netns/test1
三、测试是否成功
[root@ganbing ~]# ip netns list test1 ns2 ns1 [root@ganbing ~]# ip netns exec test1 ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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 226: eth0@if227: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever
完成上面的配置后,就能够自行配置Docker的网络环境了。除了ip netns命令外,还有一些工具能够进入linux network namespace,好比nsenter。但须要额外的安装这个工具。
Docker现有的网络比较简单,扩展性和灵活性都不能知足不少复杂的应用场景。不少时候都须要自定义Docker容器的网络。好比,为了使容器各节点之间通讯、各节点和本地主机之间的通讯,比较简单的作法就是将Docker容器网络配置到本地主机网络的网段中。咱们来看一下怎么实现。
若是想要使Docker容器和容器主机处于同一网络,那么容器和主机应该处在一个二层网络中。就是把两台机器连在同一个交换机上,或者连在不一样的级联交换机上。在虚拟场影 下,虚拟网桥能够将容器连在一个二层网络中,只要将主机的网卡桥接到虚拟网桥中,就能将容器和主机的网络连起来,再给Docker容器分配一个本地局域网IP就OK了。
咱们来通个一个例子分析一下这个过程 :本地网络为 172.18.18.0/24,网关为 172.18.18.1,宿主机IP为172.18.18.34(网卡ens160),要在这台宿主机上启动一个名为test的Docker容器,并给它配置IP为 172.18.18.36。因为并不须要Docker提供的网络,因此用--net=none参数来启动容器。操做以下:
一、启动一个test容器
[root@docker ~]# docker run -itd --name test01 --network none busybox 39ea5fac5ebb8bd25372d04efb6b662a18cd6fdf85105c22df0796087d776280
二、建立一个供容器链接的网桥br0
[root@docker ~]# brctl addbr br0 [root@docker ~]# ip link set dev br0
三、将主机ens160网卡桥接到br0上,并把ens160的IP配置在br0上。因为笔者是远程操做服务器,因此执行这一步的时候会致使网络断开,所以这里放在一条命令执行
[root@docker ~]# ip addr add 172.18.18.34/24 dev br0; \ > ip addr del 172.18.18.34/24 dev ens160; \ > brctl addif br0 ens160; \ > ip route del default; \ > ip route add default via 172.18.18.1 dev br0
四、找到test01的PID
[root@docker ~]# docker inspect --format '{{.State.Pid}}' test01 4557
五、将容器的network namespace添加到/var/run/netns/目录下
[root@docker ~]# mkdir /var/run/netns [root@docker netns]# ln -s /proc/4557/ns/net /var/run/netns/test01
六、建立用于链接网桥和Docker容器的网卡设备
#将veth-a链接到br0网桥中 [root@docker ~]# ip link add veth-a type veth peer name veth-b [root@docker ~]# brctl addif br0 veth-a [root@docker ~]# ip link set dev veth-a up #将veth-b放在test的network namespace中,重命令eth0,并为其配置IP和默认路由 [root@docker ~]# ip netns exec test01 ip link set dev lo up [root@docker ~]# ip link set veth-b netns test01 [root@docker ~]# ip netns exec test01 ip link set dev veth-b name eth0 [root@docker ~]# ip netns exec test01 ip link set eth0 up [root@docker ~]# ip netns exec test01 ip addr add 172.18.18.36/24 dev eth0 [root@docker ~]# ip netns exec test01 ip route add default via 172.18.18.1
七、查看一下test01的网卡状况,并测试
[root@docker ~]# ip netns exec test01 ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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 9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000 link/ether 66:fa:71:ba:0e:fb brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.18.18.36/24 scope global eth0 valid_lft forever preferred_lft forever [root@docker ~]# ip netns exec test01 route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 172.18.18.1 0.0.0.0 UG 0 0 0 eth0 172.18.18.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
完成配置扣,Docker容器和宿主机链接的网络图以下所示:
如今test01容器能够与本地主机相互访问,而且test01容器能够经过本地网络的网关172.18.18.1访问外部网络。
从上面的过程能够发现,配置Docker容器的网络是至关繁琐的。若是须要常常自定义Docker网络,能够把上面的步骤编写成shell脚本,这样方便操做。事实上,目前已有了一个这样的工具解脱咱们繁琐的步骤,就是由Docker公司工程师Jerome Petazzoni在Githu上发布的pipework的工具。pipwork号称是容器的SDN解决方案,能够在复场景下将容器链接起来。其实随着Docker网络的不断改进,piipwork工具的不少功能会被Docker原生支持,所以pipework当初只是过渡方案之一而以,你们只要知道了解就好了。下面来看一下pipework的功能。
* 支持linux网桥链接到容器并配置容器IP
一、下载pipework
[root@docker ~]# git clone https://github.com/jpetazzo/pipework
二、将pipework脚本放处指定的目录,/usr/local/bin
[root@docker ~]# cp ./pipework/pipework /usr/local/bin/
三、对test01容器进行配置
[root@docker /]# docker run -itd --name test01 --network none busybox [root@docker /]# pipework br0 test01 172.18.18.36/24@172.18.18.1
上面配置命令操做以下:
这个过程和以前采用ip命令配置的过程相似,pipework其实就是用shell写的代码。
pipework其实还有其它的不少功能,好比还支持open vswitch、支持dhcp获取容器的IP等等,本文只要是和你们了解一下它的做用和功能,其它详细的功能就不做介绍了。
看到这里的朋友应该对network namespace和pipework有了更好的理解