官方文档:https://github.com/coreos/flannel

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

1. Docker跨主机容器间网络配置

   方式方式有:Pipework、Flannel、Weave、Open vSwitch(虚拟交换机)、Calico, 其中

   Pipework、Weave、Flannel是使用的最多。

   本文介绍的是Flannel

2. Flannel是什么

   1.Flannel是CoreOS提供用于解决Dokcer集群跨主机通讯的覆盖网络工具。它的主要思路是:预先留出一个网段,

     每个主机使用其中一部分,然后每个容器被分配不同的ip;让所有的容器认为大家在同一个直连的网络,

     底层通过UDP/VxLAN等进行报文的封装和转发。      

   2.Flannel实质上是一种覆盖网络(overlay network),即表示运行在一个网上的网(应用层网络),并不依靠ip地址来传递消息,

     而是采用一种映射机制,把ip地址和identifiers做映射来资源定位。也就是将TCP数据包装在另一种网络包里面进行路由转发和通信,

     目前已经支持UDP、VxLAN、AWS VPC和GCE路由等数据转发方式

     注1:Flannel支持多种Backend协议,但是不支持运行时修改Backend。官方推荐使用以下Backend:

          VXLAN,性能损耗大概在20~30%;

          host-gw, 性能损耗大概10%,要求Host之间二层直连,因此只适用于小集群;

          UDP, 建议只用于debug,因为性能烂到家了,如果网卡支持 enable udp offload,直接由网卡进行拆包解包,性能还是很棒的。

          AliVPC。

   3.Flannel使用etcd存储配置数据和子网分配信息。flannel 启动之后,后台进程首先检索配置和正在使用的子网列表,

     然后选择一个可用的子网,然后尝试去注册它。etcd也存储这个每个主机对应的ip。flannel使用etcd的watch机制

     监视/coreos.com/network/subnets下面所有元素的变化信息,并且根据它来维护一个路由表。  

     注1:etcd可以理解成springcloud中的注册中心,用于服务注册和发现,这里是节点(即宿主机)注册和发现

   小结:Flannel是一种覆盖网络(overlay network),

         使用etcd存储配置数据和子网分配信息

         通过UDP/VxLAN等进行报文的封装和转发,建议使用VxLAN性能更好

3. 情景描述

   某公司有一个简单的集群网络,在该集群内,有两台服务器甲和乙,每台服务器上都有两张网卡,分别连接公网和私网,

   两台服务器可以通过私网互联,在两个服务器节点上分别安装了Docker,并且运行了A/B/C/D 4个容器。

   每台服务器节点上都有一个docker0网桥,这是docker启动后初始化的虚拟设备,每个容器都与docker0网桥连接,

   并且,容器的IP由docker自动分配

   问题分析

   1.images/01 跨主机容器互联-A.png

     默认情况下此网络设置不支持跨主机的容器互联,原因有两方面:

     1.跨主机访问容器,没有有效路由

     2.多个节点上的容器网段冲突,docker启动后初始化docker0网桥时,会随机分配一个IP段,那么,如果不加以协调,

       多个节点内的容器网络有可能会冲突,容器C和D的IP都是“192.168.1.3”

   2.images/01 跨主机容器互联-B.png

4. Flannel实现的容器的跨主机通信通过如下过程实现

   1.每个主机上安装并运行etcd和flannel;

   2.在etcd中规划配置所有主机的docker0子网范围

   3.每个主机上的flanneld根据etcd中的配置,为本主机的docker0分配子网,保证所有主机上的docker0网段不重复,

     并将结果(即本主机上的docker0子网信息和本主机IP的对应关系)存入etcd库中,这样etcd库中就保存了所有主机

     上的docker子网信息和本主机IP的对应关系;

   4.当需要与其他主机上的容器进行通信时,查找etcd数据库,找到目的容器的子网所对应的outip(目的宿主机的IP);

   5.将原始数据包封装在VXLAN或UDP数据包中,IP层以outip为目的IP进行封装;

   6.由于目的IP是宿主机IP,因此路由是可达的;

   7.VXLAN或UDP数据包到达目的宿主机解封装,解出原始数据包,最终到达目的容器。

   注1:flannel默认使用8285端口作为UDP封装报文的端口,VxLan使用8472端口

   注2:Flannel工作流程可参考“images/02 Flannel模式.png”

