对 Kubernetes 部署进行故障排除的视觉指南

最后更新于 2022 年 4 月


k8s故障排查 - 图1
TL;DR:这里有一个图表,可以帮助您在 Kubernetes 中调试您的部署(您可以下载 PDF 版本PNG版本)。


当您希望在 Kubernetes 中部署应用程序时,通常会定义三个组件:

  • 部署——这是创建应用程序副本的秘诀。
  • 服务——将流量路由到 Pod 的内部负载均衡器。
  • 一个入口——描述流量应该如何从集群外部流向你的服务。

这是一个快速的视觉回顾。
k8s故障排查 - 图2
假设您希望部署一个简单的Hello World应用程序,此类应用程序的 YAML 应类似于以下内容:

定义很长,很容易忽略组件之间的关系。
例如:

  • 什么时候应该使用端口 80,什么时候应该使用端口 8080?
  • 您是否应该为每个服务创建一个新端口,以便它们不会发生冲突?
  • 标签名称重要吗?应该到处都一样吗?

在专注于调试之前,让我们回顾一下这三个组件是如何相互链接的。
让我们从部署和服务开始。

连接部署和服务

令人惊讶的消息是服务和部署根本没有连接。
相反,Service 直接指向 Pod 并完全跳过 Deployment。
所以你应该注意的是 Pod 和 Services 是如何相互关联的。
你应该记住三件事:

  1. 服务选择器应至少匹配一个 Pod 的标签。
  2. ServicetargetPort应该与containerPortPod 的匹配。
  3. 服务port可以是任何数字。多个服务可以使用相同的端口,因为它们分配了不同的 IP 地址。

下图总结了如何连接端口:

  • k8s故障排查 - 图3

如果您查看 YAML,标签和ports/targetPort应该匹配:

你好世界.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
track: canary
spec:
selector:
matchLabels:
any-name: my-app
template:
metadata:
labels:
any-name: my-app
spec:
containers:
- name: cont1
image: learnk8s/app:1.0.0
ports:
- containerPort: 8080
—-
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- port: 80
targetPort: 8080
selector:
any-name: my-app
track: canaryDeployment 顶部的标签呢?
那也应该匹配吗?
该标签属于部署,服务的选择器不使用它来路由流量。
换句话说,您可以安全地删除它或为其分配不同的值。
那么matchLabels选择器呢?
它始终必须匹配 Pod 的标签,并且部署使用它来跟踪 Pod。
假设你做出了正确的改变,你如何测试它?
您可以使用以下命令检查 Pod 是否具有正确的标签:

重击
kubectl get pods —show-labels
NAME READY STATUS LABELS
my-deployment-pv6pd 1/1 Running any-name=my-app,pod-template-hash=7d6979fb54
my-deployment-f36rt 1/1 Running any-name=my-app,pod-template-hash=7d6979fb54
或者,如果您有属于多个应用程序的 Pod:
kubectl get pods —selector any-name=my-app —show-labels

any-name=my-app标签在哪里any-name: my-app。
仍有问题?
您还可以连接到 Pod!
您可以使用port-forwardkubectl 中的命令连接到 Service 并测试连接。

kubectl port-forward service/ 3000:80
Forwarding from 127.0.0.1:3000 -> 8080
Forwarding from [::1]:3000 -> 8080
在哪里:

  • service/是服务的名称——在当前 YAML 中是“my-service”。
  • 3000 是您希望在计算机上打开的端口。
  • 80是Service在port字段中暴露的端口。

如果可以连接,则设置正确。
如果你不能,你很可能放错了标签或端口不匹配。

连接服务和入口

公开您的应用程序的下一步是配置 Ingress。
Ingress 必须知道如何检索服务,然后连接 Pod 并路由流量。
Ingress 通过名称和暴露的端口检索正确的服务。
Ingress 和 Service 中应该匹配两件事:

  1. Ingress的service.port应该匹配portService 的
  2. Ingress的service.name应该匹配nameService 的

下图总结了如何连接端口:

  • k8s故障排查 - 图4

在实践中,您应该查看以下几行:

你好世界.yaml
apiVersion: v1 kind: Service metadata: name: my-service spec: ports: - port: 80 targetPort: 8080 selector: any-name: my-app —- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress spec: rules: - http: paths: - backend: service: name: my-service port: number: 80 path: / pathType: Prefix
您如何测试 Ingress 是否有效?
您可以使用与之前相同的策略kubectl port-forward,但您应该连接到 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
识别 Ingress Pod(可能在不同的命名空间中)并对其进行描述以检索端口:

重击
kubectl describe pod nginx-ingress-controller-6fc5bcc \ —namespace kube-system \ | grep Ports
Ports: 80/TCP, 443/TCP, 18080/TCP
最后,连接到 Pod:

