kubernetes常见故障

如题所述

第1个回答  2022-07-27
kubernetes常见故障

1. 节点CNI不可用,其它节点无法连接到故障节点的Pod

2. Subpath方式挂载的Configmap,特定条件下出现Pod无限重启的问题

3. 集群DNS服务器无法通过上游DNS解析外部名称

4. 节点假死,但是持有的Ceph RBD的Watcher不释放,导致有状态服务的Pod调度走后仍然无法启动

5. 误删Etcd数据、持久卷

有四个有用的命令可以对Pod进行故障排除:

kubectl logs 有助于检索Pod容器的日志

kubectl describe pod 检索与Pod相关的事件列表很有用

kubectl get pod 用于提取存储在Kubernetes中的Pod的YAML定义

kubectl exec -ti bash 在Pod的一个容器中运行交互式命令很有用

常见Pod错误

Pod可能会出现启动和运行时错误。

启动错误包括:

ImagePullBackoff

ImageInspectError

ErrImagePull

ErrImageNeverPull

RegistryUnavailable

InvalidImageName

运行时错误包括:

CrashLoopBackOff

RunContainerError

KillContainerError

VerifyNonRootError

RunInitContainerError

CreatePodSandboxError

ConfigPodSandboxError

KillPodSandboxError

SetupNetworkError

TeardownNetworkError

有些错误比其他错误更常见。

以下是最常见的错误列表以及如何修复它们的方法。

ImagePullBackOff

当Kubernetes无法获取到Pod中某个容器的镜像时,将出现此错误。

共有三个可能的原因:

镜像名称无效-例如,你拼错了名称,或者image不存在

你为image指定了不存在的标签

你尝试检索的image属于一个私有registry,而Kubernetes没有凭据可以访问它

前两种情况可以通过更正image名称和标记来解决。

针对第三种情况,你应该将私有registry的访问凭证通过Secret添加到k8s中并在Pod中引用它。

官方文档中有一个有关如何实现此目标的示例。

CrashLoopBackOff

如果容器无法启动,则Kubernetes将显示错误状态为:CrashLoopBackOff。

通常,在以下情况下容器无法启动:

应用程序中存在错误,导致无法启动

你未正确配置容器

Liveness探针失败太多次

你应该尝试从该容器中检索日志以调查其失败的原因。

如果由于容器重新启动太快而看不到日志,则可以使用以下命令:

$ kubectl logs <pod-name> --previous 

这个命令打印前一个容器的错误消息。

RunContainerError

当容器无法启动时,出现此错误。

甚至在容器内的应用程序启动之前。

该问题通常是由于配置错误,例如:

挂载不存在的卷,例如ConfigMap或Secrets

将只读卷安装为可读写

你应该使用kubectl describe pod 命令收集和分析错误。

处于Pending状态的Pod

当创建Pod时,该Pod保持Pending状态。

为什么?

假设你的调度程序组件运行良好,可能的原因如下:

集群没有足够的资源(例如CPU和内存)来运行Pod

当前的命名空间具有ResourceQuota对象,创建Pod将使命名空间超过配额

该Pod绑定到一个处于pending状态的 PersistentVolumeClaim

最好的选择是检查kubectl describe命令输出的“事件”部分内容:

$ kubectl describe pod <pod name> 

对于因ResourceQuotas而导致的错误,可以使用以下方法检查集群的日志:

$ kubectl get events --sort-by=.metadata.creationTimestamp 

处于未就绪状态的Pod

如果Pod正在运行但未就绪(not ready),则表示readiness就绪探针失败。

当“就绪”探针失败时,Pod未连接到服务,并且没有流量转发到该实例。

就绪探针失败是应用程序的特定错误,因此你应检查kubectl describe中的“ 事件”部分以识别错误。

2. 服务的故障排除

如果你的Pod正在运行并处于就绪状态,但仍无法收到应用程序的响应,则应检查服务的配置是否正确。

service旨在根据流量的标签将流量路由到Pod。

因此,你应该检查的第一件事是服务关联了多少个Pod。

你可以通过检查服务中的端点(endpoint)来做到这一点:

