天天看点

Docker高级网络实践之 玩转Linux network namespace & pipework

 在上一篇文章中 《“深入浅出”来解读Docker网络核心原理》 大家了解了Docker中libnetwrok提供的4种驱动,它们各有千秋,但实际上每一种方式都有一定的局限性。假设需要运营一个数据中心的网络,我们有许多的宿主机,每台宿主机上运行了成千上万个Docker容器,如果使用4种网络驱动的话会是怎么样的呢,我们来分析一下:

使用host驱动可以让容器与宿主机共用同一个网络栈,这么做看似解决了网络问题,可实际上并未使用network namespace的隔离,缺乏安全性。

使用Docker默认的bridge驱动,容器没有对外IP,只能通过NAT来实现对外通信。这种方式不能解决跨主机容器间直接通信的问题,难以满足复杂场景下的业务需求。

使用overlay驱动,可以用于支持跨主机的网络通信,但必须配合swarm进行配置和使用才能实现跨主机的网络通信。

使用null驱动实际上不进行任何网络配置。

 可见,为了实现数据中心大量容器间的跨主机网络通信,为了更灵活地实现容器间网络的共享与隔离,也为了在管理成千上万个容器时可以更加自动化地进行网络配置,我们需要来了解更高级的网络方案。

 本文及后期的文章将通过一些工具和额外的操作来突破Docker网络原有的限制,实现一些更高级的功能来满足实际运用中的复杂需求。

 在上一篇文章中已经介绍过了linux network namespace,在本文中我们将从实践的角度来了解如何在linux系统下操作linux network namespace。

 ip是linux系统下一个强大的网络配置工具,它不仅可以替代一些传统的网络管理工具,如ifconfig、route等,还可以实现更丰富的功能。下面将介绍如何使用ip命令来管理network namespace。

ip netns命令是用来操作network namespace的指令,具体使用方法如下。

创建一个network namespace:

列出系统中已存在的network namespace:

删除一个network namespace:

在network namespace中执行一条命令:

#命令格式

ip netns exec <network nameapce name> <command>

#比如显示net-test namespace的网卡信息,路由信息

其实,你如果觉得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)。该设备默认不启动,最好将其启动。

在主机上创建两张虚拟网卡veth-1 和 veth-2:

将veth-2设备添加到net-test这个network namespace中,veth-1留在宿主机中:

现在net-test这个network namespace就有两块网卡了(lo和veth-2),验证看一下:

接下来可以为网卡分配IP并启动网卡:

 给两张网卡配置了IP后,会在各自的network namespace中生成一条路由,用ip route 或者 route -n查看:

 上面这两条路由表明的意义是目的地址 10.0.0.0/24网络的IP包分别从veth-1和veth-2发出。

 现在net-test这个network namespace有了自己的网卡、IP地址、路由表等信息,就相当于成了一台小型的“虚拟机”了。测试一下它的连通性,来检查配置是否正确。

 从主机的veth-1网卡ping net-test的veth-2网卡:

Docker高级网络实践之 玩转Linux network namespace & pipework

 从net-test的veth-2网卡ping主机的veth-1网卡:

Docker高级网络实践之 玩转Linux network namespace & pipework

 很多时候,想搭建一个复杂的网络环境来测试数据,往往受困于没有足够的资源来创建虚拟机。我们掌握了配置network namespace后,便可以解决这个问题。可以在一台普通的机器上,以简单的方式创建多个相互隔离的network namespace,然后通过网卡、网桥等虚拟设备将它们连接起来,组成想要的网络拓扑。

 下面我们来演示一个简单的例子,将两个network namespace通过veth pair设备连起来。过程如下:

1、创建两个network namespace ns1、ns2,名称可自行定义:

2、创建veth pair设备veth-a,veth-b:

3、将网卡分别放到两个network namespace中:

4、启动这两个网张:

5、分配IP:

6、验证连通

Docker高级网络实践之 玩转Linux network namespace & pipework

 通过veth pair设备连接起来的两个network namespace就好像直接通过网线连接起来的两台机器,它的拓扑图如下所示:

