docker swarm (三):overlay与docker_gwbridge网络详解

本文通过实验,帮助大家认识docker swarm中的overlay和docker_gwbridge网络。

实验环境搭建

先建立两台物理机组成的docker swarm网络(方法可见《docker swarm(一): 入门,搭建一个简单的swarm集群》):

$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
43k0p9fnwu9dhsyr0n6utfynn *   ubuntu              Ready               Active              Leader              19.03.5
gorkh8cb5ylb7szzbbrp2sheu     ubuntu-2            Ready               Active                                  19.03.5

创建一个overlay网络。

docker network create -d overlay --attachable --subnet 10.200.0.0/16 overlay_test

当前建立的docker相关的网络有:

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
a473a52d686d        bridge              bridge              local
5e1880193fbf        docker_gwbridge     bridge              local
62ba25167374        host                host                local
jjyg85t5ta3k        ingress             overlay             swarm
d056684646b3        none                null                local
hxyiridl2b9r        overlay_test        overlay             swarm

这里关注两个网络:

  • overlay_test:overlay网络,实现容器间东西向流量的网络。
  • docker_gwbridge: 容器收发南北向报文的网络。

工具准备

我们知道,docker是基于namespace,划分了网络空间。这里先准备一段脚本,由于在各个namespece中,执行对应的网络命令。

#!/bin/bash 
NAMESPACE=$1    
if [[ -z $NAMESPACE ]]; then    
    ls -1 /var/run/docker/netns/    
    exit 0  
fi  
NAMESPACE_FILE=/var/run/docker/netns/${NAMESPACE}   
if [[ ! -f $NAMESPACE_FILE ]]; then 
    NAMESPACE_FILE=$(docker inspect -f "{{.NetworkSettings.SandboxKey}}" $NAMESPACE 2>/dev/null)    
fi  
if [[ ! -f $NAMESPACE_FILE ]]; then 
    echo "Cannot open network namespace '$NAMESPACE': No such file or directory"    
    exit 1  
