Kubernetes——Flannel网络插件

Flannel网络插件

  各 Docker 主机在 docker0 桥上默认使用同一个子网,不同节点的容器很可能会得到相同的地址,于是跨节点的容器间通信会面临地址冲突的问题。另外,即使人为地设定多个节点上的 docker0 桥使用不同的子网,其报文也会因为在网络中缺乏路由信息而无法准确送达。事实上,各类 CNI 插件都至少要解决这个两类的问题。

  对于第一个问题(跨节点通信会面临IP冲突),Flannel 的解决办法是:预留使用一个网络,如 10.244.0.0/16,而后自动为每个节点的 Docker 容器引擎分配一个子网,如 10.244.1.0/24 和 10.244.2.0/24,并将其分配信息保存于 etcd 持久化存储。

  对于第二个问题(即使人为设定多个节点上的 docker0 桥使用不同的子网,也会遇到网络路由信息无法准确达到情况),Flannel 有着多种不同的处理方法,每一种处理方法也可以成为一种网络模型,或者称为 Flannel 使用的后端。

    • VxLAN: Linux 内核自 3.7.0 版本起支持 VxLAN,flannel的此种后端意味着使用内核中的 VxLAN 模块封装报文,这也是 flannel 较为推荐使用的方式。
    • host-gw: 即 Host Gateway,它通过在节点上创建到达目标容器地址的路由直接完成报文转发,因此这种方式要求各节点本身必须在同一个二层网络中,故该方式不太适用于较大的网络规模(大二层网络除外)。host-gw 有着较好的转发性能,且易于设定,推荐对报文转发性能要求较高的场景适用。
    • UDP: 使用普通 UDP 报文封装完成隧道转发,其性能较前两种方式要低很多,仅应该在不支持前两种方式的环境中使用。

  当前 Flannel 默认使用叠加网络模型 VxLAN 模型。

一、Flannel的配置参数

  为了跟踪各子网分配信息等,flannel 使用 etcd 存储来存储虚拟 IP 和 主机 IP 之间的映射,各个节点上运行的 flanneld 守护进程负责监视 etcd 中的信息并完成报文路由。

  默认情况下,flannel 的配置信息保存于 etcd 的键名 /coreos.com/network/config 之下,可以使用 etcd 服务的客户端工具来设定或修改其可用的相关配置。

  config 的值是一个 JSON 格式的字典数据结构,它可以使用的键包含以下几个:

1)Network: flannel 于全局使用的 CIDR 格式的 IPv4 网络,字符串格式,此为必选键,余下的均为可选。

2)SubnetLen: 将 Network 属性指定的 IPv4 网络基于指定位的掩码切割为供各节点使用的子网,此网络的掩码小于 24 时(如16),其切割子网时使用的掩码默认为 24 位。

3)SubnetMin: 可用作分配给节点使用的起始子网,默认为切分完成后第一个子网;字符串格式。

4)SubnetMax: 可用作分配给节点使用的最大子网,默认为切分完成后最大的一个子网;字符串格式。

5)Backend: flannel 要使用的后端类型,以及后端的相关配置,字典格式;VxLAN、host-gw 和 UDP 后端个有其相关的参数。

  例如,下面的配置示例中,全局网络为 "10.244.0.0/16",切分子网时用到的掩码长度为 24,将相应的子网 10.244.0.0/24 - 10.244.255.0/24 分别分配给每一个工作节点使用,选择 VxLAN 作为使用的后端类型,并监听于 8472 端口:

{
    "Network": "10.244.0.0/16",
	"SubnetLen": 24,
	"Backend":{
	    "Type": "VxLAN",
		"Port": 8472
	}
}

  以上配置信息科直接由 flannel 保存于 etcd 存储中,也可交由 kubernetes 进行存储。

  flannel 默认使用 VxLAN 后端,但 VxLAN direct routing 和 host-gw 却有着更好的性能表现。