$ kubectl describe service <service-name> | grep Endpoints 

端点是一对,并且在服务(至少)以Pod为目标时,应该至少有一个端点。

如果“端点”部分为空,则有两种解释:

你没有运行带有正确标签的Pod(提示:你应检查自己是否在正确的命名空间中)

service的selector标签上有错字

如果你看到端点列表,但仍然无法访问你的应用程序,则targetPort可能是你服务中的罪魁祸首。

你如何测试服务?

无论服务类型如何,你都可以使用kubectl port-forward来连接它:

$kubectl port-forward service/<service-name> 3000:80 

这里:

是服务的名称

3000 是你希望在计算机上打开的端口

80 是服务公开的端口

3.Ingress的故障排除

如果你已到达本节,则:

Pod正在运行并准备就绪

服务会将流量分配到Pod

但是你仍然看不到应用程序的响应。

这意味着最有可能是Ingress配置错误。

由于正在使用的Ingress控制器是集群中的第三方组件,因此有不同的调试技术,具体取决于Ingress控制器的类型。

但是在深入研究Ingress专用工具之前,你可以用一些简单的方法进行检查。

Ingress使用serviceName和servicePort连接到服务。

你应该检查这些配置是否正确。

你可以通过下面命令检查Ingress配置是否正确:

$kubectl describe ingress <ingress-name> 

如果backend一列为空,则配置中必然有一个错误。

如果你可以在“backend”列中看到端点,但是仍然无法访问该应用程序,则可能是以下问题:

你如何将Ingress暴露于公共互联网

你如何将集群暴露于公共互联网

你可以通过直接连接到Ingress Pod来将基础结构问题与Ingress隔离开。

首先,获取你的Ingress控制器Pod(可以位于其他名称空间中):

$ kubectl get pods --all-namespaces 

NAMESPACE NAME READY STATUS 

kube-system coredns-5644d7b6d9-jn7cq 1/1 Running 

kube-system etcd-minikube 1/1 Running 

kube-system kube-apiserver-minikube 1/1 Running 

kube-system kube-controller-manager-minikube 1/1 Running 

kube-system kube-proxy-zvf2h 1/1 Running 

kube-system kube-scheduler-minikube 1/1 Running 

kube-system nginx-ingress-controller-6fc5bcc 1/1 Running 

描述它以检索端口:

# kubectl describe pod nginx-ingress-controller-6fc5bcc 

--namespace kube-system \ 

| grep Ports 

最后,连接到Pod:

$ kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system 

此时,每次你访问计算机上的端口3000时,请求都会转发到Pod上的端口80

现在可以用吗?

如果可行,则问题出在基础架构中。你应该调查流量如何路由到你的集群。

如果不起作用,则问题出在Ingress控制器中。你应该调试Ingress。

如果仍然无法使Ingress控制器正常工作,则应开始对其进行调试。

目前有许多不同版本的Ingress控制器。

热门选项包括Nginx,HAProxy,Traefik等。

你应该查阅Ingress控制器的文档以查找故障排除指南。

由于Ingress Nginx是最受欢迎的Ingress控制器,因此在下一部分中我们将介绍一些有关调试ingress-nginx的技巧。

调试Ingress Nginx

Ingress-nginx项目有一个Kubectl的官方插件。

你可以用kubectl ingress-nginx来:

检查日志,后端,证书等。

连接到ingress

检查当前配置

你应该尝试的三个命令是:

kubectl ingress-nginx lint,它会检查 nginx.conf

kubectl ingress-nginx backend,以检查后端(类似于kubectl describe ingress )

kubectl ingress-nginx logs,查看日志

请注意,你可能需要为Ingress控制器指定正确的名称空间–namespace 。

------------------------------------------------------------------------------------------------------

kubernetes之故障排查和节点维护(二)

系列目录

案例现场:

测试环境集群本来正常,突然间歇性地出现服务不能正常访问,过一会儿刷新页面又可以正常访问了.进入到服务所在的pod查看输出日志并没有发现异常.使用kubectl get node命令正好发现一个节点是NotReady状态

