docker网络2

http://blog.51cto.com/ganbing/2088899

前言

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

  1. 使用host驱动可以让容器与宿主机共用同一个网络栈,这么做看似解决了网络问题,可实际上并未使用network namespace的隔离,缺乏安全性。
  2. 使用Docker默认的bridge驱动,容器没有对外IP,只能通过NAT来实现对外通信。这种方式不能解决跨主机容器间直接通信的问题,难以满足复杂场景下的业务需求。
  3. 使用overlay驱动,可以用于支持跨主机的网络通信,但必须配合swarm进行配置和使用才能实现跨主机的网络通信。
  4. 使用null驱动实际上不进行任何网络配置。

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

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

把Linux network namespace玩起来

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

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

使用ip netns来操作network namespace

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

  • 创建一个network namespace:

    #创建一个名为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
    
  • 在network namespace中执行一条命令:

命令格式

ip netns exec <network nameapce name> <command>

比如显示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为network namespace配置网卡

当使用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网卡:


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

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


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

将两个network namespace连接起来

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

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

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

[root@ganbing ~]# ip netns add ns1
[root@ganbing ~]# ip netns add ns2
[root@ganbing ~]# ip netns ls
ns2
ns1

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

[root@ganbing ~]# ip link add veth-a type veth peer name veth-b

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

[root@ganbing ~]# ip link set veth-a netns ns1
[root@ganbing ~]# ip link set veth-b netns ns2

4、启动这两个网张:

[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

5、分配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

6、验证连通


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

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


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

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

使用ip命令配置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(ns1、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命令操作了,步骤方法如下:

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

[root@ganbing ~]# docker inspect  --format '{{.State.Pid}}' test1
17037

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

[root@ganbing ~]# ln -s /proc/17037/ns/net  /var/run/netns/test1

3、测试是否成功

[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。但需要额外的安装这个工具。

把pipework玩起来

Docker现有的网络比较简单,扩展性和灵活性都不能满足很多复杂的应用场景。很多时候都需要自定义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容器

[root@docker ~]# docker run -itd --name test01 --network none busybox
39ea5fac5ebb8bd25372d04efb6b662a18cd6fdf85105c22df0796087d776280

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

[root@docker ~]# brctl addbr br0
[root@docker ~]# ip link set dev br0

3、将主机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

4、找到test01的PID

[root@docker ~]# docker inspect  --format '{{.State.Pid}}' test01
4557

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

[root@docker ~]# mkdir /var/run/netns
[root@docker netns]# ln -s /proc/4557/ns/net /var/run/netns/test01

6、创建用于连接网桥和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

7、查看一下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容器和宿主机连接的网络图如下所示:


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

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

来解析一下pipework

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

*** 支持linux网桥连接到容器并配置容器IP**
1、下载pipework

[root@docker ~]# git clone https://github.com/jpetazzo/pipework

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

[root@docker ~]# cp ./pipework/pipework /usr/local/bin/

3、对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

上面配置命令操作如下:

  • 查看主机是否存在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有了更好的理解

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • docker之容器通信 这节属于了解学习,算是烂尾,最后我也没找到合适的方式去固定容器ip,然后作为正式环境去跑,...
    道无虚阅读 5,471评论 1 7
  • 转载自 http://blog.opskumu.com/docker.html 一、Docker 简介 Docke...
    极客圈阅读 10,468评论 0 120
  • 概述 自从docker容器出现以来,容器的网络通信就一直是大家关注的焦点,也是生产环境的迫切需求。而容器的网络通信...
    糙老爷们儿吃什么樱桃阅读 3,608评论 1 5
  • 昨夜繁星月下 我嗅到了都快已淡忘的芳香 依然是那么迷人与醉人心魂 回头望去空无一物 突然觉得还是自己太敏感 望着空...
    窮奇阅读 182评论 0 0
  • 没有什么能够阻挡你对自由的向往 即便是死亡也是你另一种重生 ——现在开头 你的心里住着一个国王 生活在不眠的都市 ...
    郁衡子阅读 400评论 7 2