二、VxLAN 后端和 direct routing

  VxLAN,全称(Virtual extensible Local Area Network)(虚拟可扩展局域网),是 VLAN 扩展方案草案,采用的是 MAC in UDP 封装方式,是 NVo3(Network Virtualization over Layer3)中的一种网络虚拟化技术。

  具体实现方式为:将虚拟网络的数据帧添加到 VxLAN 首部后,封装在物理网络的 UDP 报文中,然后以传统网络的通信方式传送该 UDP 报文,待其到达目标主机后,去掉物理网络报文的头部信息以及 VxLAN 首部,然后将报文交付给目的终端。

  VxLAN 技术的引用使得逻辑网络拓扑和物理网络拓扑实现了一定程度的解耦,网络拓扑的配置对于物理设备的配置依赖程度有所降低,配置更加灵活更方便。另外,VxLAN 技术解决了二层网络广播域分割的问题,提供了多租户的良好支持,通过 VxLAN 进行分割,各个租户可以独立组网、通信。但是,为了确保 VxLAN 机制通信过程的正确性,涉及 VxLAN 通信的 IP 报文一律不能分片,这就要求在物理网络的链路层实现中必须提供足够大的 MTU 值,或者修改其 MTU 值以保证 VxLAN 报文的顺利传输。

  不过,降低默认 MTU 值,以及额外的首部开销,必然会影响其报文传输性能。

  对于 Kubernetes 1.7 及以后的版本来说,flannel 项目官方给出的在线配置方式清单中的默认配置即为 VxLAN 后端,它定义在 kube-system 名称空间 ConfigMap 资源 kube-flannel-cfg 中,它以 10.244.0.0/16 为 Pod 网络地址,其在线配置清单地址:https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml,其部分配置内容如下:

  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan"
      }
    }

  在线配置清单地址:https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml,其完整配置内容如下:

---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: psp.flannel.unprivileged
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default
    seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default
    apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default
    apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default
spec:
  privileged: false
  volumes:
  - configMap
  - secret
  - emptyDir
  - hostPath
  allowedHostPaths:
  - pathPrefix: "/etc/cni/net.d"
  - pathPrefix: "/etc/kube-flannel"
  - pathPrefix: "/run/flannel"
  readOnlyRootFilesystem: false
  # Users and groups
  runAsUser:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  fsGroup:
    rule: RunAsAny
  # Privilege Escalation
  allowPrivilegeEscalation: false
  defaultAllowPrivilegeEscalation: false
  # Capabilities
  allowedCapabilities: ['NET_ADMIN', 'NET_RAW']
  defaultAddCapabilities: []
  requiredDropCapabilities: []
  # Host namespaces
  hostPID: false
  hostIPC: false
  hostNetwork: true
  hostPorts:
  - min: 0
    max: 65535
  # SELinux
  seLinux:
    # SELinux is unused in CaaSP
    rule: 'RunAsAny'
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: flannel
rules:
- apiGroups: ['extensions']
  resources: ['podsecuritypolicies']
  verbs: ['use']
  resourceNames: ['psp.flannel.unprivileged']
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes/status
  verbs:
  - patch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: flannel
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: flannel
subjects:
- kind: ServiceAccount
  name: flannel
  namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: flannel
  namespace: kube-system
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: kube-flannel-cfg
  namespace: kube-system
  labels:
    tier: node
    app: flannel
data:
  cni-conf.json: |
    {
      "name": "cbr0",
      "cniVersion": "0.3.1",
      "plugins": [
        {
          "type": "flannel",
          "delegate": {
            "hairpinMode": true,
            "isDefaultGateway": true
          }
        },
        {
          "type": "portmap",
          "capabilities": {
            "portMappings": true
          }
        }
      ]
    }
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan"
      }
    }
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kube-flannel-ds
  namespace: kube-system
  labels:
    tier: node
    app: flannel
spec:
  selector:
    matchLabels:
      app: flannel
  template:
    metadata:
      labels:
        tier: node
        app: flannel
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/os
                operator: In
                values:
                - linux
      hostNetwork: true
      priorityClassName: system-node-critical
      tolerations:
      - operator: Exists
        effect: NoSchedule
      serviceAccountName: flannel
      initContainers:
      - name: install-cni-plugin
       #image: flannelcni/flannel-cni-plugin:v1.1.0 for ppc64le and mips64le (dockerhub limitations may apply)
        image: rancher/mirrored-flannelcni-flannel-cni-plugin:v1.1.0
        command:
        - cp
        args:
        - -f
        - /flannel
        - /opt/cni/bin/flannel
        volumeMounts:
        - name: cni-plugin
          mountPath: /opt/cni/bin
      - name: install-cni
       #image: flannelcni/flannel:v0.18.1 for ppc64le and mips64le (dockerhub limitations may apply)
        image: rancher/mirrored-flannelcni-flannel:v0.18.1
        command:
        - cp
        args:
        - -f
        - /etc/kube-flannel/cni-conf.json
        - /etc/cni/net.d/10-flannel.conflist
        volumeMounts:
        - name: cni
          mountPath: /etc/cni/net.d
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
      containers:
      - name: kube-flannel
       #image: flannelcni/flannel:v0.18.1 for ppc64le and mips64le (dockerhub limitations may apply)
        image: rancher/mirrored-flannelcni-flannel:v0.18.1
        command:
        - /opt/bin/flanneld
        args:
        - --ip-masq
        - --kube-subnet-mgr
        resources:
          requests:
            cpu: "100m"
            memory: "50Mi"
          limits:
            cpu: "100m"
            memory: "50Mi"
        securityContext:
          privileged: false
          capabilities:
            add: ["NET_ADMIN", "NET_RAW"]
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: EVENT_QUEUE_DEPTH
          value: "5000"
        volumeMounts:
        - name: run
          mountPath: /run/flannel
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
        - name: xtables-lock
          mountPath: /run/xtables.lock
      volumes:
      - name: run
        hostPath:
          path: /run/flannel
      - name: cni-plugin
        hostPath:
          path: /opt/cni/bin
      - name: cni
        hostPath:
          path: /etc/cni/net.d
      - name: flannel-cfg
        configMap:
          name: kube-flannel-cfg
      - name: xtables-lock
        hostPath:
          path: /run/xtables.lock
          type: FileOrCreate

  其通过名为 kube-flannel-ds 的 DaemonSet 控制器资源,在每个节点运行一个 flannel 相关的 Pod 对象。DaemonSet 控制器的 Pod 模版中使用 "hostNetwork: true" 配置每个节点上的 Pod 资源直接共享使用节点的网络名称空间以完成网络配置,其配置结果直接生效于节点的根网络名称空间,配置内容如下:

