带你玩转kubernetes-k8s(第48篇:深入分析k8s网络原理[开源的网络组件Flannel])

     Kubernetes的网络模型假定了所有Pod都在一个可以直接连通的扁平网络空间中。这在GCE里面是现成的网络模型,Kubernetes假定这个网络也存在。而在私有云里搭建Kubernetes集群,就不能假定这种网络已经存在了。我们需要自己实现这个网络假设,将不同节点上的Docker容器之间的相互访问先打通,然后运行Kubernetes。

     目前已经有多个开源组件支持容器网络模型。本节介绍几个常见的网络组件及其安装配置方法,包括Flannel、Open vSwitch、直接路由和Calico。

Flannel

   Flannel之所以可以搭建Kubernetes依赖的底层网络,是因为它能实现以下两点。
(1)它能协助Kubernetes,给每一个Node上的Docker容器都分配互相不冲突的IP地址。
(2)它能在这些IP地址之间建立一个覆盖网络(Overlay Network),通过这个覆盖网络,将数据包原封不动地传递到目标容器内。

现在,通过下图来看看Flannel是如何实现这两点的。

带你玩转kubernetes-k8s(第48篇:深入分析k8s网络原理[开源的网络组件Flannel])_第1张图片

     可以看到,Flannel首先创建了一个名为flannel0的网桥,而且这个网桥的一端连接docker0网桥,另一端连接一个叫作flanneld的服务进程。
     flanneld进程并不简单,它上连etcd,利用etcd来管理可分配的IP地址段资源,同时监控etcd中每个Pod的实际地址,并在内存中建立了一个Pod节点路由表;它下连docker0和物理网络,使用内存中的Pod节点路由表,将docker0发给它的数据包包装起来,利用物理网络的连接将数据包投递到目标flanneld上,从而完成Pod到Pod之间的直接地址通信。
Flannel之间的底层通信协议的可选技术包括UDP、VxLan、AWS VPC等多种方式。通过源flanneld封包、目标flanneld解包,最终docker0收到的就是原始的数据,对容器应用来说是透明的,感觉不到中间Flannel的存在。
     我们看一下Flannel是如何做到为不同Node上的Pod分配的IP不产生冲突的。其实想到Flannel使用了集中的etcd存储就很容易理解了。它每次分配的地址段都在同一个公共区域获取,这样大家自然能够互相协调,不产生冲突了。而且在Flannel分配好地址段后,后面的事情是由Docker完成的,Flannel通过修改Docker的启动参数将分配给它的地址段传递进去:

--bip 172.17.18.1/24

     通过这些操作,Flannel就控制了每个Node上的docker0地址段的地址,也就保障了所有Pod的IP地址在同一个水平网络中且不产生冲突了。

     Flannel完美地实现了对Kubernetes网络的支持,但是它引入了多个网络组件,在网络通信时需要转到flannel0网络接口,再转到用户态的flanneld程序,到对端后还需要走这个过程的反过程,所以也会引入一些网络的时延损耗。

      另外,Flannel模型默认采用了UDP作为底层传输协议,UDP本身是非可靠协议,虽然两端的TCP实现了可靠传输,但在大流量、高并发的应用场景下还需要反复测试,确保没有问题。

Flannel的安装和配置如下。

   1. 由于Flannel使用etcd作为数据库,所以需要预先安装好etcd,此处不在过多描述。

    2.安装Flannel

        需要在每个Node上都安装Flanel。Flannel软件的下载地址为:

https://github.com/coreos/flannel/releases

       将下载的压缩包flannel--linux-amd64.tar.gz解压,将二进制文件flanneld和mk-docker-opts.sh复制到/usr/bin(或其他PATH环境变量中的目录)下,即可完成对Flannel的安装。

 3. 配置Flannel

    此处以使用systemd系统为例对flanneld服务进行配置。编辑服务配置文件/usr/lib/systemd/system/flanneld.service:

[Unit]
Description=flanneld overlay address etcd agent
After=network.target
Before=docker.service

[Service]
Type=notify
EnvironmentFile=/etc/sysconfig/flanneld
ExecStart=/usr/bin/flanneld -etcd-endpoints=${FLANNEL_ETCD} $FLANNEL_OPTIONS

[Install]
RequiredBy=docker.service
WantedBy=multi-user.target

编辑配置文件/etc/sysconfig/flannel,设置etcd的URL地址:

FLANNEL_ETCD="http://20.0.40.51:2379"
FLANNEL_ETCD_KEY="/coreos.com/network"

 

    在启动flanneld服务之前,需要在etcd中添加一条网络配置记录,这个配置将用于flanneld分配给每个Docker的虚拟IP地址段:

etcdctl set /coreos.com/network/config '{"Network": "10.1.0.0/16"}'

由于Flannel将覆盖docker0网桥,所以如果Docker服务已启动,则需要停止Docker服务。

   4.启动flanneld服务

systemctl restart flanneld

 5. 设置docker0网桥的IP地址

mk-docker-opts.sh -i
source /run/flannel/subnet.env
ifconfig docker0 ${FLANNEL_SUBNET}

完成后确认网络接口docker0的IP地址属于flannel0的子网,重启docker服务。

 

