概述

    大家都知道,通过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 跨主机网络通讯 flannel+etcd_ip地址_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)