---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kube-flannel-ds
  namespace: kube-system
  labels:
    tier: node
    app: flannel
spec:
  selector:
    matchLabels:
      app: flannel
  template:
    metadata:
      labels:
        tier: node
        app: flannel
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/os
                operator: In
                values:
                - linux
      hostNetwork: true
      priorityClassName: system-node-critical
      tolerations:
      - operator: Exists
        effect: NoSchedule
      serviceAccountName: flannel
      initContainers:
      - name: install-cni-plugin
       #image: flannelcni/flannel-cni-plugin:v1.1.0 for ppc64le and mips64le (dockerhub limitations may apply)
        image: rancher/mirrored-flannelcni-flannel-cni-plugin:v1.1.0
        command:
        - cp
        args:
        - -f
        - /flannel
        - /opt/cni/bin/flannel
        volumeMounts:
        - name: cni-plugin
          mountPath: /opt/cni/bin
      - name: install-cni
       #image: flannelcni/flannel:v0.18.1 for ppc64le and mips64le (dockerhub limitations may apply)
        image: rancher/mirrored-flannelcni-flannel:v0.18.1
        command:
        - cp
        args:
        - -f
        - /etc/kube-flannel/cni-conf.json
        - /etc/cni/net.d/10-flannel.conflist
        volumeMounts:
        - name: cni
          mountPath: /etc/cni/net.d
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
      containers:
      - name: kube-flannel
       #image: flannelcni/flannel:v0.18.1 for ppc64le and mips64le (dockerhub limitations may apply)
        image: rancher/mirrored-flannelcni-flannel:v0.18.1
        command:
        - /opt/bin/flanneld
        args:
        - --ip-masq
        - --kube-subnet-mgr
        resources:
          requests:
            cpu: "100m"
            memory: "50Mi"
          limits:
            cpu: "100m"
            memory: "50Mi"
        securityContext:
          privileged: false
          capabilities:
            add: ["NET_ADMIN", "NET_RAW"]
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: EVENT_QUEUE_DEPTH
          value: "5000"
        volumeMounts:
        - name: run
          mountPath: /run/flannel
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
        - name: xtables-lock
          mountPath: /run/xtables.lock
      volumes:
      - name: run
        hostPath:
          path: /run/flannel
      - name: cni-plugin
        hostPath:
          path: /opt/cni/bin
      - name: cni
        hostPath:
          path: /etc/cni/net.d
      - name: flannel-cfg
        configMap:
          name: kube-flannel-cfg
      - name: xtables-lock
        hostPath:
          path: /run/xtables.lock
          type: FileOrCreate

  传统的 VxLAN 后端使用隧道网络转发叠加网络的通信报文会导致不少的流量开销,于是 flannel 的 VxLAN 后端还支持 DirectRouting 模式,它通过添加必要的路由信息使用节点的二层网络直接发送 Pod 的通信报文,仅在 跨 IP 网络时,才启用传统的隧道方式转发通信流量。由于大部分场景中都省去了隧道首部开销,因此 DirectRouting 通信模式的性能基本接近于直接二层物理网络。

  如何开启 flannel VxLAN Direct Routing 网络呢?

