前几天,一位朋友的Kubernetes集群遇到了问题,他请求我帮忙进行恢复。由于现场信息有限,我在这里分享一下解决问题的思路和方法。
环境问题
该环境只有一个master节点,控制面pod(如etcd、scheduler等)也只有一个pod。
问题起因是他们的服务访问出现了问题。为了修复这个问题,他采取了以下措施:
- 备份了3天前的etcd数据
- 重启了docker
- 将3天前的etcd数据进行恢复
然而,即便采取了这些措施,服务依然无法正常访问。
问题诊断
Deployment版本不匹配
首先发现pod并非处于
running
状态,因此删除了pod,让其重新创建。然而,在查看pod创建过程中发现,pod并没有被分配到节点。
问题分析
首先怀疑可能是kube-scheduler出现了问题:
- 删除kube-scheduler pod后发现无法重新创建该pod
-
最终通过将
/etc/kubernetes/manifests/kube-scheduler.yaml
文件移出再移入的方式成功创建了scheduler pod
然而,尽管这样做了,仍然无法调度pod。因此怀疑在scheduler之前出现了问题。查看api-server的日志后发现,出现了很多reversion版本不匹配的错误,应该是集群中的资源版本和etcd中的资源版本不匹配导致的。
-
使用etcdctl检查etcd的状态,发现etcd一切正常
etcdctl endpoint health etcdctl endpoint status --write-out=table
-
使用
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和环境中的配置区别 -
回滚之后发现pod可以正常创建出来
Iptables丢失问题
即使pod重新启动后,服务访问仍然不通。使用
kubectl describe
命令查看服务的service后,发现没有找到service对应的endpoints。经过一番调试后,发现绝大部分services都没有endpoints。
问题分析
service找不到endpoints,在系统中体现为可能没有创建出iptables规则:
-
使用
iptables-save
命令查看,发现果然没有kubernetes的iptables规则 -
该环境使用的是ipvs模式,使用
ipvsadm -l -n
也发现service的cluster IP没有对应的pod IP - 查看kube-proxy日志,并未发现任何异常
此时想到的解决方式有:
- 重新创建pod和对应的service,刷新iptables :尝试失败,重建之后并未生成iptables
-
重建节点
:所有节点都存在问题,无法通过
kubectl drain
迁移pod - 手动添加iptables :太过复杂,即便成功,也会污染节点的iptables规则
- 重新创建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的方式,重新初始化整个节点,逐步重置集群节点配置。