重击
kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 —namespace kube-system
Forwarding from 127.0.0.1:3000 -> 80
Forwarding from [::1]:3000 -> 80
此时,每次访问计算机上的 3000 端口时,请求都会转发到 Ingress 控制器 Pod 上的 80 端口。
如果您访问http://localhost:3000,您应该会发现该应用程序服务于一个网页。

端口回顾

以下是关于哪些端口和标签应该匹配的快速回顾:

  1. Service 选择器应该与 Pod 的标签匹配
  2. ServicetargetPort应该与containerPortPod 内的容器相匹配
  3. 服务端口可以是任意数字。多个服务可以使用相同的端口,因为它们分配了不同的 IP 地址。
  4. service.port入口的 应该与服务port中的匹配
  5. 服务名称应与service.nameIngress中的字段匹配

了解如何构建 YAML 定义只是故事的一部分。
当出现问题时会发生什么?
也许 Pod 没有启动,或者它正在崩溃。

排除 Kubernetes 部署问题的 3 个步骤

在深入调试损坏的部署之前,必须对 Kubernetes 的工作方式有一个定义明确的心理模型。
由于每个部署中都有三个组件,因此您应该从底部开始按顺序调试所有组件。

  1. 您应该确保您的 Pod 正在运行,然后
  2. 专注于让服务将流量路由到 Pod,然后
  3. 检查 Ingress 是否正确配置。
  • k8s故障排查 - 图5

  • 1. Pod 故障排除

    大多数时候,问题出在 Pod 本身。
    您应该确保您的 Pod 处于RunningReady 状态
    你怎么检查?

重击
kubectl get pods
NAME READY STATUS RESTARTS AGE
app1 0/1 ImagePullBackOff 0 47h
app2 0/1 Error 0 47h
app3-76f9fcd46b-xbv4k 1/1 Running 1 47h
在上面的输出中,最后一个 Pod 是RunningReady——但是,前两个 Pod 既不是Running也不是Ready
你如何调查出了什么问题?
有四个有用的命令可以对 Pod 进行故障排除:

  1. kubectl logs 有助于检索 Pod 的容器的日志。
  2. kubectl describe pod 对于检索与 Pod 关联的事件列表很有用。
  3. kubectl get pod 用于提取存储在 Kubernetes 中的 Pod 的 YAML 定义。
  4. kubectl exec -ti — bash在 Pod 的容器之一中运行交互式命令很有用。

你应该使用哪一个?
没有一种万能的。
相反,您应该使用它们的组合。

常见的 Pod 错误

Pod 可能有启动和运行时错误。
启动错误包括:

  • ImagePullBackoff
  • 图像检查错误
  • ErrImagePull
  • ErrImageNeverPull
  • 注册表不可用
  • 无效的图像名称

运行时错误包括:

  • CrashLoopBackOff
  • 运行容器错误
  • KillContainerError
  • 验证非根错误
  • RunInitContainerError
  • CreatePodSandboxError
  • ConfigPodSandboxError
  • KillPodSandbox 错误
  • 设置网络错误
  • 拆解网络错误

有些错误比其他错误更常见。
以下是最常见错误的列表以及如何修复它们。

ImagePullBackOff

当 Kubernetes 无法检索 Pod 的容器之一的映像时,会出现此错误。
常见的罪魁祸首有以下三种:

  1. 图像名称无效 - 例如,您拼错了名称,或者图像不存在。
  2. 您为图像指定了一个不存在的标签。
  3. 您尝试检索的映像属于私有注册表,并且 Kubernetes 没有访问它的凭据。

前两种情况可以通过修正图像名称和标签来解决。
最后,您应该将凭证添加到您的私有注册表中,并在您的 Pod 中引用它。
官方文档有一个关于如何做到这一点的示例。

CrashLoopBackOff

如果容器无法启动,则 Kubernetes 将 CrashLoopBackOff 消息显示为状态。
通常,容器在以下情况下无法启动:

  1. 应用程序中存在错误,导致其无法启动。
  2. 错误地配置了容器
  3. Liveness 探测失败太多次。

您应该尝试从该容器中检索日志以调查失败的原因。
如果因为容器重启太快而看不到日志,可以使用以下命令:

重击
kubectl logs —previous
它打印来自前一个容器的错误消息。

运行容器错误

容器无法启动时出现错误。
这甚至在容器内的应用程序启动之前。
该问题通常是由于配置错误造成的,例如:

  • 挂载不存在的卷,例如 ConfigMap 或 Secrets。
  • 将只读卷挂载为读写。

您应该使用kubectl describe pod 来检查和分析错误。

处于Pending状态的 Pod