为了方便观察,使用kubectl get node --watch来观测一段时间,发现k8s-node1节点不断的在Ready和NotReady状态之间切换(使用kubectl get node -o wide可以查看节点的ip信息).

进入到出现问题的节点,使用命令journalctl -f -u kubelet来查看kubelet的日志信息,把错误日志截出来一段搜索一下,发现问题和这个问题基本上是一样的,发现这个问题的时间和github上issue提出的时间是在同一天,也没有看到解决办法.但是基本能确定是因为集群中k8s-node1上的kubernetes版本不一致造成的(从上面截图上可以看到,这个节点的版本是1.14.1其它的都是1.13.1,是怎么升上来的不清楚,可能是其它同事误操作升级导致的)

搜索kubernetes NotReady查看了一些解决经验,很多都是重启docker,重启kubectl等,然后都解决不了问题.于是尝试重置这个节点.

从集群中删除Node

由于这个节点上运行着服务,直接删除掉节点会导致服务不可用.我们首先使用kubectl drain命令来驱逐这个节点上的所有pod

kubectl drain k8s-node1 --delete-local-data --force --ignore-daemonsets

以上命令中--ignore-daemonsets往往需要指定的,这是因为deamonset会忽略unschedulable标签(使用kubectl drain时会自动给节点打上不可调度标签),因此deamonset控制器控制的pod被删除后可能马上又在此节点上启动起来,这样就会成为死循环.因此这里忽略daemonset.

实际在使用kubectl drain时候,命令行一直被阻塞,等了很久还在被阻塞.使用kubectl get pod命令查看pod状态时.其中一个叫作busybox的pod一直处于Terminating状态. 使用kubectl delete pod busybox同样无法删除它.这时候可以使用命令kubectl delete pods busybox --grace-period=0 --force来强制马上删除pod.

这时候控制台阻塞状态结束.下面执行命令kubectl delete node k8s-node1来删除这个节点.然后我们重新安装kubelet,kubeadm和kubectl

卸载旧版本

如果是通过yum方式安装的,可以通过yum list installed|grep xxx形式来找到已安装的组件,然后删除它们.删除以后重新安装.

这里之所以要重新安装是因为版本升级成了较为新的版本,如果版本是一样的,其它的不确定因素导致节点不稳定,又找不到具体原因,则可以通过kubeadm reset来重置安装.

重置命令并不会重置设置的iptables规则和IPVS如果想要重置iptables,则需要执行以下命令:

iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X

如果想要重置IPVS,则需要执行以下命令:

ipvsadm -C

这里我能够基本确定是由于版本不一致导致的,因此我并不重置iptables和IPVS,仅仅是重装组件.

重新加入集群

重置完成以后,我们把删除掉的k8s-node1节点使用kubeadm join重新加入到集群中

如果忘记了主节点初始化时候生成的加入token,可以在主节点上执行kubeadm token create --print-join-command重新生成加入token,然后把生成的命令复制到要加入集群的节点上执行.

重新加入集群后,观察了一段时间,一直是Ready状态,感觉终于稳定了,但是同事又反馈部署服务时出现以下错误

Failed create pod sandbox: rpc error: code = Unknown desc = failed to set up sandbox container "5159f7918d520aee74c5a08c8707f34b61bcf1c340bfc444125331034e1f57f6" network for pod "test-58f4789cb7-7nlk8": NetworkPlugin cni failed to set up pod "test-58f4789cb7-7nlk8_default" network: failed to set bridge addr: "cni0" already has an IP address different from 10.244.4.1/24

幸好有伟大的互联网,通过搜索,找到以下解决方案

由于这次启动以后初次部署pod就失败了,因此此节点上还没有运行的服务,我们不需要执行kubectl drain,可以直接把这个节点删除.然后执行以下命令

kubeadm reset

systemctl stop kubelet

systemctl stop docker

rm -rf /var/lib/cni/

rm -rf /var/lib/kubelet/*

rm -rf /etc/cni/

ifconfig cni0 down

ifconfig flannel.1 down

ifconfig docker0 down

ip link delete cni0

ip link delete flannel.1

systemctl start docker

完了以后重新加入集群.这次可以正常工作了.

-----------------------------------------------------------------
相似回答