Kubernetes 故障排除三板斧:理解管理和预防

Posted 奇妙的Linux世界

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kubernetes 故障排除三板斧:理解管理和预防相关的知识,希望对你有一定的参考价值。

公众号关注 「奇妙的 Linux 世界
设为「星标」,每天带你玩转 Linux !



Kubernetes 生态系统充斥着各种工具,例如监控、可观察性、跟踪、日志记录等,但一般很难真正理解故障排除与这些工具有何联系。

当故障发生时,我们要掌握是从哪里发生,了解所面临的问题,解决眼前的问题,然后修复根本原因。随着系统规模的扩大,这一切会变得越来越复杂。

一名从事现代、复杂、分布式系统工作的软件工程师,会经常发现,每次出现问题或故障时,都需要了解引发问题的原因以及是谁造成的,但是,这并不是一件容易的事。更难的,是弄清楚幕后发生了什么,以及如何防止它再次发生。一般,我们会这样思考:

  • 究竟发生了什么?
  • 哪些事情是相关的?
  • 什么与我们试图排除故障的特定症状相关?
  • 我们如何确定根本原因?
  • 最终,我们还要确保将来不再发生此问题或类似问题?
  • 本文中,我们将之简化为3个步骤:

  • 理解
  • 管理
  • 预防
  • 我将深入探讨如何实施好这三个步,以及它们如何帮助我们对 Kubernetes 进行故障排除。我还将回顾哪些生态系统工具适合哪个步,以更好地掌握或使用那些工具。

    1第一步:理解

    毫不奇怪,这是重要的一步。理解系统资源,通常使你能够了解发生了什么、出了什么问题以及我们接下来应该做什么。

    为了尝试了解故障原因,开发人员将首先分析系统最近的修改以及可能导致此故障发生的更改。当然,这说起来容易做起来难。在复杂的分布式系统,尤其是基于 Kubernetes 的系统中,这意味着大量使用 kubectl 来对部署日志、跟踪和指标进行故障排除,验证 pod 健康状况和资源上限,以及服务连接,以及其他常见的 pod 错误,检查 YAML 配置文件,验证第三方工具和集成等等。这可能是一行代码、一行配置的更改,触发了故障。

    下图一张图,可以帮助我们在排除 K8s 系统进行故障时,缩小问题的范围。

    图片来源

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

    原文发表于kubernetes中文社区,为作者原创翻译
    更多kubernetes文章,请多关注kubernetes中文社区


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

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

    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。

    2. 创建 Pod 时,你应该为 Pod中的每个容器的containerPort定义端口。

    1. 创建 Service时,你可以定义 一个port和 一个 targetPort

    1. targetPortcontainerPort应该始终匹配。

    1. 如果你的容器端口是 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.

    1. Ingress 有一个名为service.port的字段

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

    1. 如果你决定将端口 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 是否已就绪并正在运行

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sYXA39lj-1640774449939)(https://learnk8s.io/a/6aa1ab46349a22202f6cfe73022e9a12.svg)]

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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5u9p3uiy-1640774449940)(https://learnk8s.io/a/140d05f49bf4347e37f664e370441486.svg)]

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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A8200dcM-1640774449941)(https://learnk8s.io/a/c3f870a281346c4bdc64de1ca81d21a0.svg)]

    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 是RunningReady —— 但是,前两个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.nameservice.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

    以上是关于Kubernetes 故障排除三板斧:理解管理和预防的主要内容,如果未能解决你的问题,请参考以下文章

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

    让Kubernetes落地的“三板斧”

    Kubernetes——污点容忍故障排除

    如何通过科学的方式对 Docker 进行故障检测和排除?

    Ingress Nginx 故障排除

    kubernetes常见故障