2021完整版:Kubernetes Deployment故障排除的可视化指南

2021完整版:Kubernetes Deployment故障排除的可视化指南

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

  • 一个Deployment-用于创建应用程序的副本。
  • 一个Service-内部负载均衡器,负责路由流量到Pod。
  • 一个Ingress-描述流量应该如何从集群外部流入到你的服务。

在 Kubernetes 中,你的应用程序通过两层负载均衡器公开:内部和外部。

  1. 在 Kubernetes 中,你的应用程序通过两层负载均衡器公开:内部和外部。
  2. 内部负载均衡器称为 Service,而外部负载均衡器称为 Ingress。
  3. Pod 不是直接部署的。相反,Deployment 会创建 Pod 并对其进行处理。

假设你希望部署一个简单的Hello World应用程序,此类应用程序的 YAML(hello-world.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:
    name: 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
  • 什么时候应该使用 80 端口,什么时候应该使用 8080 端口?
  • 你是否应该为每个服务创建一个新端口,以免它们发生冲突?
  • 标签名称重要吗?应该都一样吗?

在调试之前,让我们回顾一下这三个组件如何相互链接。

让我们从Deployment 和 Service开始。

连接Deployment 和 Service

令人惊讶的是 Service 和 Deployment 根本没有联系。

相反,Service直接指向 Pod 并完全跳过Deployment。

所以你应该注意的是Pods和Services是如何相互关联的。

你应该记住三件事:

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

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

  1. 考虑以下由 Service 公开的 Pod。Consider the following Pod exposed by a Service.
  1. 创建 Pod 时,你应该为 Pod中的每个容器的containerPort定义端口。创建 Pod 时,你应该为 Pod 中的每个容器定义端口 containerPort。
  2. 创建 Service时,你可以定义 一个port和 一个 targetPort。
  3. targetPort和containerPort应该始终匹配。
  4. 如果你的容器端口是 3000,则targetPort应与该数字匹配。

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

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

由此我们找到开头提到的Deployment和 Service的标签并不匹配。

假设你做出了正确的改变,你如何测试它?

你可以使用以下命令检查 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。

你可以使用kubectl 中的命令port-forward,从集群外部连接到 Service 进行测试。

kubectl port-forward service/<service name> 3000:80
Forwarding from 127.0.0.1:3000 -> 8080
Forwarding from [::1]:3000 -> 8080

说明:

  • service/<service name> 是服务的名称——在本文的 YAML 中是“my-service”。
  • 3000 是你希望在计算机上打开的端口。
  • 80是Service在暴露的port端口。

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

连接 Service和Ingress

公开你的应用程序的下一步是配置 Ingress。

Ingress 通过公开的名称和端口检索正确的 ,然后连接 Pod 并路由流量。

Ingress 和 Service 中应该匹配两件事:

  1. Ingress中的service.port应该匹配Service的port
  2. Ingress中的service.name应该匹配Service的name

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

  1. 通过上文,你已经知道该Service公开了一个port.你已经知道该服务公开了一个端口。
  2. Ingress 有一个名为service.port的字段
  3. Ingress中的service.port应该匹配Service的port
  4. 如果你决定将端口 80 分配给Service,你应该更改service.port为 80
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 端口。

端口回顾

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

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

了解如何构建 YAML 定义只是故事的一部分。

当出现问题时会发生什么?

也许 Pod 没有启动,或者它正在崩溃。

对 Kubernetes 部署进行故障排除的 3 个步骤

在深入调试发生故障的Deployment之前,必须对 Kubernetes 的工作方式有一个明确定义的心理模型。

由于每个部署中都有三个组件,因此你应该从底部开始按顺序调试所有组件。

  1. 你应该确保你的 Pod 正在运行,然后
  2. 专注于让 Service 将流量路由到 Pod,然后
  3. 检查 Ingress 是否正确配置。

你应该从底部开始对Deployment进行故障排除。首先,检查 Pod 是否已就绪并正在运行。

你应该从底部开始对部署进行故障排除。 首先,检查 Pod 是否已准备就绪并正在运行。

如果 Pod 已就绪,你应该检查 Service 是否可以将流量分配给 Pod。

最后,你应该检查 Service 和 Ingress 之间的连接。

1. 对 Pod 进行故障排除

大多数时候,问题出在 Pod 本身。

你应该确保你的 Pod 正在运行 并准备就绪。

你如何检查?

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 是Running和Ready —— 但是,前两个Pod既不是Running也不是Ready。

你如何排查出了什么问题?

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

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

你应该使用哪一个?

没有一种万能的。

相反,你应该使用它们的组合。

常见的 Pod 错误

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

启动错误包括:

  • ImagePullBackoff
  • ImageInspectError
  • ErrImagePull
  • ErrImageNeverPull
  • RegistryUnavailable
  • InvalidImageName

运行时错误包括:

  • CrashLoopBackOff
  • RunContainerError
  • KillContainerError
  • VerifyNonRootError
  • RunInitContainerError
  • CreatePodSandboxError
  • ConfigPodSandboxError
  • KillPodSandboxError
  • SetupNetworkError
  • TeardownNetworkError

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

ImagePullBackOff

当 Kubernetes 无法检索 Pod 容器之一的镜像时,会出现此错误。

常见的罪魁祸首有以下三种:

  1. 镜像名称无效 – 例如,你拼错了名称,或者镜像不存在。
  2. 你为镜像指定了一个不存在的标签。
  3. 你尝试检索的镜像属于私有镜像仓库,Kubernetes 没有访问它的凭据。

前两种情况可以通过更正镜像名称和标签来解决。

最后一个,你应该在你的 Pod 中引用你的私有镜像仓库中的 Secret。

CrashLoopBackOff

如果容器无法启动,Kubernetes 会将 CrashLoopBackOff 作为状态显示。

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

  1. 应用程序中存在阻止其启动的错误。
  2. 错误地配置了容器
  3. Liveness 探针失败的次数太多。

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

如果因为容器重启太快而看不到日志,可以使用以下命令:

kubectl logs <pod-name> --previous

它会打印来自前一个容器的错误消息。

运行容器错误

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

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

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

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

你应该使用kubectl describe pod <pod-name>来检查和分析错误。

处于Pending状态的Pod

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

为什么?

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

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

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

kubectl describe pod <pod name>

对于因 ResourceQuotas 而产生的错误,你可以使用以下命令检查集群的日志:

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

处于not Ready状态的Pod

如果 Pod 正在运行但未就绪,则意味着Readiness探针失败。

当 Readiness 探针失败时,Pod 不会附加到服务,并且没有流量转发到该实例。

失败的就绪探针是特定于应用程序的错误,因此你应该通过kubectl describe命令查看事件以识别错误。

2.对 Service 进行故障排除

如果你的 Pod 正在运行 并准备就绪,但你仍然无法从你的应用程序收到响应,你应该检查服务是否配置正确。

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

因此,你应该检查的第一件事是Service匹配的 Pod 。

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

kubectl describe service my-service
Name:                     my-service
Namespace:                default
Selector:                 app=my-app
IP:                       10.100.194.137
Port:                     <unset>  80/TCP
TargetPort:               8080/TCP
Endpoints:                172.17.0.5:8080

一个endpoint 是一对<ip address:port>,并且应该至少有一个。

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

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

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

你如何测试服务?

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

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

在哪里:

  • <service-name> 是服务的名称。
  • 3000 是你希望在计算机上打开的端口。
  • 80 是服务公开的端口。

3. 对 Ingress 进行故障排除

如果你已到达此部分,则:

  • Pod 正在运行并准备就绪。
  • Service 将流量分配给 Pod。

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

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

由于 Ingress 控制器是集群中的第三方组件,因此根据 Ingress 控制器的类型有不同的调试技术。

但在深入研究 Ingress 特定工具之前,你可以检查一些简单的东西。

Ingress 使用service.name和service.port连接到服务。

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

你可以检查 Ingress 是否正确配置:

kubectl describe ingress my-ingress
Name:             my-ingress
Namespace:        default
Rules:
  Host        Path  Backends
  ----        ----  --------
  *
              /   my-service:80 (<error: endpoints "my-service" not found>)

如果Backends列是空的,那么一定是配置有错误。

如果你可以在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 控制器,我们在下一节中包含了一些关于它的调试技巧。

调试 Ingress Nginx

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

你可以使用kubectl ingress-nginx命令来:

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

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

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

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

总结

Kubernetes 中的故障排除可能是一项艰巨的任务。你应该始终记住自下而上地解决问题:从 Pod 开始,然后排查 Service 和 Ingress 。

你在本文中学到的故障排除技术也可以应用于其他对象,例如:

  • Jobs 和 CronJobs。
  • StatefulSet 和 DaemonSet。

译文链接: https://learnk8s.io/troubleshooting-deployments

K8S中文社区微信公众号

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址