5. Flannel的工作原理

   images/02 Flannel工作原理.png

   1.数据从源容器中发出后,经由所在主机的docker0虚拟网卡转发到flannel0虚拟网卡,

     这是个P2P的虚拟网卡,flanneld服务监听在网卡的另外一端

   2.Flannel通过Etcd服务(重点)维护了一张节点间的路由表,该张表里保存了各个节点主机的子网网段信息

     flannel默认使用etcd作为配置和协调中心,首先使用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为10.1.15.2的容器发到IP为10.1.20.3的容器。根据数据发送节点的路由表,

     它只与10.1.0.0/16匹配这条记录匹配,因此数据从docker0出来以后就被投递到了flannel0。

     同理在目标节点,由于投递的地址是一个容器,因此目的地址一定会落在docker0对于的10.1.20.1/24这个记录上,

     自然的被投递到了docker0网卡。

6. Flannel安装配置

   0.机器环境(CentOS7)

     一共三台机器:一个etcd集群,三台机器安装flannel和Docker

     节点名称            IP地址(根据自己机器变化)    软件环境

     node-0             192.168.183.161                 etcd、flannel、docker

     node-1             192.168.183.162                 etcd、flannel、docker

     node-2             192.168.183.163                 etcd、flannel、docker

     注1:通常按照需求将集群节点部署为3,5,7,9个节点。这里能选择偶数个节点吗?最好不要这样。原因有二:

          1.偶数个节点集群不可用风险更高,表现在选主过程中,有较大概率或等额选票,从而触发下一轮选举

          2.偶数个节点集群在某些网络分割的场景下无法正常工作。当网络分割发生后,将集群节点对半分割开。

            此时集群将无法工作。按照RAFT协议,此时集群写操作无法使得大多数节点同意,从而导致写失败,集群无法正常工作

   ###################################################

   ##### 以下均为node-0机器上的操作  ########  

   ###################################################

   1.docker安装与配置(略)

   2.etcd集群安装与配置(略)

     注1:ETCD3.4版本中ETCDCTL_API=3和etcd --enable-v2=false成为了默认配置

          flannel操作etcd使用的是v2的API,而kubernetes操作etcd使用的v3的API

          为了兼容flannel,将默认开启v2版本,故配置文件中还要设置

          ETCD_ENABLE_V2="true"

          经实践:ETCD_ENABLE_V2无效,flannel连接不上etcd,最后不得不修改环境变量(之后再考虑兼容性问题)

          vi /etc/profile

          export ETCDCTL_API=2 # 指定etcdctl命令的版本为v2

          source /etc/profile  重启

     注2:etcdctl2和etcdctl3是不兼容的,两者的api参数也不一样,最最关键的是“v2/v3的数据是不互通的”

          “v2/v3的数据是不互通的”~~~

          “v2/v3的数据是不互通的”~~~

          “v2/v3的数据是不互通的”~~~

          api 2 使用方法

          ETCDCTL_API=2 etcdctl set key value

          ETCDCTL_API=2 etcdctl ls /

          ETCDCTL_API=2 etcdctl del / --prefix

          api 3 使用方法                  

          ETCDCTL_API=3 etcdctl put key value

          ETCDCTL_API=3 etcdctl get /

          ETCDCTL_API=3 etcdctl del / --prefix

     注3:是不是要用v2的命令保存数据,flannel才能通过v2命令获得数据

   3.向etcd注册网段,供flanneld使用

     错误:“failed to retrieve network config: 100: Key not found (/coreos.com)”

     这里有个巨大的坑:etcd-v3.4.3中,虽然开启了兼容模式,但v2/v3命令保存的数据是不互通的,

     所以要使用v2的set命令而非v3的put命令保存数据,因为etcd开启了兼容模式,所以在命令前面添加

     ETCDCTL_API=2,切换到v2版本的命令即可  

     “数据是不互通的”这句话是重点啊~~~4天时间…-_-

     “数据是不互通的”这句话是重点啊~~~

     “数据是不互通的”这句话是重点啊~~~

     ##1.使用v2版的set(而非put)命令向ETCD中保存flannel覆盖网络信息

     ETCDCTL_API=2 etcdctl --endpoints "http://192.168.106.128:2379,http://192.168.106.132:2379" \

       set /coreos.com/network/config '{"NetWork":"10.0.0.0/16","SubnetMin": "10.0.1.0", "SubnetMax": "10.0.20.0","Backend": {"Type": "vxlan"}}'

     ##2.v2命令获得网络信息9哦

     ETCDCTL_API=2 etcdctl --endpoints "http://192.168.106.128:2379,http://192.168.106.132:2379" get /coreos.com/network/config

     注1:etcd保存的网络参数说明

          '{"Network": "10.0.0.0/16", "SubnetLen": 24, "SubnetMin": "10.0.1.0","SubnetMax": "10.0.20.0", "Backend": {"Type": "vxlan"}}'

          10.0.0.0/16:请参考附录三

          Network: 用于指定Flannel地址池, 整个overlay(覆盖)网络为10.0.0.0/16网段.

          SubnetLen: 用于指定分配给单个宿主机的docker0的ip段的子网掩码的长度,默认值也是24

          SubnetMin: 用于指定最小能够分配的ip段

          SudbnetMax: 用于指定最大能够分配的ip段,在上面的示例中,表示每个宿主机可以分配一个24位掩码长度的子网,

            可以分配的子网从10.0.1.0/24到10.0.20.0/24,也就意味着在这个网段中,最多只能有20台宿主机

          Backend: 用于指定数据包以什么方式转发,默认为udp模式, 这里使用的是vxlan模式.因为为vxlan比起预设的udp性能相对好一些  

     注2:如何知道v2/v3命令保存的数据是不兼容的呢

          ## v3命令查询,无数据

          etcdctl --endpoints "http://192.168.199.160:2379,http://192.168.199.157:2379,http://192.168.199.158:2379" get /coreos.com/network/config

          ## v2命令查询,有数据

          ETCDCTL_API=2 etcdctl --endpoints "http://192.168.199.160:2379,http://192.168.199.157:2379,http://192.168.199.158:2379" get /coreos.com/network/config

     注3:flannel访问etcd时使用的key默认为:/coreos.com/network,可修改,但必须重新配置flanneld.conf的参数etcd-prefix,

          例如:-etcd-prefix=/zking.com/network    

   4.安装flannel

     ##1.下载或rz上传flannel安装包

     cd /usr/local/mytools

     rz

     ##2.创建flannel安装目录

     mkdir -p /opt/flannel

     ##3.解压安装文件至指定目录

     tar xzf flannel-v0.11.0-linux-amd64.tar.gz -C /opt/flannel

     ##4.查看解压后文件:主要有flanneld、mk-docker-opts.sh这两个文件,其中flanneld为主要的执行文件,sh脚本用于生成Docker启动参数

     cd /opt/flannel && ls

   5.为flannel创建一个systemd服务,用于后台启动

     vim /etc/systemd/system/flanneld.service

     ## 详情见:资料/flanneld-v1.1.0.service

     1.启动脚本关键参数:

     ##1.flannel服务需要先于Docker启动,后于etcd启动

     After=etcd.service

     Before=docker.service

     ##2.ExecStart即flanneld启动程序位置

     ExecStart=/opt/flannel/flanneld

     ##3.--etcd-endpoints参数为ectd集群客户端地址

     --etcd-endpoints="http://192.168.199.160:2379,http://http://192.168.199.157:2379,http://http://192.168.199.158:2379" \

     ##4.--iface参数为要绑定的网卡的网卡名(或IP地址)请根据实际情况修改,注意:IP或网卡名都可用ifconfig命令获得

     --iface=ens33

    2.flannel启动过程解析

      flannel服务需要先于Docker启动。flannel服务启动时主要做了以下几步的工作:

      1.从etcd中获取network的配置信息

      2.划分subnet,并在etcd中进行注册

      3.将子网信息记录到/run/flannel/subnet.env中

   6.flanneld服务启用/自启/停止/重启

     systemctl daemon-reload

     systemctl enable flanneld

     systemctl start flanneld

     systemctl status flanneld

     systemctl stop flanneld

     systemctl restart flanneld   

     调试相关命令:

     cd /var/lib/etcd && rm -rf *

     systemctl daemon-reload && systemctl restart flanneld

     如果启动失败,可通过如下命令查看原因

     ## 显示概要

     systemctl status flanneld.service

     ## 可以查看启动参数

     ps -ef |grep flanneld

     ## 查看启动详情

     journalctl -xe

     ## 显示实时日志

     journalctl -f

     ## 查看本机监听端口

     netstat -tunlp|grep etcd

   7.验证flannel网络

     1.flanneld服务启动成功后,使用ifconfig命令查看系统,会发现系统多了一个虚拟网卡“flannel.1”

       注:“flannel.1”为生成的虚拟网卡名,此名字不固定,以实际情况为准

     2.在etcd集群的任一节点上都可以查看到子网IP

       ETCDCTL_API=2 etcdctl --endpoints "http://127.0.0.1:2379" ls /coreos.com/network/subnets

     3.查看“flannel.1”的网络详细情况

       ifconfig flannel.1

       可以看到flannel.1网卡的地址和etcd中存储的地址一样,这样flannel网络配置完成

   8.配置Docker

     在各个节点安装好Docker,然后更改Docker的启动参数,使其能够使用flannel进行IP分配,以及网络通讯

     1.查看flannel分配的网络参数

       ##在Flannel运行之后,会生成一个环境变量文件,包含了当前主机要使用flannel通讯的相关参数

       cat /run/flannel/subnet.env

       ##控制台显示如下内容

       FLANNEL_NETWORK=10.0.0.0/16     ##10.0.1~20.1~254(0和255是特殊值不能用)

       FLANNEL_SUBNET=10.0.14.1/24     ##10.0.14(不一定是14,可以在1~20之间).1~254

       FLANNEL_MTU=1450

       FLANNEL_IPMASQ=true

     2.创建Docker运行参数

       ##使用flannel提供的脚本将subnet.env转写成Docker启动参数

       /opt/flannel/mk-docker-opts.sh -d /run/flannel/docker_opts.env -c

       ##创建好的启动参数位于/run/flannel/docker_opts.env文件中,可使用cat命令查看

       cat /run/flannel/docker_opts.env

       注1:网上很多将docker_opts.env保存到/run目录下,run目录下重启系统后docker_opts.env文件会自动消失,引发错误

            所以改到/run/flannel目录下

    4.修改Docker启动参数

       ##编辑 systemd service 配置文件

       vim /lib/systemd/system/docker.service

       ##下面是docker.service要修改的2个地方

       ##1.[Service]节中,指定启动参数所在的文件位置(这个配置是新增的)

       EnvironmentFile=/run/flannel/docker_opts.env

       ##在原有ExecStart后面添加$DOCKER_OPTS

       ##修改前

       #ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

       ##2.修改后

       ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock $DOCKER_OPTS

     5.重新加载systemd配置,并重启Docker即可,

       systemctl daemon-reload && systemctl restart docker && systemctl status docker

     6.查看overlay(覆盖)网络

       ##查看宿主机网络情况,此时可以看到docker0的网卡ip地址已经处于flannel网卡网段之内

       ifconfig

     7.测试flannel

       三台机器(node1/node2/node3)都配置好了之后,我们在三台机器上分别开启一个docker容器,测试它们的网络是否可相互联通的  

       ##下载centos镜像,因为此镜像中其它软件及命令均有安装

       docker pull centos

       ## 依次查看3台机器的flannel.1网络IP

       1.node-0

         ##创建/启动/进入容器

         docker run -it centos /bin/bash

         ##进入容器后,查看容器IP

         cat /etc/hosts         

         ##控制台输出如下内容

         10.0.14.2    ff43c3e20bde

         ##另外,请注意宿主机flannel.1网络的IP为

         10.0.14.0

       2.node2

         ##创建/启动/进入容器

         docker run -it centos /bin/bash

         ##进入容器后,查看容器IP

         cat /etc/hosts         

         ##控制台输出如下内容

         10.0.7.2    45f1155a3e25

         ##另外,请注意宿主机flannel.1网络的IP为

         10.0.7.0

       3.node3

         ##创建/启动/进入容器

         docker run -it alpine:latest /bin/sh

         ##进入容器后,查看容器IP

         cat /etc/hosts         

         ##控制台输出如下内容

         10.0.8.2    dd061df9ad27

         ##另外,请注意宿主机flannel.1网络的IP为

         10.0.8.0

       4.从不同宿主机容器到三台宿主机

         #-c count是数量,即发三个ping 包,请注意控制台信息

         ping -c3 192.168.183.161

         ping -c3 192.168.183.162

         ping -c3 192.168.199.158          

       5.从容器到到跨宿主机容器

         ping -c3 10.0.13.2

         ping -c3 10.0.19.2

         ping -c3 10.0.8.2

       6.node1再启动一个容器

         ##创建/启动/进入容器

         docker run -it cet:latest /bin/sh

         ##进入容器后,查看容器IP

         cat /etc/hosts         

         ##控制台输出如下内容

         10.0.14.3    fbdddd756a16      

         ##另外,请注意宿主机flannel.1网络的IP为

         10.0.14.0

       7.测试同一宿主机不同容器的连接

         #10.0.14.3<-->10.0.14.3互ping

         ping -c3 10.0.14.2

         ping -c3 10.0.14.3

