k8s集群flannel部署错误异常排查:pod cidr not assigned

图片来自pixabay.com的DerWeg会员

1. 现象

在k8s集群的部署过程中,在初始化k8s master节点之后,准备通过如下kubeadm join命令添加当前worker节点到k8s集群,

kubeadm join xxx:6443 --token xxx.xxx \
>     --discovery-token-ca-cert-hash sha256:xxxx

[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

根据日志输出,可知当前节点已加入集群,通过kubectl get nodes命令可以正常看到节点的状态为ready,但是通过kubectl get pods -A命令查看pods状态时,看到如下的CrashLoopBackOff的异常状态,

$ kubectl get pods -A
NAMESPACE     NAME                                      READY   STATUS              RESTARTS   AGE
kube-system   kube-flannel-ds-j69g6                     0/1     CrashLoopBackOff    3          18m

通过kubectl logs命令查询pod日志可以看到报pod cidr not assigned的异常信息,

$ kubectl logs kube-flannel-ds-j69g6 -n kube-system
I0218 06:23:21.796296       1 main.go:518] Determining IP address of default interface
I0218 06:23:21.796512       1 main.go:531] Using interface with name eth0 and address 10.250.41.77
I0218 06:23:21.796525       1 main.go:548] Defaulting external address to interface address (10.250.41.77)
W0218 06:23:21.796537       1 client_config.go:517] Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
I0218 06:23:21.906396       1 kube.go:119] Waiting 10m0s for node controller to sync
I0218 06:23:21.906791       1 kube.go:306] Starting kube subnet manager
I0218 06:23:22.906882       1 kube.go:126] Node controller sync successful
I0218 06:23:22.906912       1 main.go:246] Created subnet manager: Kubernetes Subnet Manager - worker-0001
I0218 06:23:22.906918       1 main.go:249] Installing signal handlers
I0218 06:23:22.906963       1 main.go:390] Found network config - Backend type: vxlan
I0218 06:23:22.907016       1 vxlan.go:121] VXLAN config: VNI=1 Port=0 GBP=false Learning=false DirectRouting=false
E0218 06:23:22.907246       1 main.go:291] Error registering network: failed to acquire lease: node "worker-0001" pod cidr not assigned
I0218 06:23:22.907272       1 main.go:370] Stopping shutdownHandler...

当前发生问题的k8s版本为1.20.0,其中配置了flannel作为网络CNI插件。

2. 快速解决方案:手动分配podCIDR

这个问题主要是由于worker节点的flannel组件无法正常获取podCIDR的定义,一个快速的解决方法:可以通过执行如下命令对相应的worker节点添加podCIDR配置,

# 注意:每个worker节点的SUBNET需要区分开,否则k8s pods之间网络访问会不通。
kubectl patch node <NODE_NAME> -p '{"spec":{"podCIDR":"<SUBNET>"}}'

然后可以再次查看节点信息,

# 如下配置是cluster-cidr=172.18.0.0/16所指定网段范围内的一个子网段
$ kubectl patch node worker-0001 -p '{"spec":{"podCIDR":"172.18.1.0/24"}}'

$ kubectl describe node worker-0001
......
PodCIDR:                      172.18.1.0/24
......

过一段时间,再次查看kube-flannel-ds-j69g6 pod,可以看到已经可以正常启动,状态为RUNNING。

$ kubectl get pods -A
NAMESPACE     NAME                                      READY   STATUS              RESTARTS   AGE
kube-system   kube-flannel-ds-j69g6                     0/1     RUNNING             3          18m

这个解决方案是通过手动指定worker节点的可分配IP地址域,但是这个不是最佳的解决方案,最好找到根本原因,让flannel自动配置各个worker节点的podCIDR。

3. 异常根因和自动分配podCIDR

在k8s master集群部署中,通过kubeadm init初始化master节点时,flannel网络插件需要确保初始化命令配置了podCIDR如下启动参数,

  • –pod-network-cidr=172.18.0.0/16

初始化完毕之后,可以在配置文件/etc/kubernetes/manifest/kube-controller-manager.yaml中看到如下信息,

  • –allocate-node-cidrs=true
  • –cluster-cidr=172.18.0.0/16

同时在安装flannel cni网络插件时,通过kubectl apply kube-flannel.yml命令,kube-flannel配置文件中的Network参数需要和pod-network-cid保持一致,

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

