Docker跨主机容器间网络通信实现的工具有Pipework、Flannel、Weave、Open vSwitch(虚拟交换机)、Calico实现跨主机容器间的通信。其中Pipework、Weave、Flannel,三者的区别是:
Weave的思路
1
2
|
在每个宿主机上布置一个特殊的route的容器,不同宿主机的route容器连接起来。 route拦截所有普通容器的ip请求,并通过udp包发送到其他宿主机上的普通容器。
这样在跨机的多个容器端看到的就是同一个扁平网络。 weave解决了网络问题,不过部署依然是单机的。
|
Flannel的思路
1
2
3
4
5
6
7
8
9
10
11
12
13
|
Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。但在默认的Docker配置中,
每个节点上的Docker服务会分别负责所在节点容器的IP分配。这样导致的一个问题是,不同节点上容器可能获得相同的内外IP地址。并使这些容器之间能够之间通过IP地址相互找到,也就是相
互
ping
通。
Flannel的设计目的就是为集群中的所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得
"同属一个内网"
且
"不重复的"
IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。
Flannel实质上是一种
"覆盖网络(overlay network)"
,即表示运行在一个网上的网(应用层网络),并不依靠ip地址来传递消息,而是采用一种映射机制,把ip地址和identifiers做映射来资源定位。也就
是将TCP数据包装在另一种网络包里面进行路由转发和通信,目前已经支持UDP、VxLAN、AWS VPC和GCE路由等数据转发方式。
原理是每个主机配置一个ip段和子网个数。例如,可以配置一个覆盖网络使用 10.100.0.0
/16
段,每个主机
/24
个子网。因此主机a可以接受10.100.5.0
/24
,主机B可以接受10.100.18.0
/24
的包。flannel使用etcd来维护分配的子网到实际的ip地址之间的映射。对于数据路径,flannel 使用udp来封装ip数据报,转发到远程主机。选择UDP作为转发协议是因为他能穿透防火墙。例如,AWS Classic无法转发IPoIP or GRE 网络包,是因为它的安全组仅仅支持TCP
/UDP/ICMP
。
flannel 使用etcd存储配置数据和子网分配信息。flannel 启动之后,后台进程首先检索配置和正在使用的子网列表,然后选择一个可用的子网,然后尝试去注册它。
etcd也存储这个每个主机对应的ip。flannel 使用etcd的
watch
机制监视
/coreos
.com
/network/subnets
下面所有元素的变化信息,并且根据他来维护一个路由表。为了提高性能,flannel优化了Universal TAP
/TUN
设备,对TUN和UDP之间的ip分片做了代理。
|
默认的节点间数据通信方式是UDP转发.在Flannel的GitHub页面有如下的一张原理图:
对上图的简单解释:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
1)数据从源容器中发出后,经由所在主机的docker0虚拟网卡转发到flannel0虚拟网卡,这是个P2P的虚拟网卡,flanneld服务监听在网卡的另外一端。
2)Flannel通过Etcd服务维护了一张节点间的路由表,在稍后的配置部分我们会介绍其中的内容。
3)源主机的flanneld服务将原本的数据内容UDP封装后根据自己的路由表投递给目的节点的flanneld服务,数据到达以后被解包,然后直接进入目的节点的flannel0虚拟网卡,
然后被转发到目的主机的docker0虚拟网卡,最后就像本机容器通信一下的有docker0路由到达目标容器。
这样整个数据包的传递就完成了,这里需要解释三个问题:
1)UDP封装是怎么回事?
在UDP的数据内容部分其实是另一个ICMP(也就是
ping
命令)的数据包。原始数据是在起始节点的Flannel服务上进行UDP封装的,投递到目的节点后就被另一端的Flannel服务
还原成了原始的数据包,两边的Docker服务都感觉不到这个过程的存在。
2)为什么每个节点上的Docker会使用不同的IP地址段?
这个事情看起来很诡异,但真相十分简单。其实只是单纯的因为Flannel通过Etcd分配了每个节点可用的IP地址段后,偷偷的修改了Docker的启动参数。
在运行了Flannel服务的节点上可以查看到Docker服务进程运行参数(
ps
aux|
grep
docker|
grep
"bip"
),例如“--bip=182.48.25.1
/24
”这个参数,它限制了所在节
点容器获得的IP范围。这个IP范围是由Flannel自动分配的,由Flannel通过保存在Etcd服务中的记录确保它们不会重复。
3)为什么在发送节点上的数据会从docker0路由到flannel0虚拟网卡,在目的节点会从flannel0路由到docker0虚拟网卡?
例如现在有一个数据包要从IP为172.17.18.2的容器发到IP为172.17.46.2的容器。根据数据发送节点的路由表,它只与172.17.0.0
/16
匹配这条记录匹配,因此数据从docker0
出来以后就被投递到了flannel0。同理在目标节点,由于投递的地址是一个容器,因此目的地址一定会落在docker0对于的172.17.46.0
/24
这个记录上,自然的被投递到了docker0网卡。
|
Pipework的思路
1
2
3
4
5
|
pipework是一个单机的工具,组合了brctl等工具,可以认为pipework解决的是宿主机上的设置容器的虚拟网卡、网桥、ip等,可以配合其他网络使用。详情可参考:Docker网络详解及pipework源码解读与实践
如果容器数量不多,想简单的组一个大的3层网络,可以考虑weave
如果容器数量很多,而且你们的环境复杂,需要多个子网,可以考虑
open
vswitch或者fannel
weave的总体网络性能表现欠佳, flannel VXLAN 能满足要求,一般推荐用flannel
|
------------------------------------------------------------------------------------------------------------------------
Flannel环境部署记录
1)机器环境(CentOS7系统)
1
2
|
10.10.172.201 部署etcd,flannel,docker 主机名:node1 主控端(通过etcd)
10.10.172.202 部署flannel,docker 主机名:node2 被控端
|
2)node1(10.10.172.201)机器操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
设置主机名及绑定hosts
[root@node1 ~]
# hostnamectl --static set-hostname node1
[root@node1 ~]
# vim /etc/hosts
10.10.172.201 node1
10.10.172.201 etcd
10.10.172.202 node2
关闭防火墙,如果开启防火墙,则最好打开2379和4001端口
[root@node1 ~]
# systemctl disable firewalld.service
[root@node1 ~]
# systemctl stop firewalld.service
先安装docker环境
[root@node1 ~]
# yum install docker -y
安装etcd
k8s运行依赖etcd,需要先部署etcd,下面采用yum方式安装:
[root@node1 ~]
# yum install etcd -y
yum安装的etcd默认配置文件在
/etc/etcd/etcd
.conf,编辑配置文件:
[root@node1 ~]
# cp /etc/etcd/etcd.conf /etc/etcd/etcd.conf.bak
[root@node1 ~]
# cat /etc/etcd/etcd.conf
#[member]
ETCD_NAME=master
#节点名称
ETCD_DATA_DIR=
"/var/lib/etcd/default.etcd"
#数据存放位置
#ETCD_WAL_DIR=""
#ETCD_SNAPSHOT_COUNT="10000"
#ETCD_HEARTBEAT_INTERVAL="100"
#ETCD_ELECTION_TIMEOUT="1000"
#ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS=
"http://0.0.0.0:2379,http://0.0.0.0:4001"
#监听客户端地址
#ETCD_MAX_SNAPSHOTS="5"
#ETCD_MAX_WALS="5"
#ETCD_CORS=""
#
#[cluster]
#ETCD_INITIAL_ADVERTISE_PEER_URLS="http://localhost:2380"
# if you use different ETCD_NAME (e.g. test), set ETCD_INITIAL_CLUSTER value for this name, i.e. "test=http://..."
#ETCD_INITIAL_CLUSTER="default=http://localhost:2380"
#ETCD_INITIAL_CLUSTER_STATE="new"
#ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_ADVERTISE_CLIENT_URLS=
"http://etcd:2379,http://etcd:4001"
#通知客户端地址
#ETCD_DISCOVERY=""
#ETCD_DISCOVERY_SRV=""
#ETCD_DISCOVERY_FALLBACK="proxy"
#ETCD_DISCOVERY_PROXY=""
启动etcd并验证状态
[root@node1 ~]
# systemctl start etcd
[root@node1 ~]
# systemctl enable etcd
[root@node1 ~]
# ps -ef|grep etcd
etcd 15148 1 0 13:10 ? 00:00:00
/usr/bin/etcd
--name=master
#节点名称 --data-dir=/var/lib/etcd/default.etcd#数据存放位置 --listen-client-urls=http://0.0.0.0:2379,http://0.0.0.0:4001#?听客户端地址
root 15506 15419 0 13:11 pts
/0
00:00:00
grep
--color=auto etcd
[root@node1 ~]
# lsof -i:2379
COMMAND PID USER FD TYPE DEVICE SIZE
/OFF
NODE NAME
etcd 15148 etcd 6u IPv6 48911 0t0 TCP *:2379 (LISTEN)
etcd 15148 etcd 11u IPv4 48055 0t0 TCP localhost:44959->localhost:2379 (ESTABLISHED)
etcd 15148 etcd 15u IPv6 48061 0t0 TCP localhost:2379->localhost:44959 (ESTABLISHED)
[root@node1 ~]
# etcdctl set testdir/testkey0 0
0
[root@node1 ~]
# etcdctl get testdir/testkey0
0
[root@node1 ~]
# etcdctl -C http://etcd:4001 cluster-health
member 8e9e05c52164694d is healthy: got healthy result from http:
//etcd
:2379
cluster is healthy
[root@node1 ~]
# etcdctl -C http://etcd:2379 cluster-health
member 8e9e05c52164694d is healthy: got healthy result from http:
//etcd
:2379
cluster is healthy
安装覆盖网络Flannel
[root@node1 ~]
# yum install flannel -y
配置Flannel
[root@node1 ~]
# cp /etc/sysconfig/flanneld /etc/sysconfig/flanneld.bak
[root@node1 ~]
# vim /etc/sysconfig/flanneld
# Flanneld configuration options
# etcd url location. Point this to the server where etcd runs
FLANNEL_ETCD_ENDPOINTS=
"http://etcd:2379"
# etcd config key. This is the configuration key that flannel queries
# For address range assignment
FLANNEL_ETCD_PREFIX=
"/atomic.io/network"
# Any additional options that you want to pass
#FLANNEL_OPTIONS=""
配置etcd中关于flannel的key(这个只在安装了etcd的机器上操作)
Flannel使用Etcd进行配置,来保证多个Flannel实例之间的配置一致性,所以需要在etcd上进行如下配置(
'/atomic.io/network/config'
这个key与上文
/etc/sysconfig/flannel
中的配置项FLANNEL_ETCD_PREFIX是相对应的,错误的话启动就会出错):
[root@node1 ~]
# etcdctl mk /atomic.io/network/config '{ "Network": "10.10.0.0/16" }'
{
"Network"
:
"10.10.0.0/16"
}
温馨提示:上面flannel设置的ip网段可以任意设定,随便设定一个网段都可以。容器的ip就是根据这个网段进行自动分配的,ip分配后,容器一般是可以对外联网的(网桥模式,只要宿主机能上网就可以)
启动Flannel
[root@node1 ~]
# systemctl enable flanneld.service
[root@node1 ~]
# systemctl start flanneld.service
[root@node1 ~]
# ps -ef|grep flannel
root 15730 1 0 13:16 ? 00:00:00
/usr/bin/flanneld
-etcd-endpoints=http:
//etcd
:2379 -etcd-prefix=
/atomic
.io
/network
root 15873 15419 0 13:16 pts
/0
00:00:00
grep
--color=auto flannel
启动Flannel后,一定要记得重启docker,这样Flannel配置分配的ip才能生效,即docker0虚拟网卡的ip会变成上面flannel设定的ip段
[root@node1 ~]
# systemctl restart docker
[root@node1 ~]
# systemctl enable docker
|
3)node2(10.10.172.202)机器操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
设置主机名及绑定hosts
[root@node2 ~]
# hostnamectl --static set-hostname node2
[root@node2 ~]
# vim /etc/hosts
10.10.172.201 node1
10.10.172.201 etcd
10.10.172.202 node2
关闭防火墙,如果开启防火墙,则最好打开2379和4001端口
[root@node2 ~]
# systemctl disable firewalld.service
[root@node2 ~]
# systemctl stop firewalld.service
先安装docker环境
[root@node2 ~]
# yum install docker -y
安装覆盖网络Flannel
[root@node2 ~]
# yum install flannel -y
配置Flannel
[root@node2 ~]
# cp /etc/sysconfig/flanneld /etc/sysconfig/flanneld.bak
[root@node2 ~]
# vim /etc/sysconfig/flanneld
# Flanneld configuration options
# etcd url location. Point this to the server where etcd runs
FLANNEL_ETCD_ENDPOINTS=
"http://etcd:2379"
# etcd config key. This is the configuration key that flannel queries
# For address range assignment
FLANNEL_ETCD_PREFIX=
"/atomic.io/network"
# Any additional options that you want to pass
#FLANNEL_OPTIONS=""
启动Flannel
[root@node2 ~]
# systemctl enable flanneld.service
[root@node2 ~]
# systemctl start flanneld.service
[root@node2 ~]
# ps -ef|grep flannel
root 31765 1 0 13:20 ? 00:00:00
/usr/bin/flanneld
-etcd-endpoints=http:
//etcd
:2379 -etcd-prefix=
/atomic
.io
/network
root 31814 31576 0 13:20 pts
/0
00:00:00
grep
--color=auto flannel
启动Flannel后,一定要记得重启docker,这样Flannel配置分配的ip才能生效,即docker0虚拟网卡的ip会变成上面flannel设定的ip段
[root@node2 ~]
# systemctl restart docker
[root@node2 ~]
# systemctl enable docker
|
4)创建容器,验证跨主机容器之间的网络联通性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
首先在node1(10.10.172.201)上容器容器,如下,登陆容器发现已经按照上面flannel配置的分配了一个ip段(每个宿主机都会分配一个182.48.0.0
/16
的网段)
[root@node1 ~]
# docker run -ti -d --name=node1.test docker.io/nginx /bin/bash
2dea24b1562f3d4dc89cde3fa00c707276edcfba1c8f3b0ab4c62e075e63de2f
[root@node1 ~]
# docker exec -ti node1.test /bin/bash
root@2dea24b1562f:/
# apt-get update && apt-get install iproute iputils-ping -y
root@2dea24b1562f:/
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
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
7: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default
link
/ether
02:42:0a:0a:5c:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.10.92.2
/24
scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:aff:fe0a:5c02
/64
scope link
valid_lft forever preferred_lft forever
接着在node2(10.10.172.202)上容器容器
[root@node2 ~]
# docker run -ti -d --name=node2.test docker.io/nginx /bin/bash
374417e9ce5bcd11222d8138e684707c00ca8a4b7a16243ac5cfb1a8bd19ee6e
[root@node2 ~]
# docker exec -ti node2.test /bin/bash
root@374417e9ce5b:/
# cat /etc/debian_version
9.3
root@374417e9ce5b:/
# apt-get update && apt-get install iproute iputils-ping -y
root@374417e9ce5b:/
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
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
469: eth0@if470: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default
link
/ether
02:42:0a:0a:1d:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.10.29.2
/24
scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:aff:fe0a:1d02
/64
scope link
valid_lft forever preferred_lft forever
root@374417e9ce5b:/
# ping 10.10.92.2
PING 10.10.92.2 (10.10.92.2) 56(84) bytes of data.
64 bytes from 10.10.92.2: icmp_seq=1 ttl=60
time
=0.657 ms
64 bytes from 10.10.92.2: icmp_seq=2 ttl=60
time
=0.487 ms
.......
root@374417e9ce5b:/
# ping www.baidu.com
PING www.a.shifen.com (115.239.210.27) 56(84) bytes of data.
64 bytes from 115.239.210.27 (115.239.210.27): icmp_seq=1 ttl=48
time
=11.7 ms
64 bytes from 115.239.210.27 (115.239.210.27): icmp_seq=2 ttl=48
time
=12.4 ms
.......
发现,在两个宿主机的容器内,互相
ping
对方容器的ip,是可以
ping
通的!也可以直接连接外网(桥接模式)
查看两台宿主机的网卡信息,发现docker0虚拟网卡的ip(相当于容器的网关)也已经变成了flannel配置的ip段,并且多了flannel0的虚拟网卡信息
[root@node1 ~]
# ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1472
inet 10.10.92.1 netmask 255.255.255.0 broadcast 0.0.0.0
inet6 fe80::42:ebff:fe16:438d prefixlen 64 scopeid 0x20<link>
ether 02:42:eb:16:43:8d txqueuelen 0 (Ethernet)
RX packets 5944 bytes 337155 (329.2 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 6854 bytes 12310552 (11.7 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
|