附录一:eth0 eth0:1 eth0.1 的区别

eth0 eth0:1 和eth0.1三者的关系对应于物理网卡、子网卡、虚拟VLAN网卡

附录二:veth是什么?

Linux container 中用到一个叫做veth的东西,这是一种新的设备,专门为 container 所建。veth 从名字上来看是

Virtual ETHernet(虚拟网络设备)的缩写,它的作用很简单,就是要把从一个 network namespace 发出的数据包转发到另一个 namespace。

veth 设备是成对的,一个是 container 之中,另一个在 container 之外,即在真实机器上能看到的

附录三:ip地址后边加个/8(16,24,32)是什么意思?

是子网掩码的位数,由于255相当于二进制的8位11111111,所以也缩写成“/8”,表示网络号占了8位

A类IP地址的默认子网掩码为255.0.0.0(/8)

B类的为255.255.0.0(/16);

C类的为255.255.255.0(/24)

/30就是255.255.255.252

/32就是255.255.255.255

例如:

xx.xx.xx.0/24 ——>表示一个网段,并且24告诉了当前具体的子网掩码

举例说吧,192.168.0.0/24”就表示,这个网段的IP地址从192.168.0.1开始,到192.168.0.254结束

(192.168.0.0和192.168.0.255有特殊含义,不能用作IP地址);子网掩码是255.255.255.0

