zoukankan      html  css  js  c++  java
  • 十四,K8s集群网络flannel及canal策略

    k8s网络CNI之flannel

    k8s网络通信模型

    • Container to Container:同一pod内的多容器通信,lo
    • Pod to Pod:Pod IP <--> Pod IP
    • Service to Pod: PodIP <--> ClusterIP,跨网段的,依靠iptables或ipvs实现的
    • 集群外部客户端 与 Service:例如ingress

    常见CNI插件(Container,Network,Interface)

    • Flannel,提供叠加网络,基于linux TUN/TAP,使用UDP封装IP报
      文来创建叠加网络,并借助etcd维护网络分配情况,要注意,flannel不支持网络策略的制定,所以如需网络策略制定需采用Canal插件
    • Calico,基于BGP的三层网络,支持网络策略实现网络的访问控制。在每台机器上运行一个vRouter,利用内核转发数据包,并借助iptables实现防火墙等功能
    • Canal,由Flannel和Calico联合发布的一个统一网络插件,支持网络策略
    • Weave Net,多主机容器的网络方案,支持去中心化的控制平面,数据平面上,通过UDP封装实现L2 Overlay
    • Contiv,思科方案,直接提供多租户网络,支持L2(VLAN)、L3(BGP)、Overlay(VXLAN)
    • OpenContrail,Juniper开源
    • kube-router,K8s网络一体化解决方案,可取代kube-proxy实现基于ipvs的Service,支持网络策略、完美兼容BGP的高级特性

    重点了解Flannel, Calico, Canal, kube-router

    插件通信一般的解决方案

    • 虚拟网桥:

    • 多路复用:MacVLAN

    • 硬件交换:SR-IOV ,在硬件上虚拟出多个网卡,供容器使用,性能最好

    网络插件的应用

    放至宿主机/etc/cni/net.d/下,即可加载使用

    [root@node1 net.d]# cat 10-flannel.conflist 
    {
      "name": "cbr0",
      "plugins": [
        {
          "type": "flannel",   #插件类型
          "delegate": {
            "hairpinMode": true,
            "isDefaultGateway": true
          }
        },
        {
          "type": "portmap",
          "capabilities": {
            "portMappings": true   #是否支持端口映射
          } 
        }
      ]
    }
    
    

    Flannel插件

    flannel原理说明

        现在,我们来简单看一下,如果上方Machine A中IP地址为10.1.15.2/24的容器要与下方Machine B中IP地址为10.1.16.2/24的容器进行通信,封包是如何进行转发的。从上文可知,每个主机的flanneld会将自己与所获取subnet的关联信息存入etcd中,例如,subnet 10.1.15.0/24所在主机可通过IP 192.168.0.100访问,subnet 10.1.16.0/24可通过IP 192.168.0.200访问。反之,每台主机上的flanneld通过监听etcd,也能够知道其他的subnet与哪些主机相关联。如下图,Machine A上的flanneld通过监听etcd已经知道subnet 10.1.16.0/24所在的主机可以通过Public 192.168.0.200访问,而且熟悉docker桥接模式的同学肯定知道,目的地址为10.1.16.2/24的封包一旦到达Machine B,就能通过cni0网桥转发到相应的pod,从而达到跨宿主机通信的目的。
    
        因此,flanneld只要想办法将封包从Machine A转发到Machine B就OK了,而上文中的backend就是用于完成这一任务。不过,达到这个目的的方法是多种多样的,所以我们也就有了很多种backend。在这里我们举例介绍的是最简单的一种方式`hostgw`:因为`Machine A和Machine B处于同一个子网内`,它们原本就能直接互相访问。因此最简单的方法是:在Machine A中的容器要访问Machine B的容器时,我们可以将Machine B看成是网关,当有封包的目的地址在subnet 10.1.16.0/24范围内时,就将其直接转发至B即可。而这通过下图中那条红色标记的路由就能完成,对于Machine B同理可得。由此,在满足仍有subnet可以分配的条件下,我们可以将上述方法扩展到任意数目位于同一子网内的主机。而任意主机如果想要访问主机X中subnet为S的容器,只要在本主机上添加一条目的地址为R,网关为X的路由即可。
    

    flannel配置参数

    Network,全局CIDR格式的IPv4网络,字符串格式,必选
    SubnetLen,子网,默认为24位
    SubnetMin,分配给节点的起始子网
    SubnetMax,分配给节点的最大子网
    Backend,flannel要使用的后端
    

    flannel初始配置

    [root@master bin]# cat /run/flannel/subnet.env
    FLANNEL_NETWORK=10.244.0.0/16
    FLANNEL_SUBNET=10.244.0.1/24
    FLANNEL_MTU=1450
    FLANNEL_IPMASQ=true
    

    flannel后端实现原理

    flannel支持的后端

    • VxLAN:
    • host-gw:
    • UDP:

    host-gw

        hostgw是最简单的backend,它的原理非常简单,直接添加路由,将目的主机当做网关,直接路由原始封包。例如,我们从etcd中监听到一个EventAdded事件:subnet为10.1.15.0/24被分配给主机Public IP 192.168.0.100,hostgw要做的工作非常简单,在本主机上添加一条目的地址为10.1.15.0/24,网关地址为192.168.0.100,输出设备为上文中选择的集群间交互的网卡即可。对于EventRemoved事件,删除对应的路由即可。
    

    VxLAN

    如上图所示,当主机B加入flannel网络时,和其他所有backend一样,它会将自己的subnet 10.1.16.0/24和Public IP 192.168.0.101写入etcd中,和其他backend不一样的是,它还会将vtep设备flannel.1的mac地址也写入etcd中。

    之后,主机A会得到EventAdded事件,并从中获取上文中B添加至etcd的各种信息。这个时候,它会在本机上添加三条信息:

    1. 路由信息:所有通往目的地址10.1.16.0/24的封包都通过vtep设备flannel.1设备发出,发往的网关地址为10.1.16.0,即主机B中的flannel.1设备。
    2. fdb信息:MAC地址为MAC B的封包,都将通过vxlan首先发往目的地址192.168.0.101,即主机B
    3. arp信息:网关地址10.1.16.0的地址为MAC B

    虚拟网络数据帧添加到VxLAN首部后,封装在物理网络UDP报文中,到达目地主机后,去掉物理网络报文头部及VxLAN首部,再将报文交付给目的终端

    VxLAN后端使用隧道网络转发会导致一定和流量开销,VxLAN DirectRouting模式,通过添加必要的路由信息使用节点的二层网络直接发送Pod通信报文,仅在跨IP网络时,才启用隧道方式。这样,在不跨IP网络时,性能基本接近二层物理网络

    k8s网络策略之canal

    canal安装

    wget https://docs.projectcalico.org/v3.9/manifests/canal.yaml 
    kubectl apply -f canal.yaml
    

    NetworkPolicy相关术语

    kubectl explain networkpolicy.spec讲解:

    • egress 出站流量规则 可以根据ports和to去定义规则。ports下可以指定目标端口和协议。to(目标地址):目标地址分为ip地址段、pod、namespace
    • ingress 入站流量规则 可以根据ports和from。ports下可以指定目标端口和协议。from(来自那个地址可以进来):地址分为ip地址段、pod、namespace
    • podSelector 定义NetworkPolicy的限制范围。直白的说就是规则应用到那个pod上。podSelector: {},留空就是定义对当前namespace下的所有pod生效。没有定义白名单的话 默认就是Deny ALL (拒绝所有)
    • policyTypes 指定那个规则 那个规则生效,不指定就是默认规则。

    实验

    创建两个namespace

    kubectl create namespace dev
    kubectl create namespace prod
    

    创建pod并启动pod

    [root@master my-yaml]# mkdir networkpolicy
    [root@master my-yaml]# cd networkpolicy/
    [root@master my-yaml]# vim pod-a.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod1
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        
    [root@master networkpolicy]# kubectl apply -f ingress-default.yaml -n dev
    networkpolicy.networking.k8s.io/deny-all-ingress created
    [root@master networkpolicy]# kubectl get pod -n dev -o wide
    NAME   READY   STATUS    RESTARTS   AGE     IP           NODE    NOMINATED NODE   READINESS GATES
    pod1   1/1     Running   0          5m37s   10.244.1.2   node3   <none>           <none>
    
    
    

    管理入站流量详解

    kubectl explain networkpolicy.spec.ingress
    

    拒绝所有入站流量的规则:

    [root@master networkpolicy]# vim ingress-default.yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: deny-all-ingress
    spec:
      podSelector: {}  #未定义任何ingress规则,就不允许任何人访问(类似白名单)
      policyTypes:
      - Ingress
    
    [root@master networkpolicy]# kubectl apply -f deny-all.yaml -n dev #指定对dev名称空间下的pod生效
    networkpolicy.networking.k8s.io/deny-all-policy created
    [root@node1 networkpolicy]# kubectl get netpol -n dev   #查看dev名称空间下的策略
    NAME                  POD-SELECTOR   AGE
    deny-all-ingress     <none>         3s
    

    测试拒绝所有入站流量:

    [root@node1 networkpolicy]# curl 10.244.1.2
    。。。。。  #无响应
    
    [root@node1 networkpolicy]# kubectl apply -f pod-a.yaml -n prod  #在prod下创建pod
    pod/pod1 created
    [root@node1 networkpolicy]# kubectl get pod -n prod -o wide
    NAME   READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
    pod1   1/1     Running   0          29s   10.244.1.3   node3   <none>           <none>
    [root@node1 networkpolicy]# curl 10.244.1.3
    Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
    # 由上述实验可看出,针对做了访问限制的dev名称空间下的pod是无法访问的,而没进行限制的prod下的pod可以正常访问
    

    允许所有入站流量的规则及测试:

    [root@master networkpolicy]# vim ingress-allow.yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-all-ingress
    spec:
      podSelector: {}   # 匹配所有Pod
      ingress:
      - {}              # 定义为空, 表示允许访问
      policyTypes: ["Ingress"]
      
    [root@master networkpolicy]# kubectl apply -f ingress-allow.yaml -n dev
    networkpolicy.networking.k8s.io/allow-all-ingress created
    [root@master networkpolicy]# curl 10.244.1.2    # 再次访问测试,能正常访问了
    Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
    

    放入特定入站流量

    # 为pod打标签
    [root@master networkpolicy]# kubectl label pod app1 -n dev app=myapp 
    pod/myapp labeled
    [root@master manifests]# kubectl get pod -n dev --show-labels
    NAME    READY   STATUS    RESTARTS   AGE   LABELS
    myapp   1/1     Running   0          29m   app=myapp
    [root@master networkpolicy]# vim allow-someport.yaml  #制定一个放行规则
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-myapp-ingress
    spec:
      podSelector:         # 该规则只在当前的namespace下,携带app: myapp标签的pod生效。限制请求的类型包括Ingress和Egress
        matchLabels:
          app: myapp
      policyTypes: ["Ingress"]
      ingress:
      - from:
        - ipBlock:                  # 网络地址块
            cidr: 10.244.0.0/16     # 允许某个网段访问
            except:                 # 排除某个网段或ip访问(只拒绝掉10.244.1.5)          
            - 10.244.1.5/32
        - podSelector:              # 携带了app: myapp标签的pod可以访问
            matchLabels:
              app: myapp
        ports:                      #定义允许本机的哪个端口(实例中只允许访问80端口)
        - protocol: TCP
          port: 80
    
    [root@master networkpolicy]# curl 10.244.1.2
    Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
    [root@master networkpolicy]# curl 10.244.1.2:443
    。。。。。。。。          # 请求80端口正常,但443会被直接阻断      
    

    管理出站流量

    拒绝所有出站流量

    [root@master networkpolicy]# vim egress-default.yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: deny-all-egress
    spec:
      podSelector: {}
      ## egress:     如果需要放行所有出站流量,增加这条即可
      ##- {} 
      policyTypes: ["Egress"]
      
    [root@master networkpolicy]# kubectl apply -f egress-default.yaml -n prod
    networkpolicy.networking.k8s.io/deny-all-egress created
    [root@node1 networkpolicy]# kubectl exec -it pod1 -n prod -- /bin/sh
    / #ping 10.244.1.2
    PING 10.244.1.2 (10.244.1.2): 56 data bytes
    ^C
    --- 10.244.1.2 ping statistics ---
    2 packets transmitted, 0 packets received, 100% packet loss
    # 由此可见在该名称空间下的pod内部ping其他名称空间的pod是Ping不通的
    

    放行特定的出站流量

    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-tomcat-egress
    spec:
      podSelector:
        matchLabels:
          app: tomcat
      policyTypes: ["Egress"]
      egress:
      - to:
        - podSelector:
            matchLabels:
              app: nginx
      - ports:
        - protocol: TCP
          port: 80
      - to:
        - podSelector:
            matchLabels:
              app: mysql
        ports:
        - protocol: TCP
          port: 3306
    

    一般网络策略:

    • 名称空间:
      • 拒绝所有出站,入站
      • 放行所有出站目标为本名称空间内的Pod

    隔离名称空间

    隔离名称空间,应该放行与kube-system名称空间中Pod的通信,以实现监控和名称解析等各种管理功能

    kubectl explain networkpolicy.spec.ingress.from.namespaceSelector.matchExpressions
    kubectl explain networkpolicy.spec.egress.to.namespaceSelector.matchExpressions
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: namespace-deny-all
      namespace: default
    spec:
      policyTypes: ["Ingress","Egress"]
      podSelector: {}
    ---
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: namespace-
      namespace: default
    spec:
      policyTypes: ["Ingress","Egress"]
      podSelector: {}
      ingress:
      - from:
        - namespaceSelector:
            matchExpressions:
            - key: name
              operator: In
              values: ["default","kube-system"]
      egress:
      - to:
        - namespaceSelector:
            matchExpressions:
            - key: name
              operator: In
              values: ["default","kube-system"]
    
  • 相关阅读:
    特征选择方法之信息增益
    6 个优秀的开源 OCR 光学字符识别工具
    Python中type与Object的区别
    Python type类具体的三大分类:metaclasses,classes,instance
    如何去除List中的重复值?
    type,isinstance判断一个变量的数据类型
    Python图形界面开发包 PyGTK
    python使用easygui写图形界面程序
    opencv-python 学习笔记2:实现目光跟随(又叫人脸跟随)
    Tomcat全攻略
  • 原文地址:https://www.cnblogs.com/peng-zone/p/11727483.html
Copyright © 2011-2022 走看看