概述

    大家都知道,通过docker-compose编排的一组docker容器,如果使用的同一个network,那么这些docker之间可以直接通过docker内部的IP进行通讯。

    然而在实际应用中,我们将很多服务拆开部署在不同的宿主机上也很实际,比如A主机部署了5个docker,B主机又部署了6个docker,这11个docker之间需要进行网络互相通讯。

    有同学会说,docker内部的服务我可以全部使用docker所在宿主机的IP地址进行通讯(每个docker映射端口到宿主机),这样虽然可行,但是提高了我们对服务的配置复杂度。如果你是微服务又使用了eureka这样的注册中心,你就会发现很头疼了(当然没有纯手工解决不了的问题,我们只想简单一点再方便一点)。

    有需要的地方就有发明就有创造,覆盖网络是现在网络应用的一大特点,其中包含了很多种软件方案。本文不再赘述,这里我们说一下其中的一个 flannel 是如何通过 etcd(基于key value的存储) 来实现这个需求目标的。

    flannel 网络借助 etcd 来实现一个全局的上帝来同步网络信息。

    flannel 网络不会创建新的 bridge,而是用默认的 docker0,但创建 flannel 网络会在主机上创建一个虚拟网卡,挂在 docker0 上,用于跨主机通信(你可以把 flannel 理解为一个路由)。

下面有两张来自网络的图片,可以清晰的表达了 docker、flannel、宿主机 之间的网络通讯和数据路由。

docker 跨主机网络通讯 flannel+etcd_docker

docker 跨主机网络通讯 flannel+etcd_docker_02

从上面两个图中可以很清晰的看出 flannel 所处的位置及担当的角色。

1、首先从宿主机A的一个docker容器中发出一个网络请求。

2、在同一个宿主机上的所有docker在同一个网段中,如果第1步的请求时同网段的,则自然没有什么问题。

3、如果第1步发出的网络请求不在当前docker所处网段,那么数据会被派发到docker0。

4、docker0 通过flannel 可以从 etcd 中找到目标服务所处的宿主机。

5、然后 flannel 通过当前所在的宿主机将数据包发给目标目标服务所处的宿主机。

6、目标宿主机收到请求后,然后一层一层向下路由到目标服务所在的docker容器。

7、然后完成数据处理后响应数据包(网络TCP请求时需要寻址,响应时是原路返回响应)

组件方式让 flannel 多了几分灵活性,它可以使用二层的 VxLAN 隧道来封装数据包完成跨主机通信,也可以使用纯三层的方案来通信,比如 host-gw,只需修改一个配置文件就可以完成转化。

当然,在线上环境,flannel 所依赖的 etcd 应当部署3个节点组成集群以确保高可用。

架构方案

1、三个etcd 组成集群保证高可用(为了方便,本文只安装一个etcd,集群也很简单几乎没有什么坑)

2、两台宿主机分别安装 flannel 和 docker(不管增加多少,都一样操作,两台已经可以完成演示)

操作步骤

一、安装etcd

关于 etcd 集群的安装和配置,可以参考:​​Docker 搭建 etcd 集群​

我们单机安装一个,执行如下命令:

# 主机执行如下命令(注意其中的IP地址为主机的宿主机IP地址 192.168.1.163,节点名称为 node1 也要注意修改,state 状态为new)
docker run -d -p 2379:2379 -p 2380:2380 \
--volume=/opt/CICD/etcd/etcd-data:/etcd-data \
--restart=always \
--name etcd1 quay.io/coreos/etcd \
/usr/local/bin/etcd --name node1 \
--data-dir=/etcd-data \
--advertise-client-urls http://192.168.1.163:2379 \
--listen-client-urls http://0.0.0.0:2379 \
--initial-advertise-peer-urls http://192.168.1.163:2380 \
--listen-peer-urls http://0.0.0.0:2380 \
--initial-cluster-token docker-etcd \
--initial-cluster "node1=http://192.168.1.163:2380" \
--initial-cluster-state new

执行命令 ​​curl -L http://127.0.0.1:2379/v2/members​​ 验证安装结果

[root@localhost etcd-data]# curl -L http://127.0.0.1:2379/v2/members
{"members":[{"id":"8e9e05c52164694d","name":"node1","peerURLs":["http://localhost:2380"],"clientURLs":["http://192.168.1.163:2379"]}]}
二、安装 flannel

主机A:192.168.1.163

主机B:192.168.1.164

flannel 的安装非常简单,直接下载二进制文件即可(当然您也可以自己编译)

打开网址 https://github.com/coreos/flannel/releases 下载最新版对应的架构的版本,一般使用 amd64(我的CentOS 7.6)

比如我的下载地址为: https://github.com/coreos/flannel/releases/download/v0.11.0/flannel-v0.11.0-linux-amd64.tar.gz

然后一顿命令操作如下:

wget https://github.com/coreos/flannel/releases/download/v0.11.0/flannel-v0.11.0-linux-amd64.tar.gz
tar -zxvf flannel-v0.11.0-linux-amd64.tar.gz

记下文件的位置,例如(/opt/flannel/flanneld)

添加一个flannel服务的System单元,简单的就可以。

#编辑文件
vi /usr/lib/systemd/system/flanneld.service
# 内容如下
[Unit]
Description=Flanneld overlay address etcd agent
After=network-online.target network.target
Before=docker.service

[Service]
Type=notify
EnvironmentFile=/etc/default/flanneld.conf
ExecStart=/opt/flannel/flanneld --ip-masq -etcd-endpoints=${FLANNEL_ETCD_ENDPOINTS} -etcd-prefix=${FLANNEL_ETCD_PREFIX} $FLANNEL_OPTIONS
ExecStartPost=/opt/flannel/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /etc/default/docker
Restart=on-failure

