修改Kubernetes集群节点的CIDR
使用Kubernetes集群的时候,遇到了在300台节点的时候,CNI从calico切换到flannel后,出现新建的pod无法分配IP的情况。
排查发现,是因为节点的podCIDR
没有可分配的IP地址段。节点的podCIDR
是由kube-controller-manager
进行分配的,因此需要查看kube-controller-manager
中--cluster-cidr
和--node-cidr-mask-size
配置,计算下支持的最大节点数。
集群最大节点数计算公式:(cluster-cidr的可用地址数)/(node-cidr-mask-size每台节点可用地址数)
。
了解到集群修改过两次--cluster-cidr
和--node-cidr-mask-size
的值,集群刚创建的时候,使用的是--cluster-cidr=10.244.0.0/16
和--node-cidr-mask-size=24
,根据公式算出最大节点数为258,第二次修改配置为--cluster-cidr=10.244.0.0/12
和--node-cidr-mask-size=16
,根据公式算出最大节点数为16,因此随着节点规模增加,kube-controller-manager
经过计算没有可分配的IP地址段给节点,所以节点的podCIDR
为空。
原因了解后,修改kube-controller-manager
的配置,即:--cluster-cidr=10.244.0.0/12
和--node-cidr-mask-size=24
,修改后集群可支持500以上节点。
修改后可以通过kubectl get nodes -ojsonpath="{range .items[*]}{.metadata.name}{'\t'}{.spec.podCIDR}{'\n'}{end}"
,看到所有节点都分配了IP地址段。
在节点已分配好podCIDR
后,创建几个nginx的pod,确认是否解决问题。查看结果,可以看到IP已经可以分配,pod已经正常运行。
$ kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE
nginx-deployment-6f769989f-2479j 1/1 Running 0 9d 10.244.52.4 worker1
nginx-deployment-6f769989f-278zs 1/1 Running 1 9d 10.240.31.10 worker289
nginx-deployment-6f769989f-27fv6 1/1 Running 1 9d 10.244.208.4 worker3
nginx-deployment-6f769989f-27phv 1/1 Running 1 9d 10.244.200.3 worker4
nginx-deployment-6f769989f-29hkt 1/1 Running 1 9d 10.241.12.6 worker5
虽然pod正常运行,但是发现在worker1上无法ping通worker289的pod,这个时候需要看下worker1的flannel的日志,看到日志有类似的错误:ignoring non-vxlan subnet(10.240.31.0/24): type=host-gw
,因为我们的flannel采用的vxlan模式,而上面的错误,会导致flannel忽略worker289上的podCIDR
,所以才会导致在worker1上无法连通worker289的pod。
连接不通的原因找到了,但是具体是什么导致了这个情况呢,查看了flannel的代码,没有找到比较明显的原因,只是怀疑可能是因为集群中有部分节点的podCIDR
有重复的,所以导致flannel这边有问题,因此比对了下整个集群节点的podCIDR
,发现果然有20个左右的节点重复,依次通过ping尝试连接这些节点的pod,都无法连通。到这里发现了根本原因,应该是之前两次修改--cluster-cidr
和--node-cidr-mask-size
导致的。因此下面要做的就是手动重新分配下重复的节点,使整个集群正常。
一开始想用kubectl patch
修改podCIDR
,但是发现不允许修改这个字段,因此只能采用另外一种方式,重新生成node的资源。因为在k8s上,所有的对象都是可以通过yaml来生成的,因此node也是可以通过yaml来重新生成。
$ kubectl get node <nodename> -oyaml > <nodename>.yaml
# 修改<nodename>.yaml中.spec.podCIDR和.spec.podCIDRs的配置,分配一个未使用的地址段
$ kubectl delete node <nodename>
$ kubectl apply -f <nodename>.yaml
修改完成后,我们可以看到节点的podCIDR
已经分配,这个时候删除节点的cni0
网桥,因为之前该节点已经创建过pod,会自动生成cni0
网桥,而cni0
的ip可能和podCIDR
的网段不一致,会导致后续创建pod的时候,无法分配IP,因此执行ip link del cni0
。
接着重建下flannel的pod,并删除之前在这个节点上的pod,重新在worker1上ping上述修改的节点上的pod,可以连通。
到这里,整个集群修改cidr就结束了。
还是建议在集群最初创建的时候,就规划好整个集群的节点规模,因为在创建后修改,极有可能会出现上面的网络问题,可能会影响集群上的业务服务。