在k8s集群上述初始化的过程中,若不小心出现如下情况,

  1. 对master主机初始化时,有1台或多台未正常配置podCIDR参数,即在kubeadm init命令中未添加pod-network-cid参数。
  2. 通过kubectl apply kube-flannel.yml命令添加flannel网络插件时,其指定的Network和pod-network-cid不一致。

新手指南:k8s集群单机部署

图片来自pixabay.com的designerpoin会员

k8s已经成为业界容器编排技术的平台标准,本文介绍了在单机上如何部署一个k8s集群,同时承担master和worker节点角色,采用flannel网络插件搭建其底层网络模型,部署完毕之后运行一个简单nginx服务。通过部署单机k8s集群,我们可以快速进行相关k8s集群的测试、调试和学习。

本文所安装k8s版本为1.20.0。

k8s集群的单机部署主要有如下步骤,

  1. 检查和配置环境,使之符合k8s所要求。
  2. 安装k8s所需的容器运行时,本文选用了Docker。
  3. 安装k8s集群管理工具kubeadm/kubelet/kubectl。
  4. 通过kubeadm初始化k8s集群。
  5. 通过kubectl部署网络插件flannel。

拍拍贷技术架构体系

拍拍贷公司成立于2007年6月,其技术体系最早基于微软.Net平台搭建,到了2016年,开始慢慢转向了Java平台,微服务开发也选择了当前业界流行的Spring Boot/Spring Cloud体系。在搭建微服务平台的过程中,拍拍贷基础架构团队并未全盘接纳Spring Cloud技术组件,更多是取其精华,兼并选择业界生产部署验证过成熟的组件。到现在2018年,微服务平台的搭建已过去快2年,整个技术体系已渐渐成型。

本文对当前拍拍贷的逻辑架构体系和微服务架构设计进行简单介绍,以供参考。

1. 逻辑架构体系

拍拍贷的逻辑架构体系如下图所示,

从上到下,分别有,

  • UI用户界面:提供Web应用和前端页面、移动应用、H5页面,以便于用户各个渠道的访问
  • Edge接入层:这一层承接外部所有用户的访问流量,负载均衡,并进行HTTPS的卸载,校验用户请求的合法性。同时,三大网关(API、移动、H5)还承接内部应用服务域名的调用访问流量。
  • Service服务层:主要的业务逻辑应用服务层
  • Platform平台层:包括各大Java服务中间件,微服务开发框架和平台,支撑整个业务服务应用的开发和运营。
  • Data Storage数据存储:数据存储层,主要包括三种:关系型数据库的MySql/MS Sql Server(目前逐渐从MS Sql Server往MySql迁移),缓存Redis,大数据的HBase。

2. 微服务架构设计

拍拍贷的微服务架构基于Spring Cloud,但很多组件选择了业界在生产环境已验证过、比较成熟的技术方案,比如配置中心、调用链监控、监控告警等。

整个微服务架构设计如下,

主要组件模块的功能如下,

  • 网关Zuul 1.0:提供服务路由、负载均衡、访问安全控制、熔断限流
  • 注册中心Eureka:服务注册和发现
  • 配置中心Ctrip Apollo:应用环境配置
  • 微服务Spring Boot:应用服务开发
  • 发布系统:通过网关实现应用服务的上下线、灰度发布、蓝绿发布
  • 调用链监控Dianping CAT:应用服务的监控埋点
  • 数据收集器Kafka:进行ELK的日志分析,收集运营Metrics到KairosDB
  • 运营指标KairosDB:存储相关的运营指标,包括业务、系统和运维指标,比如用户的访问量、消息的消费吞吐量、zabbix的运维监控数据等。
  • 运营看板Grafana:对接ELK/KairosDB/Zabbix,展现数据看板
  • 告警Zalando Zmon:健康和熔断告警

该图可以和Spring Cloud的微服务架构图相比较,可以看到拍拍贷在搭建微服务架构所做的改变,

  1. 添加发布系统,对接注册中心和网关,实现微服务实例的无缝上下线,实现灰度发布要求,为微服务的持续交付打下基础
  2. 添加ELK日志分析,完善监控告警,通过Grafana提供运营看板,确保微服务运营的快速、简单和稳定。在Spring Cloud技术体系中,其运营和监控是短板,ELK和Grafana作为业界流行的日志分析和运营看板组件,具有高可扩展性。
  3. 内部应用服务调用并未选择直连模式,而是通过服务域名走网关,简单可靠,其适合当前拍拍贷的业务运营需求。