附录四: flanneld启动失败,报错的原因如下:

Failed to create SubnetManager: parse first path segment in URL cannot contain colon

ExecStart=/usr/local/bin/flanneld \

  -etcd-cafile=/etc/etcd/ssl/ca.pem \

  -etcd-certfile=/etc/etcd/ssl/server.pem \

  -etcd-keyfile=/etc/etcd/ssl/server-key.pem \

  ###是不能加引号的

  ##错误

  ##-etcd-endpoints="https://192.168.50.128:2379,https://192.168.50.129:2379,https://192.168.50.130:2379" \

  ##正确

  -etcd-endpoints=https://192.168.50.128:2379,https://192.168.50.129:2379,https://192.168.50.130:2379 \

  -etcd-prefix=/coreos.com/network \

  -iface=ens33 \

  -ip-masq

附录五:解决flannel下容器无法跨主机互通问题

经测试,发现是防火墙的问题,关闭防火墙后问题解决。

但作为服务的防火墙是不能关闭的,如何解决呢?

这是由于linux还有底层的iptables,所以在node上分别执行

iptables -P INPUT ACCEPT \

iptables -P FORWARD ACCEPT \

iptables -F \

iptables -L -n

问题完美解决

flanneld-v1.1.0.service

[Unit]

Description=Flanneld

After=network.target

After=network-online.target

Wants=network-online.target

##1.flannel服务需要先于Docker启动,后于etcd启动

After=etcd.service

Before=docker.service

[Service]

User=root

##2.ExecStart即flanneld启动程序位置

##3.--etcd-endpoints参数为ectd集群客户端地址

##4.--iface参数为要绑定的网卡的IP地址,或是网卡名(ifconfig查看获得)请根据实际情况修改

ExecStart=/opt/flannel/flanneld \

--etcd-endpoints=http://192.168.106.128:2379,http://192.168.106.132:2379 \

--etcd-prefix=/coreos.com/network \

--iface=ens33 \

--ip-masq

Restart=on-failure

Type=notify

LimitNOFILE=65536

[Install]

WantedBy=multi-user.target

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