创建 Pod 时,Pod 保持在Pending状态。
为什么?
假设您的调度程序组件运行良好,原因如下:

  1. 集群没有足够的资源(如 CPU 和内存)来运行 Pod。
  2. 当前 Namespace 有一个 ResourceQuota 对象,创建 Pod 将使 Namespace 超过配额。
  3. Pod 绑定到Pending PersistentVolumeClaim。

您最好的选择是检查命令中的事件部分kubectl describe:

重击
kubectl describe pod
对于因 ResourceQuotas 而创建的错误,您可以使用以下命令检查集群的日志:

重击
kubectl get events —sort-by=.metadata.creationTimestamp

处于未就绪状态的 Pod

如果 Pod 正在运行但未就绪,则意味着就绪探测失败。
当 Readiness 探测失败时,Pod 不会附加到 Service,并且不会将流量转发到该实例。
失败的就绪探测是特定于应用程序的错误,因此您应该检查事件部分kubectl describe以识别错误。

2. 故障排除服务

如果您的 Pod 正在RunningReady,但您仍然无法从您的应用收到响应,您应该检查 Service 是否配置正确。
服务旨在根据标签将流量路由到 Pod。
因此,您应该检查的第一件事是 Service 定位了多少 Pod。
您可以通过检查服务中的端点来做到这一点:

重击
kubectl describe service my-service
Name: my-service Namespace: default
Selector: app=my-app
IP: 10.100.194.137
Port: 80/TCP
TargetPort: 8080/TCP
Endpoints: 172.17.0.5:8080
端点是一对,并且应该至少有一个——当服务以(至少)一个 Pod 为目标时。
如果“Endpoints”部分为空,有两种解释:

  1. 您没有任何使用正确标签运行的 Pod(提示:您应该检查您是否在正确的命名空间中)。
  2. selector您在服务的标签中有错字。

如果您看到端点列表,但仍然无法访问您的应用程序,那么targetPort您的服务中的 可能是罪魁祸首。
您如何测试服务?
无论服务类型如何,您都可以使用kubectl port-forward它来连接它:

重击
kubectl port-forward service/ 3000:80
在哪里:

  • 是服务的名称。
  • 3000是您希望在计算机上打开的端口。
  • 80是服务公开的端口。

    3. Ingress 故障排除

    如果您已到达此部分,那么:

  • Pod 正在运行准备就绪

  • Service 将流量分发到 Pod。

但是您仍然看不到来自您的应用的响应。
这意味着很可能 Ingress 配置错误。
由于 Ingress 控制器是集群中的第三方组件,因此根据 Ingress 控制器的类型有不同的调试技术。
但在深入研究 Ingress 特定工具之前,您可以检查一些简单的东西。
Ingress 使用service.nameandservice.port连接到服务。
您应该检查这些配置是否正确。
您可以检查 Ingress 是否正确配置:

重击
kubectl describe ingress my-ingress
Name: my-ingress
Namespace: default
Rules:
Host Path Backends
—— —— ———— *
/ my-service:80 ()
如果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
Ports: 80/TCP, 443/TCP, 8443/TCP
Host Ports: 80/TCP, 443/TCP, 0/TCP
最后,连接到 Pod:

重击
kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 —namespace kube-system
Forwarding from 127.0.0.1:3000 -> 80
Forwarding from [::1]:3000 -> 80
此时,您每次访问计算机上的 3000 端口时,请求都会被转发到 Pod 上的 80 端口。
它现在有效吗?

  • 如果是这样,则问题出在基础架构上。您应该调查流量如何路由到您的集群。
  • 如果它不起作用,则问题出在 Ingress 控制器中。您应该调试 Ingress。

如果你仍然不能让 Ingress 控制器工作,你应该开始调试它。
有许多不同版本的 Ingress 控制器。
流行的选项包括 Nginx、HAProxy、Traefik 等。
您应该查阅 Ingress 控制器的文档以查找故障排除指南。
由于Ingress Nginx是最流行的 Ingress 控制器,我们在下一节中包含了一些关于它的技巧。

调试入口 Nginx

Ingress-nginx 项目有一个Kubectl 的官方插件
您可以kubectl ingress-nginx用来:

  • 检查日志、后端、证书等。
  • 连接到入口。
  • 检查当前配置。

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

  • kubectl ingress-nginx lint,它检查nginx.conf.
  • kubectl ingress-nginx backend, 检查后端(类似于kubectl describe ingress )。
  • kubectl ingress-nginx logs, 检查日志。

请注意,您可能需要为 Ingress 控制器指定正确的命名空间—namespace

概括

如果您不知道从哪里开始,在 Kubernetes 中进行故障排除可能是一项艰巨的任务。
您应该始终记住自下而上地解决问题:从 Pod 开始,然后使用 Service 和 Ingress 向上移动堆栈。
您在本文中学到的相同调试技术可以应用于其他对象,例如:

  • 失败的 Jobs 和 CronJobs。
  • StatefulSets 和 DaemonSets。