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
这个命令打印前一个容器的错误消息。
RunContainerError
当容器无法启动时,出现此错误。
甚至在容器内的应用程序启动之前。
该问题通常是由于配置错误,例如:
挂载不存在的卷,例如ConfigMap或Secrets
将只读卷安装为可读写
你应该使用kubectl describe pod 命令收集和分析错误。
处于Pending状态的Pod
当创建Pod时,该Pod保持Pending状态。
为什么?
假设你的调度程序组件运行良好,可能的原因如下:
集群没有足够的资源(例如CPU和内存)来运行Pod
当前的命名空间具有ResourceQuota对象,创建Pod将使命名空间超过配额
该Pod绑定到一个处于pending状态的 PersistentVolumeClaim
最好的选择是检查kubectl describe命令输出的“事件”部分内容:
$ kubectl describe pod
对于因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
端点是一对,并且在服务(至少)以Pod为目标时,应该至少有一个端点。
如果“端点”部分为空,则有两种解释:
你没有运行带有正确标签的Pod(提示:你应检查自己是否在正确的命名空间中)
service的selector标签上有错字
如果你看到端点列表,但仍然无法访问你的应用程序,则targetPort可能是你服务中的罪魁祸首。
你如何测试服务?
无论服务类型如何,你都可以使用kubectl port-forward来连接它:
$kubectl port-forward service/
这里:
是服务的名称
3000 是你希望在计算机上打开的端口
80 是服务公开的端口
3.Ingress的故障排除
如果你已到达本节,则:
Pod正在运行并准备就绪
服务会将流量分配到Pod
但是你仍然看不到应用程序的响应。
这意味着最有可能是Ingress配置错误。
由于正在使用的Ingress控制器是集群中的第三方组件,因此有不同的调试技术,具体取决于Ingress控制器的类型。
但是在深入研究Ingress专用工具之前,你可以用一些简单的方法进行检查。
Ingress使用serviceName和servicePort连接到服务。
你应该检查这些配置是否正确。
你可以通过下面命令检查Ingress配置是否正确:
$kubectl describe ingress
如果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
完了以后重新加入集群.这次可以正常工作了.
-----------------------------------------------------------------