k8s-flannel
一、Flannel介绍
Flannel是由CoreOS开源的针对k8s的网络服务,其目的是为解决k8s集群中各主机上Pod之间的通信问题,其借助etcd维护网络IP地址分配,并为每个Node节点分配一个不同的IP地址段。 Flannel在每个节点运行一个名为flanneld的二进制代理程序,它负责从预留的网络中按照指定或者默认的掩码长度为当前节点申请分配一个子网,并将网络配置、 已分配的子网和辅助数据(比如主机的公网IP等)存储在Kubernetes API或独立的etcd中。Flannel通过不同的后端来实现跨节点Pod间的通信,目前支持的后端如下: 1.vxlan:Linux 内核在在2012年底的v3.7.0之后加入了VXLAN协议支持,使用Linux内核中的封装隧道报文,以Overlay模型支持跨节点的Pod通信,同时该后端支持直接路由模式(Directrouting,类似于host-gw模式),在该模式下, 位于同一个二层网络中的节点上的Pod间通信可通过路由模式直接发送,而跨二层网络的节点之上的Pod间通信仍要使用VXLAN隧道协议转发; 因而VXLAN隶属于Overlay网络模型,或者混合网络模型,,在基础网络上叠加的一种虚拟网络技术模式,该网络中的主机通过虚拟链路连接起来类似VPN隧道,原理为在物理网络上实现的逻辑网络。 vxlan模型中,flanneld监听udp 8472端口接受和发送封装的数据包. Directrouting 为在同一个二层网络中的node节点启用直接路由机制,类似于host-gw模式,启用后路由表中的Iface中没有Flannel.1接口,而是使用宿主机的接口转发。 2.host-gw:类似于VXLAN后端的直接路由模式,但不支持跨二层网络(须处于同一个局域网)的节点,因此这种模式要求各节点处于同一个二层网络中,不太适用于规模较大的环境,但转发性能较好 3.udp:使用常规UDP报文封装完成隧道转发,性能比vxlan和host-gw相比较差,仅在不支持vxlan和host-gw时使用;UDP后端模式中,flanneld监听UDP 8285端口发送报文(1.15版本已停用) Pod通信流程分析文档 https://blog.csdn.net/weixin_43266367/article/details/127836595 K8S 中 Pod 网络通信 (1)Pod 内容器与容器之间的通信 在同一个 Pod 内的容器(Pod 内的容器是不会跨宿主机的)共享同一个网络命令空间,相当于它们在同一台机器上一样,可以用 localhost 地址访问彼此的端口。 (2)同一个 Node 内 Pod 之间的通信 每个 Pod 都有一个真实的全局 IP 地址,同一个 Node 内的不同 Pod 之间可以直接采用对方 Pod 的 IP 地址进行通信,Pod1 与 Pod2 都是通过 Veth 连接到同一个 CNI0 网桥,网段相同,所以它们之间可以直接通信。 (3)不同 Node 上 Pod 之间的通信 Pod 地址与 CNI0 在同一网段,CNI0 网段与宿主机网卡是两个不同的网段,且不同 Node 之间的通信只能通过宿主机的物理网卡进行。 要想实现不同 Node 上 Pod 之间的通信,就必须想办法通过主机的物理网卡 IP 地址进行寻址和通信。因此要满足两个条件:Pod 的 IP 不能冲突;将 Pod 的 IP 和所在的 Node 的 IP 关联起来, 通过这个关联让不同 Node 上 Pod 之间直接通过内网 IP 地址通信。 calico和flannel calico支持更多的网络层的安全策略,flannel不支持 公有云大多数都是使用的flannel,早期使用UDP,后来有了vxlan之后就使用vxlan+Directrouting 自建IDC里面使用什么都可以,flannel配置比较交单,calico功能更加完善 性能差别不是很大,据统计,calico性能略高于flannel 私有云推荐使用calico 公有云可以使用flannel Flannel 组件的解释: Cni0:网桥设备,每创建一个pod都会创建一对 veth pair,其中一端是pod中的eth0,另一端是Cni0网桥中的端口(网卡),Pod中从网卡eth0发出的流量都会发送到Cni0网桥设备的端口(网卡)上,Cni0设备获得的ip地址是该节点分配到的网段的第一个地址。 Flannel.1: overlay网络的设备,用来进行vxlan报文的处理(封包和解包),不同node之间的pod数据流量都从overlay设备以隧道的形式发送到对端。 flannel还会在运行的每个node节点上生成一个环境变量文件,默认是/run/flannel/subnet.env,其包含本节点使用的子网、mtu等信息。例如下面的示例: root@master-01:~# cat /run/flannel/subnet.env FLANNEL_NETWORK=10.20.0.0/16 #flannel全局网段 FLANNEL_SUBNET=10.20.2.1/24 #本节点子网 FLANNEL_MTU=1450 #容器接口mtu值 FLANNEL_IPMASQ=true #地址映射 Flannel官方的部署文件默认使用vxlan后端,相关配置定义在kube-flannel名称空间下configmap/kube-flannel-cfg资源对象中, /var/lib/kubelet/pods/ID/volumes/kubernetes.io~configmap/flannel-cfg/net-conf.json:内容如下 net-conf.json: | { "Network": "10.20.0.0/16", "Backend": { "Type": "vxlan", "Directrouting": true } } 如上所示,其配置是json格式,常用的键有如下几个: Network: Flannel全局使用的子网,即pod cidr的值 SubnetLen:子网分割的长度,在全局子网掩码小于24时(例如16),默认为24 SubnetMin:分配给节点使用的起始子网,默认为切割完成后的第一个子网 SubnetMax:分给给节点使用的最大子网,默认为切割完成后的最后一个子网 Backend: Flannel使用的后端,以及后端的配置 Directrouting: 直接路由模式 当前node主机cni信息: [root@localhost7F ~]# cat /var/lib/cni/flannel/222021ac78e25174272ce8d7f3d5f74cf0e31d9a122f82a0fb42a5cc7fd367e4 {"cniVersion":"0.3.1","hairpinMode":true,"ipMasq":false,"ipam":{"routes":[{"dst":"10.20.0.0/16"}],"subnet":"10.20.2.0/24", "type":"host-local"},"isDefaultGateway":true,"isGateway":true,"mtu":1450,"name":"cbr0","type":"bridge"}
二、测试
#vxlan转发测试 #创建pod测试夸主机网络通信是否正常(域名无法ping通,是DNS没有设置) kubectl run net-test1 --image=alpine --replicas=4 sleep 360000 [root@localhost7C ~]# kubectl exec -it net-test1-5fcc69db59-dlwmh sh / # traceroute 10.20.5.7 traceroute to 10.20.5.7 (10.20.5.7), 30 hops max, 46 byte packets 1 10.20.2.1 (10.20.2.1) 0.006 ms 0.006 ms 0.003 ms 2 10.20.5.0 (10.20.5.0) 1.367 ms 0.747 ms 0.213 ms #使用Flannel.1转发 3 10.20.5.7 (10.20.5.7) 12.041 ms 0.572 ms 0.272 ms [root@localhost7C ~]# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.80.2 0.0.0.0 UG 100 0 0 eth0 10.20.1.0 10.20.1.0 255.255.255.0 UG 0 0 0 flannel.1 10.20.2.0 10.20.2.0 255.255.255.0 UG 0 0 0 flannel.1 10.20.3.0 10.20.3.0 255.255.255.0 UG 0 0 0 flannel.1 10.20.4.0 10.20.4.0 255.255.255.0 UG 0 0 0 flannel.1 10.20.5.0 10.20.5.0 255.255.255.0 UG 0 0 0 flannel.1 #使用Flannel.1转发 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 192.168.10.0 0.0.0.0 255.255.255.0 U 101 0 0 eth1 192.168.80.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0 192.168.122.0 0.0.0.0 255.255.255.0 U 0 0 0 virbr0 [root@localhost7C ~]# kubectl get pod -A -o wide NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES default net-test1-5fcc69db59-dlwmh 1/1 Running 1 3d 10.20.2.6 192.168.80.150 <none> <none> default net-test1-5fcc69db59-sk2xw 1/1 Running 1 3d 10.20.5.7 192.168.80.170 <none> <none> default net-test1-5fcc69db59-tszvf 1/1 Running 1 3d 10.20.5.9 192.168.80.170 <none> <none> default net-test1-5fcc69db59-v7zqg 1/1 Running 1 3d 10.20.3.8 192.168.80.160 <none> <none> kube-system kube-dns-6b65447b86-wh2fp 3/3 Running 3 47h 10.20.3.7 192.168.80.160 <none> <none> kube-system kube-flannel-ds-amd64-bl89w 1/1 Running 0 15s 192.168.80.130 192.168.80.130 <none> <none> kube-system kube-flannel-ds-amd64-ddfjm 1/1 Running 0 15s 192.168.80.160 192.168.80.160 <none> <none> kube-system kube-flannel-ds-amd64-jjsx4 1/1 Running 0 15s 192.168.80.140 192.168.80.140 <none> <none> kube-system kube-flannel-ds-amd64-rn9c8 1/1 Running 0 15s 192.168.80.170 192.168.80.170 <none> <none> kube-system kube-flannel-ds-amd64-v5758 1/1 Running 0 15s 192.168.80.150 192.168.80.150 <none> <none> kube-system kube-flannel-ds-amd64-vdf4v 1/1 Running 0 15s 192.168.80.120 192.168.80.120 <none> <none> 删除flannel网络方法: # 主节点 kubectl delete -f /opt/kube/kube-system/flannel.yaml # 所有节点上(master没有cni0) systemctl stop kubelet ifconfig cni0 down ifconfig flannel.1 down ip link set cni0 down ip link set flannel.1 down ip link delete cni0 ip link delete flannel.1 rm -rf /var/lib/cni/* rm -rf /etc/cni/net.d/* systemctl start kubelet #ansible设置VXLAN+Directrouting [root@localhost7B ansible]# cat roles/flannel/defaults/main.yml # 部分flannel配置,参考 docs/setup/network-plugin/flannel.md # 设置flannel 后端 FLANNEL_BACKEND: "vxlan" DIRECT_ROUTING: "true" #启用"Directrouting" #flanneld_image: "quay.io/coreos/flannel:v0.10.0-amd64" flanneld_image: "easzlab/flannel:v0.11.0-amd64" # 离线镜像tar包 flannel_offline: "flannel_v0.11.0-amd64.tar" #使用VXLAN+Directrouting测试 [root@localhost7F ~]# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.80.2 0.0.0.0 UG 100 0 0 eth0 10.20.0.0 192.168.80.120 255.255.255.0 UG 0 0 0 eth0 10.20.1.0 192.168.80.130 255.255.255.0 UG 0 0 0 eth0 10.20.2.0 0.0.0.0 255.255.255.0 U 0 0 0 cni0 10.20.3.0 192.168.80.160 255.255.255.0 UG 0 0 0 eth0 10.20.4.0 192.168.80.140 255.255.255.0 UG 0 0 0 eth0 10.20.5.0 192.168.80.170 255.255.255.0 UG 0 0 0 eth0 #使用宿主机的接口转发 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 192.168.10.0 0.0.0.0 255.255.255.0 U 101 0 0 eth1 192.168.80.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0 192.168.122.0 0.0.0.0 255.255.255.0 U 0 0 0 virbr0 [root@localhost7C ~]# kubectl exec -it net-test1-5fcc69db59-dlwmh sh / # traceroute 10.20.5.7 traceroute to 10.20.5.7 (10.20.5.7), 30 hops max, 46 byte packets 1 10.20.2.1 (10.20.2.1) 0.007 ms 0.008 ms 0.003 ms 2 192.168.80.170 (192.168.80.170) 0.276 ms 0.363 ms 0.156 ms #使用宿主机的接口转发 3 10.20.5.7 (10.20.5.7) 0.283 ms 0.758 ms 1.136 ms host-gw测试,与VXLAN+Directrouting一样,先删除flannel网络: [root@localhost7B ansible]# cat roles/flannel/defaults/main.yml # 部分flannel配置,参考 docs/setup/network-plugin/flannel.md # 设置flannel 后端 #FLANNEL_BACKEND: "host-gw" #flanneld_image: "quay.io/coreos/flannel:v0.10.0-amd64" flanneld_image: "easzlab/flannel:v0.11.0-amd64" # 离线镜像tar包 flannel_offline: "flannel_v0.11.0-amd64.tar" [root@localhost7C ~]# kubectl exec -it net-test1-5fcc69db59-dlwmh sh / # traceroute 10.20.5.7 traceroute to 10.20.5.7 (10.20.5.7), 30 hops max, 46 byte packets 1 10.20.2.1 (10.20.2.1) 0.007 ms 0.008 ms 0.003 ms 2 192.168.80.170 (192.168.80.170) 0.276 ms 0.363 ms 0.156 ms #使用宿主机的接口转发 3 10.20.5.7 (10.20.5.7) 0.283 ms 0.758 ms 1.136 ms
三、flannel yaml文件
--- 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'] defaultAddCapabilities: [] requiredDropCapabilities: [] # Host namespaces hostPID: false hostIPC: false hostNetwork: true hostPorts: - min: 0 max: 65535 # SELinux seLinux: # SELinux is unsed in CaaSP rule: 'RunAsAny' --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: flannel rules: - apiGroups: ['policy'] 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.20.0.0/16", "Backend": { "DirectRouting": true, "Type": "vxlan" } } --- apiVersion: apps/v1 kind: DaemonSet metadata: name: kube-flannel-ds-amd64 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: beta.kubernetes.io/os operator: In values: - linux - key: beta.kubernetes.io/arch operator: In values: - amd64 hostNetwork: true tolerations: - operator: Exists effect: NoSchedule serviceAccountName: flannel initContainers: - name: install-cni image: easzlab/flannel:v0.11.0-amd64 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: easzlab/flannel:v0.11.0-amd64 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"] env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace volumeMounts: - name: run mountPath: /run/flannel - name: flannel-cfg mountPath: /etc/kube-flannel/ volumes: - name: run hostPath: path: /run/flannel - name: cni hostPath: path: /etc/cni/net.d - name: flannel-cfg configMap: name: kube-flannel-cfg