fi  
shift   
if [[ $# -lt 1 ]]; then 
    echo "No command specified" 
    exit 1  
fi  
nsenter --net=${NAMESPACE_FILE} $@

它可以查看有哪些namespace:

$ sudo ./docker_netns.sh 
1-k2rx924tgr
eab3f856fe9a
ingress_sbox

还可以在指定的namespace下执行命令:

$ sudo ./docker_netns.sh eab3f856fe9a ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
170: eth0@if171: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP mode DEFAULT group default 
    link/ether 02:42:0a:00:00:54 brd ff:ff:ff:ff:ff:ff link-netnsid 0
172: eth1@if173: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default 
    link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 1

第二个工具,find_links.sh

#!/bin/bash 
DOCKER_NETNS_SCRIPT=./docker_netns.sh   
IFINDEX=$1  
if [[ -z $IFINDEX ]]; then  
    for namespace in $($DOCKER_NETNS_SCRIPT); do    
        printf "\e[1;31m%s: \e[0m\n" $namespace 
        $DOCKER_NETNS_SCRIPT $namespace ip -c -o link   
        printf "\n" 
    done    
else    
    for namespace in $($DOCKER_NETNS_SCRIPT); do    
        if $DOCKER_NETNS_SCRIPT $namespace ip -c -o link | grep -Pq "^$IFINDEX: "; then 
            printf "\e[1;31m%s: \e[0m\n" $namespace 
            $DOCKER_NETNS_SCRIPT $namespace ip -c -o link | grep -P "^$IFINDEX: ";  
            printf "\n" 
        fi  
    done    
fi

这个脚本可以根据ifindex查找接口所在的namespace。

$ sudo ./find_links.sh 60
1-hxyiridl2b: 
60: veth1@if59: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UP mode DEFAULT group default \    link/ether 4a:0a:52:98:84:a7 brd ff:ff:ff:ff:ff:ff link-netnsid 2

网络结构分析

以下,我们通过实验,了解一下overlay网络与docker_gwbridge网络。

我们现在在两个nodes上都创建容器:

$ docker run -d --name busybox --net overlay_test busybox sleep 36000

在容器的环境下,查看一下网络连接:

docker exec busybox ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    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
59: eth0@if60: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue 
    link/ether 02:42:0a:c8:00:02 brd ff:ff:ff:ff:ff:ff
    inet 10.200.0.2/16 brd 10.200.255.255 scope global eth0
       valid_lft forever preferred_lft forever
61: eth1@if62: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.3/16 brd 172.18.255.255 scope global eth1
       valid_lft forever preferred_lft forever

我们发现,除了回环口外,还有两个接口。10.200.0.2/16即是容器busybox在overlay_test网络上的接口的IP地址。172.18.0.3/16是容器busybox在docker_gwbridge网络上的接口的IP地址。

到目前为止,我们看到的容器网络是这样的。我们只看到了网络地址,还不知道它们间的报文是如何交互的。(192.168.154.2是宿主机的网关)

step 0

南北向流量

我们尝试从容器内跟踪访问外部IP的路由

$ docker exec busybox traceroute baidu.com
traceroute to baidu.com (220.181.38.148), 30 hops max, 46 byte packets
 1  bogon (172.18.0.1)  0.003 ms  0.004 ms  0.006 ms
 2  bogon (192.168.154.2)  0.148 ms  0.330 ms  0.175 ms
 ...

可见,流量经过172.18.0.1,然后访问到宿主机网关上。

接下来,我们尝试解析出内部网络连接。上面我们已经得知,从容器内部的视角,172.18.0.3所在的接口为:61: eth1@if62。我们可以理解为,此接口的ifindex为61,通过veth连接到ifindex为62的接口上。

我们查找看看62接口的namespace是:

$ sudo ./find_links.sh 62

居然没有显示。这就说明62接口是在宿主机的主namespace中的。我们在宿主机上看看:

$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    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
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:e5:66:45 brd ff:ff:ff:ff:ff:ff
    inet 192.168.154.135/24 brd 192.168.154.255 scope global dynamic noprefixroute ens33
       valid_lft 1502sec preferred_lft 1502sec
    inet6 fe80::f378:1d3:6cde:69bb/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: docker_gwbridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:50:e9:2d:e1 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.18.255.255 scope global docker_gwbridge
       valid_lft forever preferred_lft forever
    inet6 fe80::42:50ff:fee9:2de1/64 scope link 
       valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:5d:cd:c3:16 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
23: veth6ee82c3@if22: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker_gwbridge state UP group default 
    link/ether 4a:71:4d:f7:0e:4e brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::4871:4dff:fef7:e4e/64 scope link 
       valid_lft forever preferred_lft forever
62: veth0204500@if61: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker_gwbridge state UP group default 
    link/ether 9e:d6:10:49:8e:42 brd ff:ff:ff:ff:ff:ff link-netnsid 4
    inet6 fe80::9cd6:10ff:fe49:8e42/64 scope link 
       valid_lft forever preferred_lft forever

可见,62接口的master是docker_gwbridge。也就是说,62接口被桥接到docker_gwbridge中。

南北向流量在经过宿主机出口时,还做了NAT转换

$ sudo iptables-save -t nat  | grep -- '-A POSTROUTING'
-A POSTROUTING -o docker_gwbridge -m addrtype --src-type LOCAL -j MASQUERADE
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 172.18.0.0/16 ! -o docker_gwbridge -j MASQUERAD

于是,南北向的流量走向就很清晰了。我们的网络拓扑可以更新为:

step 1

东西向流量

东西向流量即容器与容器间的流量。我们先测试一下容器间的连通性。

$ docker exec busybox ping 10.200.0.2
PING 10.200.0.2 (10.200.0.2): 56 data bytes
64 bytes from 10.200.0.2: seq=0 ttl=64 time=41.177 ms
64 bytes from 10.200.0.2: seq=1 ttl=64 time=1.181 ms
64 bytes from 10.200.0.2: seq=2 ttl=64 time=1.110 ms

接下来探索这个流量是怎么走的。我们再看一下容器中的网络配置。

$ docker exec busybox ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    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
59: eth0@if60: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue 
    link/ether 02:42:0a:c8:00:02 brd ff:ff:ff:ff:ff:ff
    inet 10.200.0.2/16 brd 10.200.255.255 scope global eth0
       valid_lft forever preferred_lft forever
61: eth1@if62: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.3/16 brd 172.18.255.255 scope global eth1
       valid_lft forever preferred_lft forever

10.200.0.2所在的接口为,59: eth0@if60。即本接口ifindex为59,连接到ifindex为60的接口上。我们查询一下60接口所在的namespaec。

$ sudo ./find_links.sh 60
1-hxyiridl2b: 
60: veth1@if59: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UP mode DEFAULT group default \    link/ether 4a:0a:52:98:84:a7 brd ff:ff:ff:ff:ff:ff link-netnsid 2

可见60接口处于1-hxyiridl2b这一namespace中。

$ sudo ./docker_netns.sh 1-hxyiridl2b ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    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
2: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default 
    link/ether 0e:2d:34:e6:eb:b7 brd ff:ff:ff:ff:ff:ff
    inet 10.200.0.1/16 brd 10.200.255.255 scope global br0
       valid_lft forever preferred_lft forever
56: vxlan0@if56: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UNKNOWN group default 
    link/ether 0e:2d:34:e6:eb:b7 brd ff:ff:ff:ff:ff:ff link-netnsid 0
58: veth0@if57: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UP group default 
    link/ether ea:c1:db:d4:b1:83 brd ff:ff:ff:ff:ff:ff link-netnsid 1
60: veth1@if59: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UP group default 
    link/ether 4a:0a:52:98:84:a7 brd ff:ff:ff:ff:ff:ff link-netnsid 2

在这个namespace中,有一个vxlan出口。docker overlsy就是通过overlay隧道与其它容器通信的。

两个容器虽然是通过vxlan隧道通信,但容器内部却不感知。它们只能看到两个容器处于同一个二层网络中。由vxlan接口将二层报文封装在UDP报文的payload中,发到对端,再由对端的vxlan接口解封装。

我们查看一下namespace 1-hxyiridl2b中的arp地址表:

$ sudo ./docker_netns.sh 1-hxyiridl2b ip neigh
10.200.0.5 dev vxlan0 lladdr 02:42:0a:c8:00:05 PERMANENT
10.200.0.4 dev vxlan0 lladdr 02:42:0a:c8:00:04 PERMANENT

我们可以看到,远端node中的容器IP 10.200.0.4,有体现在本端的arp地址表中。即是通过查找此表,得到对端的二层地址。

我们再来看看,vxlan报文的出口在哪里:

$ sudo ./docker_netns.sh 1-hxyiridl2b bridge fdb
...
02:42:0a:c8:00:05 dev vxlan0 dst 192.168.154.136 link-netnsid 0 self permanent
02:42:0a:c8:00:04 dev vxlan0 dst 192.168.154.136 link-netnsid 0 self permanent
...

这可以理解为VxLAN的VTEP表,即根据MAC地址,查找出VxLAN报文应该封装的外层IP,是192.168.154.136

我们可以画出东西向流量的完整的拓扑了:

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

推荐阅读更多精彩内容