基于Kubeadm的Flannel分析

Flannel概述

Flannel是将多个不一样子网(基于主机Node)经过被Flannel维护的Overlay网络拼接成为一张大网来实现互联的,经过官方的一张网络拓扑图咱们能够对其基本原理一目了然:node

值得探讨的是,flannel的这个overlay网络支持多种后端实现,除上图中的UDP,还有VXLAN和host-gw等。此外,flannel支持经过两种模式来维护隧道端点上FDB的信息,其中一种是经过链接Etcd来实现,另一种是直接对接K8S,经过K8S添加删除Node来触发更新。docker

Flannel部署常见问题

1. Node状态显示为“NotReady”

个人K8S环境使用kubeadm来容器化运行K8S的各个组件(除kubelet直接运行在裸机上外),当我使用kubeadm join命令加入新的Minion Node到K8S集群中后,经过kubectl get node会发现全部的node都仍是not ready状态,这是由于尚未配置好flannel网络。json

2. 使用kube-flannel.yml没法建立DaemonSet

我使用的是K8S的1.6.4的版本,而后按照官方的说明,使用kube-flannel.yml来建立flannel deamon set,结果始终报错。正确的姿式是先使用kube-flannel-rbac.yml来建立flannel的ClusterRole并受权。该yml的主要做用是建立名叫flannel的ClusterRole,而后将该ClusterRole与ServiceAccount(flannel)绑定。接下来,当咱们使用kube-flannel.yml来建立flannel daemon set的时候,该daemon set明确指定其Pod的ServiceAccount为flannel,因而经过它启动起来的Pod就具备了flannel ClusterRole中指定的权限。后端

3.flannel Pod状态为Running,网络不通

我以前在个人Mac Pro上跑了三个VM,为了可以访问公网拉取镜像,我为每一个VM分配了一张网卡使用NAT模式,其分配到的IP地址可能重启后发生变化。另外,为了我本机方便管理,我为每台VM又分配了一张网卡使用Host-Only网络模式,每一个网卡都有一个固定的IP地址方便SSH。而后,奇怪的事情就这样发生了….api

缘由在与在kube-flannel.yml中,kube-flannel容器的command被指定为:网络

command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr"]

可见,其并无指定使用哪一张网卡做为flanneld对外通讯的物理网卡,因而,可能因为机器上面路由配置的差别,致使三台机器并无一致经过Host-Only网络模式的网卡来打通Overlay网络。遇到这种状况,若是几台机器的配置一致,能够手动修改kube-flannel.yml文件中kube-flannel的command的值,添加参数–iface=ethX,这里的ethX就为咱们但愿flanneld通讯使用的网卡名称。分布式

4.flannel启动异常,显示install-cni错误

这个现象比较坑,遇到这种状况的第一反应就是去查看install-cni容器到底作了什么。咱们打开kube-flannel.yml能够看到,该容器的command字段只有简单的一行Shell:ui

command: [ "/bin/sh", "-c", "set -e -x; cp -f /etc/kube-flannel/cni-conf.json /etc/cni/net.d/10-flannel.conf; while true; do sleep 3600; done" ]

也就是将镜像中作好的cni-conf.json拷贝到cni的netconf目录下。因为容器的/etc/cni/net.d是挂载主机的对应的目录,因此该操做主要目的是为CNI准备flannel环境,便于启动容器的时候正确从netconf目录中加载到flannel,从而使用flannel网络。spa

我发现进入主机的netconf目录中可以看到10-flannel.conf:插件

#ls /etc/cni/net.d/10-flannel.conf
/etc/cni/net.d/10-flannel.conf
# cat /etc/cni/net.d/10-flannel.conf
cat: /etc/cni/net.d/10-flannel.conf: No such file or directory

没法打出其内容,并且文件显示为红色,说明其内容并无正确从容器中拷贝过来。

以前我怀疑该异常是由于kubelet所带的文件系统参数为systemd,而docker的文件系统参数为cgroupfs所致,结果发现并不是如此。当前可以绕开该异常的workaround为手动进入到主机的netconf目录,建立10-flannel.conf目录,并写入如下数据:

{
"name": "cbr0",
 "type": "flannel",
 "delegate": {
 "isDefaultGateway": true
 }
}

5.flannel网络启动正常,可以建立pod,可是网络不通

出现该现象通常会想到debug,而debug的思路固然是基于官方的那张网络拓扑图。好比在个人机器上,经过参看网卡IP地址有以下信息:

6: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN
link/ether 22:a0:ce:3c:bf:1f brd ff:ff:ff:ff:ff:ff
 inet 10.244.1.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet 10.244.2.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
7:cni0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1450 qdisc noqueue state DOWN qlen 1000
 link/ether 0a:58:0a:f4:01:01 brd ff:ff:ff:ff:ff:ff
inet 10.244.1.1/24 scope global cni0
valid_lft forever preferred_lft forever
inet6 fe80::4cb6:7fff:fedb:2106/64 scope link
valid_lft forever preferred_lft forever

很明显,cni0为10.244.1.1/24网段,说明该主机应该位于10.244.1.0/16子网内;可是咱们看flannel.1网卡,确有两个IP地址,分别为于两个不一样的”/16”子网。因此,能够确定的是,咱们必定是在该太主机上部署了屡次kubeadm,而kubeadm reset并不会清理flannel建立的flannel.1和cni0接口,这就致使环境上遗留下了上一次部署分配到的IP地址。这些IP地址会致使IP地址冲突,子网混乱,网络通讯异常。

