解决Kubernetes集群故障的思路和方法

前几天,一位朋友的Kubernetes集群遇到了问题,他请求我帮忙进行恢复。由于现场信息有限,我在这里分享一下解决问题的思路和方法。

环境问题

该环境只有一个master节点,控制面pod(如etcd、scheduler等)也只有一个pod。

问题起因是他们的服务访问出现了问题。为了修复这个问题,他采取了以下措施:

  • 备份了3天前的etcd数据
  • 重启了docker
  • 将3天前的etcd数据进行恢复

然而,即便采取了这些措施,服务依然无法正常访问。

问题诊断

Deployment版本不匹配

首先发现pod并非处于 running 状态,因此删除了pod,让其重新创建。然而,在查看pod创建过程中发现,pod并没有被分配到节点。

问题分析

首先怀疑可能是kube-scheduler出现了问题:

  1. 删除kube-scheduler pod后发现无法重新创建该pod
  2. 最终通过将 /etc/kubernetes/manifests/kube-scheduler.yaml 文件移出再移入的方式成功创建了scheduler pod

然而,尽管这样做了,仍然无法调度pod。因此怀疑在scheduler之前出现了问题。查看api-server的日志后发现,出现了很多reversion版本不匹配的错误,应该是集群中的资源版本和etcd中的资源版本不匹配导致的。

  1. 使用etcdctl检查etcd的状态,发现etcd一切正常

    etcdctl endpoint health
    etcdctl endpoint status --write-out=table
    
  2. 使用 kubectl rollout history deployment/<deployment_name> 查看etcd中保存的deployment的版本,然后执行 kubectl rollout undo daemonset/<deployment_name> --to-revision=<version> 回滚到与etcd匹配的版本。

    回滚之前可以通过 kubectl rollout history daemonset/<deployment_name> --revision=<version> 对比etcd和环境中的配置区别

  3. 回滚之后发现pod可以正常创建出来

Iptables丢失问题

即使pod重新启动后,服务访问仍然不通。使用 kubectl describe 命令查看服务的service后,发现没有找到service对应的endpoints。经过一番调试后,发现绝大部分services都没有endpoints。

问题分析

service找不到endpoints,在系统中体现为可能没有创建出iptables规则:

  1. 使用 iptables-save 命令查看,发现果然没有kubernetes的iptables规则
  2. 该环境使用的是ipvs模式,使用 ipvsadm -l -n 也发现service的cluster IP没有对应的pod IP
  3. 查看kube-proxy日志,并未发现任何异常

此时想到的解决方式有:

  1. 重新创建pod和对应的service,刷新iptables :尝试失败,重建之后并未生成iptables
  2. 重建节点 :所有节点都存在问题,无法通过 kubectl drain 迁移pod
  3. 手动添加iptables :太过复杂,即便成功,也会污染节点的iptables规则
  4. 重新创建kube-proxy pod :重启kube-proxy pod之后也并未创建iptables规则

最后怀疑kube-proxy也可能出现问题,需要重新初始化kube-proxy。幸运的是,kubeadm提供了如下命令可以重新初始化kube-proxy:

kubeadm init phase addon kube-proxy --kubeconfig ~/.kube/config --apiserver-advertise-address <api-server-ip>

在重新初始化kube-proxy之后,发现iptables规则创建成功。删除并创建pod和service之后可以正确创建出对应的iptables规则,此时service也有了endpoints。

CNI连接错误

在上一步重启pod之后,发现一个名为webhook的pod没有成功重启。使用 kubectl describe 命令查看该pod后,发现如下错误:

networkPlugin cni failed to set up pod "webhook-1" network: Get "https://[10.233.0.1]:443/api/v1/namespaces/volcano-system": dial tcp 10.233.0.1:443: i/o timeout

该集群使用的是calico CNI,查看该CNI对应的daemonset后,发现只有5个pod是ready的。

删除"webhook-1" pod所在的节点的"calico-node" pod后,发现该"calico-node" pod启动失败。

问题分析

在上述错误中,"10.233.0.1"为kubernetes apiserver的service cluster IP。由于"clico-node" pod使用的是 hostnetwork ,因此可以直接在node上测试联通性。使用 telnet 10.233.0.1 443 测试后,发现果然不通。

calico的 /etc/cni/net.d/10-calico.conflist 配置文件中定义了连接apiserver所需的 kubeconfig 文件:

{
  "name": "cni0",
  "cniVersion":"0.3.1",
  "plugins":[
    {
      ... 
      "kubernetes": {
        "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    ...
  ]
}

/etc/cni/net.d/calico-kubeconfig 中就定义了连接apiserver所需的地址和端口。因此只需将该地址端口换成apiserver pod的地址和端口应该就可以解决该问题:

# cat /etc/cni/net.d/calico-kubeconfig
# Kubeconfig file for Calico CNI plugin.
apiVersion: v1
kind: Config
clusters:
- name: local
  cluster:
    server: https://[10.233.0.1]:443
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0...
users:
- name: calico
  user:
    token: eyJhbGciOiJSUzI1NiIsImtpZC...
contexts:
- name: calico-context
  context:
    cluster: local
    user: calico

Calico提供了如下两个环境变量用于修改生成的kubeconfig中的apiserver的地址和端口,将如下环境变量加入calico的daemonset,重新创建 calico-node pod即可:

- name: KUBERNETES_SERVICE_HOST
  value: <api-server-pod-ip>
- name: KUBERNETES_SERVICE_PORT
  value: "6443"

至此,问题基本解决。由于错误的操作,该集群出现了大量问题,后续可以通过驱逐节点pod的方式,重新初始化整个节点,逐步重置集群节点配置。

热门手游下载