暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Kubernetes Deployment故障排除图解指南

TonyBai 2021-01-23
1258




下面是一个示意图,可帮助你调试Kubernetes Deployment(你可以在此处下载它的PDF版本 https://tonybai.com/wp-content/uploads/k8s-deployment-troubleshooting/troubleshooting-kubernetes.pdf)。


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

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

下面让我们用示意图快速总结一下要点。




在Kubernetes中,你的应用程序通过两层负载均衡器暴露服务:内部的和外部的




内部的负载均衡器称为Service,而外部的负载均衡器称为Ingress




Pod不会直接部署。Deployment会负责创建Pod并管理它们

假设你要部署一个简单的"HelloWorld"应用,该应用的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/v1beta1
    kind: Ingress
    metadata:
    name: my-ingress
    spec:
    rules:
    - http:
    paths:
    - backend:
    serviceName: app
    servicePort: 80
    path: /


    这个定义很长,组件之间的相互关系并不容易看出来。

    例如:

    什么时候应使用端口80,又是何时应使用端口8080?你是否应该为每个服务创建一个新端口以免它们相互冲突?标签(label)名重要吗?它们是否在每一处都应该是一样的?

    在进行调试之前,让我们回顾一下这三个组件是如何相互关联的。

    让我们从Deployment和Service开始。

    一. 连接Deployment和Service

    令人惊讶的消息是,Service和Deployment之间根本没有连接。

    事实是:Service直接指向Pod,并完全跳过了Deployment。

    因此,你应该注意的是Pod和Service之间的相互关系。

    你应该记住三件事:

    Service selector应至少与Pod的一个标签匹配;Service的targetPort应与Pod中容器的containerPort匹配;Service的port可以是任何数字。多个Service可以使用同一端口号,因为它们被分配了不同的IP地址。

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




    考虑上面被一个服务暴露的Pod



    创建Pod时,应为Pod中的每个容器定义containerPort端口




    当创建一个Service时,你可以定义port和targetPort,但是哪个用来连接容器呢?




    targetPort和containerPort应该始终保持匹配




    如果容器暴露3000端口(containerPort),那么targetPort应该匹配这一个端口号

    再来看看YAML,标签和ports/targetPort应该匹配:

      // 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:
      any-name: my-app

      那deployment顶部的track: canary标签呢?

      它也应该匹配吗?

      该标签属于deployment,service的选择器未使用它来路由流量。

      换句话说,你可以安全地删除它或为其分配其他值。

      matchLabels选择器呢?

      它必须始终与Pod的标签匹配,并且被Deployment用来跟踪Pod。

      假设你已经进行了所有正确的设置,该如何测试它呢?

      你可以使用以下命令检查Pod是否具有正确的标签:

        $ kubectl get pods --show-labels

        或者,如果你拥有属于多个应用程序的Pod:

          $ kubectl get pods --selector any-name=my-app --show-labels

          any-name=my-app就是标签:any-name: my-app

          还有问题吗?

          你也可以连接到Pod!

          你可以使用kubectl中的port-forward命令连接到service并测试连接。

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

            service/ 是服务的名称- 在上面的YAML中是“my-service”3000是你希望在计算机上打开的端口80是service通过port字段暴露的端口

            如果可以连接,则说明设置正确。

            如果不行,则很可能是你填写了错误的标签或端口不匹配。

            二. 连接Service和Ingress

            接下来是配置Ingress以将你的应用暴露到集群外部。

            Ingress必须知道如何检索服务,然后检索Pod并将流量路由给它们。

            Ingress按名字和暴露的端口检索正确的服务。

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

            Ingress的servicePort应该匹配service的portIngress的serviceName应该匹配服务的name

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




            你已经知道servive暴露一个port




            Ingress有一个字段叫servicePort




            service的port和Ingress的service应该始终保持匹配



            如果你为service指定的port是80,那么你也应该将ingress的servicePort改为80

            实践中,你应该查看以下几行(下面代码中的my-service和80):

              // hello-world.yaml


              apiVersion: v1
              kind: Service
              metadata:
              name: my-service --- 需关注
              spec:
              ports:
              - port: 80 --- 需关注
              targetPort: 8080
              selector:
              any-name: my-app
              ---
              apiVersion: networking.k8s.io/v1beta1
              kind: Ingress
              metadata:
              name: my-ingress
              spec:
              rules:
              - http:
              paths:
              - backend:
              serviceName: my-service --- 需关注
              servicePort: 80 --- 需关注
              path: /

              你如何测试Ingress是否正常工作呢?

              你可以使用与以前相同的策略kubectl port-forward,但是这次你应该连接到Ingress控制器,而不是连接到Service。

              首先,使用以下命令检索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

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

                    如果访问http://localhost:3000,则应找到提供网页服务的应用程序。

                    回顾Port

                    快速回顾一下哪些端口和标签应该匹配:

                    service selector应与Pod的标签匹配service的targetPort应与Pod中容器的containerPort匹配service的端口可以是任何数字。多个服务可以使用同一端口,因为它们分配了不同的IP地址。ingress的servicePort应该匹配service的portserivce的名称应与ingress中的serviceName字段匹配

                    知道如何构造YAML定义只是故事的一部分。

                    出了问题后该怎么办?

                    Pod可能无法启动,或者正在崩溃。

                    三. kubernetes deployment故障排除的3个步骤

                    在深入研究失败的deployment之前,我们必须对Kubernetes的工作原理有一个明确定义的思维模型。

                    由于每个deployment中都有三个组件,因此你应该自下而上依次调试所有组件。

                    你应该先确保Pods正在运行然后,专注于让service将流量路由到到正确的Pod然后,检查是否正确配置了Ingress




                    你应该从底部开始对deployment进行故障排除。首先,检查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处于就绪并正常运行的状态;但是,前两个Pod既不处于Running也不是Ready。

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

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

                      kubectl logs 有助于检索Pod容器的日志kubectl describe pod 检索与Pod相关的事件列表很有用kubectl get pod 用于提取存储在Kubernetes中的Pod的YAML定义kubectl exec -ti bash 在Pod的一个容器中运行交互式命令很有用

                      应该使用哪一个呢?

                      没有一种万能的。

                      相反,我们应该结合着使用它们。

                      常见Pod错误

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

                      启动错误包括:

                      ImagePullBackoffImageInspectErrorErrImagePullErrImageNeverPullRegistryUnavailableInvalidImageName

                      运行时错误包括:

                      CrashLoopBackOffRunContainerErrorKillContainerErrorVerifyNonRootErrorRunInitContainerErrorCreatePodSandboxErrorConfigPodSandboxErrorKillPodSandboxErrorSetupNetworkErrorTeardownNetworkError

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

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

                      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.confkubectl ingress-nginx backend,以检查后端(类似于kubectl describe ingress kubectl ingress-nginx logs,查看日志

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

                                        四. 总结

                                        如果你不知道从哪里开始,那么在Kubernetes中进行故障排除可能是一项艰巨的任务。

                                        你应该始终牢记从下至上解决问题:从Pod开始,然后通过Service和Ingress向上移动堆栈。

                                        你在本文中了解到的调试技术也可以应用于其他对象,例如:

                                        failing Job和CronJobStatefulSets和DaemonSets

                                        本文翻译自learnk8s上的文章A visual guide on troubleshooting Kubernetes deployments。


                                        我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网上线了,感谢小伙伴们学习支持!https://coding.imooc.com/class/284.html

                                        我爱发短信:企业级短信平台定制开发专家 https://51smspush.com/ smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展;短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。

                                        著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

                                        Gopher Daily(Gopher每日新闻)归档仓库 - https://github.com/bigwhite/gopherdaily

                                        我的联系方式:

                                        微博:https://weibo.com/bigwhite20xx 微信公众号:iamtonybai 博客:tonybai.com github: https://github.com/bigwhite

                                        微信赞赏:



                                        商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。


                                        文章转载自TonyBai,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                        评论