解决的方法就是每次执行kubeadm reset的时候,手动执行如下命令来清楚对应的残余网卡信息:

ip link del cni0
ip link del flannel.1

K8S如何使用Flannel网络

Flannel打通Overlay网络

当使用kubeadm来部署k8s,网络组件(如flannel)是经过Add-ons的方式来部署的。咱们使用这个yml文件来部署的flannel网络。
我截除了关键的一段内容(containers spec):

containers:
- name: kube-flannel
image: quay.io/coreos/flannel:v0.7.1-amd64
command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr" ]
securityContext:
privileged: true
env:
- name: POD_NAME
valueFrom:
fieldRef:
 fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
 fieldPath: metadata.namespace
 volumeMounts:
- name: run
mountPath: /run
- name: flannel-cfg
mountPath: /etc/kube-flannel/
 - name: install-cni
image: quay.io/coreos/flannel:v0.7.1-amd64
 command: [ "/bin/sh", "-c", "set -e -x; cp -f /etc/kube-flannel/cni-conf.json /etc/cni/net.d/10-flannel.conf; while true; do sleep 3600; done" ]
volumeMounts:
- name: cni
mountPath: /etc/cni/net.d
- name: flannel-cfg
 mountPath: /etc/kube-flannel/

经过分析该yaml文件能够知道:该pod内有两个container,分别为install-cni和 kube-flannel。

  • install-cni

主要负责将config-map中适用于该flannel的CNI netconf配置拷贝到主机的CNI netconf 路径下,从而使得K8S在建立pod的时候能够遵守标准的CNI流程挂载网卡。

  • kube-flannel

主要启动flanneld,该command中为flanneld的启动指定了两个参数: –ip-masq, –kube-subnet-mgr。第一个参数–ip-masq表明须要为其配置SNAT;第二个参数–kube-subnet-mgr表明其使用kube类型的subnet-manager。该类型有别于使用etcd的local-subnet-mgr类型,使用kube类型后,flannel上各Node的IP子网分配均基于K8S Node的spec.podCIDR属性。能够参考下图的方式来查看(该示例中,k8s为node-1节点分配的podCIDR为:10.244.8.0/24):

#kubectl edit node node-1

apiVersion: v1
kind: Node
 ...
name: dev-1
resourceVersion: "2452057"
selfLink: /api/v1/nodesdev-1
uid: 31f6e4c3-57b6-11e7-a0a5-00163e122a49
spec:
 externalID: dev-1
podCIDR: 10.244.8.0/24

另外,flannel的subnet-manager经过监测K8S的node变化来维护了一张路由表,这张表里面描述了要到达某个Pod子网须要先到达哪一个EndPoint。

CNI挂载容器到隧道端点

若是说flannel为Pod打通了一张跨node的大网,那么CNI就是将各个终端Pod挂载到这张大网上的安装工人。

在刚部署好flannel网络并未在该Node上启动任何Pod时,经过ip link咱们只可以看到flannel.1这张网卡,却没法看到cni0。难道是flannel网络运行异常吗?咱们接下来就来分析flannel的CNI实现原理,就知道答案了。

经过传统方式来部署flannel都须要经过脚原本修改dockerd的参数,从而使得经过docker建立的容器可以挂载到指定的网桥上。可是flannel的CNI实现并无采用这种方式。经过分析CNI代码,

咱们能够了解flannel CNI的流程:

  • 读取netconf配置文件,并加载/run/flannel/subnet.env环境变量信息。
  • 基于加载的信息,生成适用于其delegate的ipam和CNI bridge的netconf文件;其中指定ipam使用host-local,CNI bridge type为bridge。
  • 调用deletgate(CNI bridge type)对应的二进制文件来挂载容器到网桥上。

这里的环境变量文件/run/flannel/subnet.env是由flanneld生成的,里面包含了该主机所可以使用的IP子网网段,具体内容以下:

# cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.8.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

这些数据生成ipam的配置文件的依据,另外,flannel CNI插件的代码中,默认指定delegate为bridge:

if !hasKey(n.Delegate, "type") {
 n.Delegate["type"] = "bridge"
}

因此,当flannel CNI插件调用delegate,本质上就是调用bridge CNI插件来将容器挂载到网桥上。分析bridge CNI 插件的过程咱们能够看到其指定了默认网桥名称为cni0:

const defaultBrName = "cni0"

func loadNetConf(bytes []byte) (*NetConf, error) {
n := &NetConf{
 BrName: defaultBrName,
 }
 ...
 return n, nil
}

所以,如今咱们能够将整个流程连起来了:flannel CNI插件首先读取netconf配置和subnet.env信息,生成适用于bridge CNI插件的netconf文件和ipam(host-local)配置,并设置其delegate为bridge CNI插件。而后调用走bridge CNI插件挂载容器到bridge的流程。因为各个Pod的IP地址分配是基于host-local的Ipam,所以整个流程彻底是分布式的,不会对API-Server形成太大的负担。

至此,基于kubeadm的flannel分析就大体结束了,但愿对您有帮助!

相关文章
相关标签/搜索