zoukankan      html  css  js  c++  java
  • kubernetes之flannel

    kubernetes网络通信

    • 容器间的通信   pod内的容器通信(lo)
    • Pod之间的通信   pod IP <-----> pod IP
    • Pod与Service之间的通信 podIP <-----> ClusterIP
    • Service与集群外部的通信 ClusterIP <-----> 集群外部

    CNI插件:

    • flannel
    • calico
    • canel
    • kube-route

    解决方案:

    • 虚拟网桥
    • 多路复用  MacVLAN
    • 硬件交换  SR-IOV

    Flannel

    Flannel本身是一个框架,真正提供网络功能是他的后端实现。目前支持三种后端实现:

    • VXLAN
    • host-gw
    • UDP

    UDP

    UDP模式是Flannel早期支持的一种方式,却也是性能最差的一种方式。所以现在基本都弃用了。但是这种模式是最直接最容易理解的一种方式。

    Node1的容器访问Node2的容器,Node1容器访问的目的地址不是在容器的路由规则里,所以会走默认路由到达宿主机的docker0 网桥上,从而出现在宿主机上,这个时候这个IP的下一步取决于这台宿主机的路由规则,这时候这条会使用flannel0的设备上,flannel是一个TUN设备,在Linux中,TUN设备是一种工作在三层的虚拟网络设备,TUN设备的功能是在操作系统内核和应用程序之间传递IP包。

    当操作系统将一个IP包发送给flannel0设备后,flannel0 就会把IP包交给创建这个设备的应用程序,也就是flannel进行。这就是从内核态(Linux操作系统)到用户态(Flannel进程)的流动方向。

    反之,如果flannel进程向flannel0设备发送一个IP包,那么这个IP包就会出现在宿主机网络栈,然后根据宿主机路由表进行下一步处理,这是用户态向内核态流动方向。

    所以,当IP包从容器经过docker0出现在宿主机上,然后又根据宿主机路由表进入flannel0设备后,宿主机上的flannel进程就会收到这个IP包。然后,flannel看到了这个IP包的目的地址,就可以把他发送到Node2宿主机上。

    flannel是如何知道目的IP地址对应的容器是运行在Node2上

    源于flannel里的一个重要概念子网(Subnet)。事实上,在由Flannel管理的容器网络里,一台宿主机上的所有容器都属于该宿主机被分配的一个“子网”,所以flannel会根据目的IP地址找到对应的子网,从而在etcd里找到对应的宿主机。只要node1 和node2 互通,flannel进程一定会到达node2。

    UDP模式的缺点

    相比两台宿主机之间的直接通信,基于flannel UDP模式的容器通信网络多个一个额外的步骤,那就是flannel的处理过程,这个过程由于使用到了flannel0这个TUN设备,仅仅是发送IP包的时候,就需要三次用户态和内核态的数据拷贝。如图:

    1.  用户态容器进程发起IP包进过docker0网桥进入内核态
    2. IP包根据路由表进入TUN(flannel0)设备,从而回到用户态的flanneld进程
    3. flanneld 进行UDP封包之后重新进入内核态,将UDP包通过宿主机的eth0发出去

    VXLAN

    VXLAN全称虚拟可扩展局域网,是Linux内核本身就支持的一种网络虚拟化技术。所以说,VXLAN可以完全在内核态实现上述封装和解封工作,从而通过前面相似的“隧道机制”,构建出覆盖网络。

    VXLAN覆盖网络的设计思想:在现有的三层网络之上,“覆盖”一层虚拟的,由内核VXLAN模块负责维护的二层网络,使得连接在这个VXLAN二层网络上的“主机”(可以是虚拟机也可以是容器)之间,可以像一个局域网里那样自由通信,当然,实际上,这些“主机”可能分布在不同的宿主机上,甚至可以分布在不同的物理机房里。

    而为了能够在二层网络上打通“隧道”,VXLAN会在宿主机上设置一个特殊的网络设备作为“隧道”的两端。这个设备叫VTEP,既:虚拟隧道端点。而这个VTEP设备的作用,其实跟前面的flanneld进程非常相似。只不过,它进行封装和解封装的对象是二层数据帧。而且这个工作的执行流程全部是在内核里完成的(因为VXLAN本身就是内核模块)。如图:

    从图里看每个宿主机都有一个flannel1的设备,就是VXLAN所需的VTEP设备,它既有IP地址也有MAC地址。现在我们是container1 访问 container2,相对UDP比较来看,当container1发出请求后,这个目的的地址是10.244.1.3的IP包,会先出现在docker0网桥,然后被路由到本机flanner1设备上处理,也就是说,来到了“隧道”的出口。既目的宿主机的VTEP设备。而这个设备信息正是每台宿主机上的flanneld进程负责维护的。

    当所有Node启动后,我们可以在Node1 上可以看到多个flannel1 网卡的路由信息,是因为flanneld启动后创建的。

    $ route -n 
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    ...
    10.244.1.0      10.244.1.0      255.255.255.0   UG    0      0        0 flannel.1
    ....

    从上图看到10.244.1.0就是Node2的VTEP设备(就是flannel1 设备)的IP地址,而这些VTEP设备之间就需要想办法组成一个虚拟的二层网络,既:通过二层数据帧进行通信,而Node1上的VTEP设备收到原始IP包后,就要想办法把原始IP包加一个目的MAC地址,封装成二层数据帧,然后发送给目的VTEP设备。这里需要解决一个问题目的VTEP设备的MAC地址是什么?

    根据路由表信息我们知道了目的VTEP设备的IP地址,而根据三层IP地址查询二层MAC地址正是ARP表的功能。而这里用ARP表的记录,也就是flanneld进程在Node2节点启动时,自动添加到Node1上的。如下:

    $ ip neigh show dev flannel.1
    10.244.1.0 lladdr b2:ba:aa:a5:10:1a PERMANENT

    有了这个MAC地址linux内核就可以开始二层封包了,上面提到的MAC地址,对宿主机的二层网络没有任何意义,所以上述封装的数据帧不能在宿主机的二层网络里传输,为了方便概述,我们把上述数据帧称为内部数据帧。所以Linux内核还要把内部数据帧进一步封装成宿主机网络的一个普通数据帧,好让他载着内部数据帧,通过eth0网卡进行传输。这次封装我们称为外部数据帧,为了实现这个搭便车的机制,Linux内核在封装内部数据帧前面,加上特殊的VXLAN头,用来表示这个乘客实际上是VXLAN使用的数据帧。而这个VXLAN头里有一个重要的标志VNI,它是识别某个数据帧是不是应该归属自己处理的标志。而flannel中,VNI的值是1,这也是为什么宿主机的VTEP设备都叫做flannel1的原因。这个时候linux内核会把这数据帧封装一个UDP包里发出去。虽然node1的flannel1知道node2的flannel2的MAC地址,但是不知道node2MAC的地址,也就是UDP该发往那台主机,实际上flannel1还要扮演一个网桥的角色,在二层网络进行UDP转发,而在Linux内核里面,网桥设备进行转发的依据来自FDB的转发数据库。这个flannel网桥对应的FDB信息,就是flannel进程维护的,他的内容如下:

    $ bridge fdb show flannel.1  | grep b2:ba:aa:a5:10:1a
    b2:ba:aa:a5:10:1a dev flannel.1 dst 172.16.138.41 self permanent

    我们可以看到发往的IP地址是172.16.138.41的主机,显然这台主机就是 Node2,UDP要转发的目的也找到了。接下来就是宿主机网络封包的过程了。

     Flannel 在kubernetes上的应用

    其实上述过程也是kubernetes的主要处理方法,只不过kubernetes是通过一个叫CNI接口维护一个单独网桥来代替docker0,这个网桥CNI网桥,默认叫做cni0。如图:

    过程也是和VXLAN是一样的。需要注意的是,CNI网桥只是接管所有CNI插件负责的,既只是kubernetes创建的容器。

    kubernetes之所以要设置这样一个与docker0网桥功能一样的CNI网桥,主要原因有两点:

    • kubernetes项目并没有使用Docker的网络模型,所以它不希望,也不具备配置docker0的能力。
    • 还与kubernetes如何配置Pod,也就是infra容器Network Namespace相关。

    我们在部署kubernetes的时候,有一个步骤是安装kubernetes-cni包,他的目的就是宿主机上安装CNI插件所需要的基础可执行文件。

     ls /opt/cni/bin/
    bridge  dhcp  flannel  host-local  ipvlan  loopback  macvlan  portmap  ptp  sample  tuning  vlan

    CNI的基础可执行文件,按照功能分三类:

    • Main插件 他是用来创建具体网络设备的二进制文件。例如:birdge(网桥设备)、ipvlan、lookback(lo 设备)、ptp(Veth Pair设备)、macvlan、vlan
    • IPAM插件 他是负责分配IP地址的二进制文件。例如:dhcp,这个文件会向dhcp服务器发起请求;host-local,则会使用预先配置的IP地址来进行分配。
    • CNI社区维护的内置CNI插件。例如 flannel,就是专门为Flannel项目提供的CNI插件。

    从二进制文件来看,如果实现一个kubernetes用的容器网络方案,其实需要两部分工作,以Flannel下项目为例。

    • 首先实现这个网络本身,其实就是flanneld进程里主要逻辑,例如:创建和配置flannel.1设备,配置宿主机路由、配置ARP和FDB表。
    • 实现该网络方案对应的CNI插件,就是配置infra容器里的网络栈,并把他连接在CNI网桥上。

    接下来进行第一步,在宿主机上安装flannel。而这个过程中,flanneld启动后会在每台宿主机上生成他对应的CNI配置文件(就是configmap),从而告诉kubernetes,这个集群要使用Flannel作为容器网络方案。

    CNI配置文件内容如下:

    cat /etc/cni/net.d/10-flannel.conflist 
    {
      "name": "cbr0",
      "plugins": [
        {
          "type": "flannel",
          "delegate": {
            "hairpinMode": true,
            "isDefaultGateway": true
          }
        },
        {
          "type": "portmap",
          "capabilities": {
            "portMappings": true
          }
        }
      ]
    }

    需要注意的是Kubernetes 目前不支持多个CNI混用,如果在/etc/cni/net.d/里放置多个CNI配置文件的话,只会加载字母排序第一个配置文件。但另一方面,CNI允许你在一个CNI配置文件里,通过plugins字段,定义多个插件协作。假如我们定义了flannel和protmap这两个插件,这个时候dockershim会把这个CNI配置文件加载起来,并把列表第一个插件,就是flannel插件设置为默认插件,然后执行过程中flannel和portmap会按照定义顺序被调用,从而一次完成“配置容器网络”和“配置端口映射”。

    CNI插件工作原理

    当kubelet组件需要创建pod时,他一个创建的一定是infra容器,所以这一步,dockershim 就会先调用Docker API创建并启动infra容器,紧着执行一个叫做SetUpPod的方法,这个方法的作用是:为CNI插件准备构建参数,然后调用CNI插件为infra容器配置网络。这里要调用CNI插件是/opt/cni/bin/flannel;而调用它所需的参数,分两部分:

    第一部分,是由dockershim设置的一组CNI环境变量。其中,最重要的环境变量参数叫做:CNI_COMMAND。他的取值只有两种ADD和DEL。这个ADD和DEL操作,就是CNI插件唯一实现的两种方法。

    • ADD 把容器添加CNI网络里
    • DEL 把容器从CNI网络里移除

    在网桥类型的CNI里,这两个操作说明把容器以“Veth Pair”方式插在CNI网桥上,或者从网桥上拔掉。

    第二部分,是dockershim从CNI配置文件里加载到的默认插件的配置信息。这个配置信息在CNI中被叫作Network Configuration,dockershim会把Network Configuration 通过JSON数据的方式,通过标准输入的方式传递给Flannel CNI插件。

    将Flannel插件的后端改成VXLAN+DriectRouting

    以下操作不适合在生产环境操作,kubernetes的网络环境需要根据未来的业务环境选择。所以要集群配置前做好规划。

    VXLAN+DriectRouting是首先采用直接路由的方式,如果不可以就使用VXLAN的方式。

    Flannel的默认配置是VXLAN,但是我们可以配置多个插件协作,接下来我们就使用VXLAN+DriectRouting这两个插件协作。

    下载Flannel配置文件

    wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/k8s-manifests/kube-flannel-legacy.yml

    修改kube-flannel-legacy.yml,在net-conf.json里的增加一个type。

    $ vim kube-flannel-legacy.yml 
    .......
      net-conf.json: |
        {
          "Network": "10.244.0.0/16",
          "Backend": {
            "Type": "vxlan",
            "Type": "DriectRouting"
          }
        }
    .....

    删除flannel插件,重新生成配置文件。

    $ kubectl delete ds kube-flannel-ds  -n kube-system 
    $ kubectl apply -f kube-flannel-legacy.yml 

    这样就修改完成了,切忌生成不可以直接操作。

  • 相关阅读:
    现代软件工程 第一章 概论 第3题——韩婧
    现代软件工程 第一章 概论 第2题——韩婧
    小组成员邓琨、白文俊、张星星、韩婧
    UVa 10892 LCM的个数 (GCD和LCM 质因数分解)
    UVa 10780 幂和阶乘 求n!中某个因子的个数
    UVa 11859 除法游戏(Nim游戏,质因子)
    Codeforces 703C Chris and Road 二分、思考
    Codeforces 703D Mishka and Interesting sum 树状数组
    hdu 5795 A Simple Nim SG函数(多校)
    hdu 5793 A Boring Question 推公式(多校)
  • 原文地址:https://www.cnblogs.com/xzkzzz/p/9936467.html
Copyright © 2011-2022 走看看