Docker高级网络实践之 玩转Linux network namespace & pipework

 大家想一下,如果有更多的network namespace需要连接怎么办?是不是就需要引入虚拟网桥了,就如同Docker网络一样。

 在上一篇文章 <“深入浅出”来解读Docker网络核心原理> 介绍过,Docker是使用Linux namespace技术进行资源隔离的,网络也是如此。当用默认网络模式(bridge模式)启动一个Docker容器时,一定是在主机上新建了一个Linux network namespace。我们可以按照在network namespace中配置网络的方法来配置Docker 容器的网络。

 首先,启动一个名为test1的Docker容器:

 然后,使用ip netns list命令查看是否可以看到新建的network namespace。执行命令后发现并没有看到新建的network namespace。这并不代表Docker容器没有创建network namespace,只是ip netns 命令无法查看而以,这个与ip netns命令工作方式有关。

 当使用ip netns命令创建了两个network namespace(ns1、ns2)后,会在/var/run/netns目录下看到ns1和ns2:

 ip netns list命令在/var/run/netns目录下查找network namespace。由于Docker创建的network namespace并不在此目录下创建任何选项,因此,需要一些额外的操作来使ip命令可以操纵Docker创建的network namespace。

 Linux下的每一个进程都会属于一个特定的network namespace,来看一下不同network namespace环境中/pro/$PID/ns目录下有何区别。

 从上面可以发现,不同network namespace中的进程有不同的net:[]号码发配。这些号码代表着不同的network namespace,拥有相同net:[]号码的进程属于同一个network namesapce。只要将代表Docker创建的network namesapce的文件链接到/var/run/netns目录下,就可以使用ip netns命令操作了,步骤方法如下:

1、用docker inspect查看test1容器的PID

2、如果/var/run/netns目录不存在,就要手工创建(一般都有),然后在/var/run/netns目录下创建软链接,指向test1容器的network namespace

3、测试是否成功

 完成上面的配置后,就可以自行配置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参数来启动容器。操作如下:

1、启动一个test容器

2、创建一个供容器连接的网桥br0

3、将主机ens160网卡桥接到br0上,并把ens160的IP配置在br0上。由于笔者是远程操作服务器,所以执行这一步的时候会导致网络断开,因此这里放在一条命令执行

4、找到test01的PID

5、将容器的network namespace添加到/var/run/netns/目录下

6、创建用于连接网桥和Docker容器的网卡设备

7、查看一下test01的网卡情况,并测试

 完成配置扣,Docker容器和宿主机连接的网络图如下所示:

Docker高级网络实践之 玩转Linux network namespace & pipework

 现在test01容器可以与本地主机相互访问,并且test01容器可以通过本地网络的网关172.18.18.1访问外部网络。

 从上面的过程可以发现,配置Docker容器的网络是相当繁琐的。如果需要经常自定义Docker网络,可以把上面的步骤编写成shell脚本,这样方便操作。事实上,目前已有了一个这样的工具解脱我们繁琐的步骤,就是由Docker公司工程师Jerome Petazzoni在Githu上发布的pipework的工具。pipwork号称是容器的SDN解决方案,可以在复场景下将容器连接起来。其实随着Docker网络的不断改进,piipwork工具的很多功能会被Docker原生支持,因此pipework当初只是过渡方案之一而以,大家只要知道了解就行了。下面来看一下pipework的功能。

* 支持linux网桥连接到容器并配置容器IP

1、下载pipework

2、将pipework脚本放处指定的目录,/usr/local/bin

3、对test01容器进行配置

上面配置命令操作如下:

查看主机是否存在br0网桥,不存在就创建;

向test01中加入一块名为eth1的网卡,并配置IP172.18.18.36/24;

若test01中已有默认路由,就删除,把172.18.18.1设为默认路由l

将test01容器连接到之前创建的网桥上br0;

这个过程和之前采用ip命令配置的过程类似,pipework其实就是用shell写的代码。

pipework其实还有其它的很多功能,比如还支持open vswitch、支持dhcp获取容器的IP等等,本文只要是和大家了解一下它的作用和功能,其它详细的功能就不作介绍了。

看到这里的朋友应该对network namespace和pipework有了更好的理解
Docker高级网络实践之 玩转Linux network namespace & pipework