[Install]
WantedBy=multi-user.target

flanneld.service 中引用了1个配置文件,指向了1个配置,的内容如下(一个需要手工配置,一个是自动生成)

/etc/default/flanneld.conf 文件内容(手工配置)

# Flanneld configuration options  

# etcd url location. Point this to the server where etcd runs
FLANNEL_ETCD_ENDPOINTS="http://192.168.1.163:2379"

# etcd config key. This is the configuration key that flannel queries
# For address range assignment
FLANNEL_ETCD_PREFIX="/flannel/network"

# Any additional options that you want to pass
#FLANNEL_OPTIONS=""

然后编辑文件 ​​vim /usr/lib/systemd/system/docker.service​

找到 ExecStart,在前面添加一行 ​​EnvironmentFile=/etc/default/docker​

然后在 ExecStart 最后添加变量 ​​$DOCKER_NETWORK_OPTIONS​​ (注意其他的参数应该是docker在之前相关需要中添加的,你不要动,这个地方只需要添加这个即可)

示例如下:

(省略前面代码)
EnvironmentFile=/etc/default/docker
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry=192.168.1.163:81 $DOCKER_NETWORK_OPTIONS
(省略后面代码)

配置etcd中关于flannel的key(这个只在安装了etcd的机器上操作)

因为我的etcd是使用的docker,所以使用命令 ​​docker exec -it etcd1 /bin/sh​​ 进入容器后再操作。

​/flannel/network/config​​ 这个key与上文 ​​/etc/default/flanneld.conf​​ 中的配置项 ​​FLANNEL_ETCD_PREFIX​​ 是相对应的,错误的话启动 flanneld 服务就会出错!

操作命令示例(这个只需要操作一次即可,也就是说无论你有多少台 flannel 宿主机,只需要首次操作一次即可):

# etcdctl  mk /flannel/network/config '{"Network":"172.72.0.0/16", "SubnetMin": "172.72.1.0", "SubnetMax": "172.72.254.0"}'
{"Network":"172.72.0.0/16", "SubnetMin": "172.72.1.0", "SubnetMax": "172.72.254.0"}

一切就绪后,重启flannel和docker

systemctl daemon-reload
systemctl start flanneld
systemctl enable flanneld
systemctl restart docker

正常情况下,到 etcd上执行 ​​etcdctl ls /flannel/network/subnets​​ 能够看到 flannel 申请的网段

操作示例:

# etcdctl ls /flannel/network/subnets
/flannel/network/subnets/172.72.5.0-24

在每个 flannel 宿主机上,使用 ​​cat /run/flannel/subnet.env​​ 查看网段信息,自动生成的内容如下:

[root@localhost flannel]# cat /run/flannel/subnet.env
FLANNEL_NETWORK=172.72.0.0/16
FLANNEL_SUBNET=172.72.95.1/24
FLANNEL_MTU=1472
FLANNEL_IPMASQ=true

在每个 flannel 宿主机上,使用 ​​cat /etc/default/docker​​ 查看自动生成的 docker 的启动参数信息,自动生成的内容如下:

[root@localhost flannel]# cat /etc/default/docker 
DOCKER_OPT_BIP="--bip=172.72.95.1/24"
DOCKER_OPT_IPMASQ="--ip-masq=false"
DOCKER_OPT_MTU="--mtu=1472"
DOCKER_NETWORK_OPTIONS=" --bip=172.72.95.1/24 --ip-masq=false --mtu=1472"

同上操作在另外一个宿主机B:192.168.1.164(或更多宿主机)上进行 flannel 配置。

多个宿主机的 flannel 服务都启动后,到 etcd上执行 ​​etcdctl ls /flannel/network/subnets​​ 能够看到多个宿主机的 flannel 都分配了网段。

验证

1、在 flannel 宿主机上使用命令 ​​ifconfig​​ 查看 flannel0 和 docker0 网卡的IP网址在同一个子网,并且和文件 ​​/etc/default/docker​​ 中的子网也一致,即为OK。

例如:

docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
inet 172.72.10.1 netmask 255.255.255.0 broadcast 172.72.10.255
inet6 fe80::42:1eff:feb7:54f prefixlen 64 scopeid 0x20<link>
ether 02:42:1e:b7:05:4f txqueuelen 0 (Ethernet)
RX packets 27 bytes 1400 (1.3 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 31 bytes 2184 (2.1 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.164 netmask 255.255.255.0 broadcast 192.168.1.255
inet6 fe80::20c:29ff:fe41:47f5 prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:41:47:f5 txqueuelen 1000 (Ethernet)
RX packets 1108823 bytes 668271247 (637.3 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 319511 bytes 33087014 (31.5 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

flannel0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1472
inet 172.72.5.0 netmask 255.255.255.255 destination 172.72.5.0
inet6 fe80::4e7a:2372:11e4:f8d8 prefixlen 64 scopeid 0x20<link>
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 500 (UNSPEC)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2 bytes 96 (96.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

2、如果网络不对,可能是上面操作不当导致,处理起来很简单,按如下命令操作一遍即可

systemctl daemon-reload
systemctl restart flanneld
systemctl enable flanneld
systemctl restart docker

3、查看具体 docker 容器的IP地址的命令为 ​​docker inspect --format='{{.NetworkSettings.IPAddress}}' ID或NAMES​​,或者直接 ​​docker inspect ID或NAMES​​ 看详细信息。

4、在不同的 flannel 宿主机上,分别随便启动一个docker服务,在一个docker中ping另外一个docker的IP地址,即可进行验证,如果你 docker 有http服务,使用 ​​curl​​ 命令请求测试也一样。


(END)