Open vSwitch

    在了解了Flannel后,我们再看看Open vSwitch是怎么解决上述两个问题的。
    Open vSwitch是一个开源的虚拟交换机软件,有点儿像Linux中的bridge,但是功能要复杂得多。Open vSwitch的网桥可以直接建立多种通信通道(隧道),例如Open vSwitch with GRE/VxLAN。这些通道的建立可以很容易地通过OVS的配置命令实现。在Kubernetes、Docker场景下,我们主要是建立L3到L3的隧道。举个例子来看看Open vSwitch with GRE/VxLAN的网络架构如下图:

带你玩转kubernetes-k8s(第48篇:深入分析k8s网络原理[开源的网络组件Flannel])_第2张图片

      首先,为了避免Docker创建的docker0地址产生冲突(因为Docker Daemon启动且给docker0选择子网地址时只有几个备选列表,很容易产生冲突),我们可以将docker0网桥删除,手动建立一个Linux网桥,然后手动给这个网桥配置IP地址范围。
    其次,建立Open vSwitch的网桥ovs,使用ovs-vsctl命令给ovs网桥增加gre端口,在添加gre端口时要将目标连接的NodeIP地址设置为对端的IP地址。对每一个对端IP地址都需要这么操作(对于大型集群网络,这可是个体力活,要做自动化脚本来完成)。
    最后,将ovs的网桥作为网络接口,加入Docker的网桥上(docker0或者自己手工建立的新网桥)。
    重启ovs网桥和Docker的网桥,并添加一个Docker的地址段到Docker网桥的路由规则项,就可以将两个容器的网络连接起来了。

1.网络通信过程

   当容器内的应用访问另一个容器的地址时,数据包会通过容器内的默认路由发送给docker0网桥。ovs的网桥是作为docker0网桥的端口存在的,它会将数据发送给ovs网桥。ovs网络已经通过配置建立了和其他ovs网桥的GRE/VxLAN隧道,自然能将数据送达对端的Node,并送往docker0及Pod。
   通过新增的路由项,Node本身的应用数据也被路由到docker0网桥上,和刚才的通信过程一样,自然也可以访问其他Node上的Pod。

2.OVS with GRE/VxLAN组网方式的特点

     OVS的优势是,作为开源虚拟交换机软件,它相对成熟和稳定,而且支持各类网络隧道协议,通过了OpenStack等项目的考验。

    另一方面,在前面介绍Flannel时可知,Flannel除了支持建立覆盖网络,保证Pod到Pod的无缝通信,还和Kubernetes、Docker架构体系紧密结合。Flannel能够感知Kubernetes的Service,动态维护自己的路由表,还通过etcd来协助Docker对整个Kubernetes集群中docker0的子网地址分配。而我们在使用OVS时,很多事情就需要手工完成。

    无论事OVS还是Flannel,通过覆盖网络提供的Pod到Pod通信都会引入一些额外的通信开销,如果是对网络依赖特别重的应用,则需要评估对业务的影响。

      Open vSwitch的安装和配置如下:

带你玩转kubernetes-k8s(第48篇:深入分析k8s网络原理[开源的网络组件Flannel])_第3张图片

    首先,确保节点192.168.18.128的Docker0采用了172.17.43.0/24网段,而192.168.18.131的Docker0采用了172.17.42.0/24网段,对应的参数为docker daemon的启动参数“--bip”设置的值。

1.在两个Node上安装ovs

yum install openvswitch-2.4.0-1.x86_64.rpm

禁止selinux,重启Linux

vim /etc/selinux/config

    SELINUX=disabled

查看Open vSwitch的服务状态,应该启动ovsdb-server与ovs-vswitchd两个进程:

service openvswitch status

查看Open vSwitch的相关日志,确认没有异常:

more /vr/log/messages | grep openv

2.创建网桥和GRE隧道

      接下来需要在每个Node上都建立ovs的网桥br0,然后在网桥上创建一个GRE隧道连接对端网桥,最后把ovs的网桥br0作为一个端口连接到docker0这个Linux网桥上(可以认为是交换机互联),这样一来,两个节点机器上的docker0网段就能互通了。
下面以节点机器192.168.18.131为例,具体的操作步骤如下。

1) 创建ovs网桥

ovs-vsctl add-br br0

2) 创建GRE隧道连接对端,remote_ip为对端eth0网卡地址:

ovs-vstl add-port bri gre1  -- set interface gre1 type=gre option:remote_ip=192.168.18.128

3) 添加br0到本地docker0,使得容器流量通过OVS流经tunnel:

brctl addif docker0 br0

4) 启动br0与docker0网桥:

ip link set dev br0 up
ip link set dev docker0 up

5) 添加路由规则。由于192.168.18.128与192.168.18.131的docker0网段分别为172.17.43.0/24与172.17.42.0/24,这两个网段的路由都需要经过本机的docker0网桥路由,其中一个24网段是通过OVS的GRE隧道到达对端的,因此需要在每个Node上都添加通过docker0网桥转发的172.17.0.0/16段的路由规则:

ip route add 172.17.0.0/16 dev docker0

6) 清空Docker自带的iptables规则及Linux的规则,后者存在拒绝icmp报文通过防火墙的规则:

iptables -t nat -F; iptables -F

       在192.168.18.131上完成上述步骤后,在192.168.18.128节点执行同样的操作,注意,GRE隧道里的IP地址要改为对端节点(192.168.18.131)的IP地址。

     至此,基于OVS的网络搭建成功,由于GRE是点对点的隧道通信方式,所以如果有多个Node,则需要建立N×(N-1)条GRE隧道,即所有Node组成一个网状网络,实现了全网互通。

小结:

      本节内容到此结束,谢谢大家的支持!

你可能感兴趣的:(kubernetes)