答案:

  1. 将 flannel 项目官方提供的配置清单下载到本地:
    [root@mh-k8s-master-247-10 ~]# wget -O /etc/kubernetes/manifests/kube-flannel.yaml https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
  2. 修改其 ConfigMap 资源 kube-flannel-cfg 的 data 字段中的网络配置部分修改为如下内容所示,并使用 "kubeclt apply" 命令重新应用于集群中即可:
    kind: ConfigMap
    apiVersion: v1
    metadata:
      name: kube-flannel-cfg
      namespace: kube-system
      labels:
        tier: node
        app: flannel
    data:
      cni-conf.json: |
        {
          "name": "cbr0",
          "cniVersion": "0.3.1",
          "plugins": [
            {
              "type": "flannel",
              "delegate": {
                "hairpinMode": true,
                "isDefaultGateway": true
              }
            },
            {
              "type": "portmap",
              "capabilities": {
                "portMappings": true
              }
            }
          ]
        }
      net-conf.json: |
        {
          "Network": "10.244.0.0/16",
          "Backend": {
            "Type": "vxlan",
    		"Directrouting": true
          }
        }
  3. 执行 kubectl apply 命令:
    kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
  4. 配置完成后,在每个节点上执行路由查看命令 "ip route show" 可以看到其生成的路由规则:
    10.244.0.0/24  via 172.16.0.70 dev ens33
    10.244.1.0/24  dev cni0 proto kernel scope link src 10.244.1.1
    10.244.2.0/24  via 172.16.0.67 dev ens33
    10.244.3.0/24  via 172.16.0.68 dev ens33
  5. 在各个集群节点上执行 "iptables -nL" 命令可以看到,iptables filter 表的 FORWARD 链上由其生成了如下两条转发规则,它显式放行了 10.244.0.0/16 网络进出的所有报文,用于确保由物理接口接收或发送的目标地址为 10.244.0.0/16 网络的所有报文均能够正常通信,这些是 Direct Routing 模式得以实现的必要条件:

    target         prot    opt   source                 destination
    ACCEPT      all       --     10.244.0.0/16      0.0.0.0/0
    ACCETP      all       --      0.0.0.0/0            10.244.0.0/16
  6. 各个节点上依然存在 flannel 相关接口,原因是对于那些无法通过直接路由到达的主机上的 Pod (非同一个二层网络),它依然是采用 VxLAN 的隧道转发机制。按需启动两个 Pod 测试其通信,并通过相关接口捕获通信报文即可分析其结果。

  VxLAN Directroutinig 后端转发模式同时兼具了 VxLAN 后端和 host-gw 后端的优势,既保证了传输性能,又具备了跨二层网络转发报文的能力。

  另外,VxLAN 后端的可用配置参数除了 Type 之外还有如下几个,它们都有其默认值,在用户需要自定义参数时可显式给出相关的配置:

    • Type: VxLAN,字符串。
    • VNI: VxLAN 的标识符,默认值为1;数值类型数据。
    • Port: 用于发送封装的报文的 UDP 端口,默认为 8472;数值型数据。
    • GBP: 全称为 Group Based Policy,配置是否启用 VxLAN 的基于组的策略机制,默认为否; 布尔类型数据。
    • DirectRouting: 是否为同一个二层网络中的节点启用直接路由机制,类似于 host-gw 后端的功能;此种场景下,VxLAN 仅用于为那些不在同一个二层网络中的节点封装并转发报文; 布尔型数据。

三、host-gw 后端

  host-gw 后端通过添加必要的路由信息使用节点的二层网络直接发送 Pod 的通信报文,其工作方式类似于 VxLAN 后端的 Direct Routing 的功能,但不包含 VxLAN 的隧道转发能力。

  编辑 kube-flannel 在线配置清单:https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml,将 ConfigMap 资源 kube-flannel-cfg 的 data 字段中网络配置部分修改为如下所示的内容,并使用 "kubectl apply" 命令重新应用于集群中即可配置 flannel 使用 host-gw 后端:

kind: ConfigMap
apiVersion: v1
metadata:
  name: kube-flannel-cfg
  namespace: kube-system
  labels:
    tier: node
    app: flannel
data:
  cni-conf.json: |
    {
      "name": "cbr0",
      "cniVersion": "0.3.1",
      "plugins": [
        {
          "type": "flannel",
          "delegate": {
            "hairpinMode": true,
            "isDefaultGateway": true
          }
        },
        {
          "type": "portmap",
          "capabilities": {
            "portMappings": true
          }
        }
      ]
    }
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "host-gw",
      }
    }

  配置完成后,各节点会生成类似于 VxLAN direct routing 一样的路由及 iptables 规则以实现二层转发 Pod 网络的通信报文,省去了隧道转发模式的额外开销。不过,对于非同一个二层网络的报文转发,host-gw 则无能为力。

  虽然,类似 host-gw 或 VxLAN direct routing 这种使用静态路由的方式来实现二层转发虽然较之 VxLAN 有着更低的资源开销和更好的性能表现,但在 Kubernetes 集群规模较大时其路由信息的规模也将变得庞大且不易维护。

  此外,flannel 自身并不具备为 Pod 网络实现网络策略以实现其网络通信隔离的能力,但它能够借助于 Canal 项目构建网络策略功能。

posted @ 2022-07-04 18:49  左扬  阅读(1109)  评论(0编辑  收藏  举报
levels of contents