25

Kubernetes Deployment故障排除图解指南

 4 years ago
source link: https://tonybai.com/2019/12/08/k8s-deployment-troubleshooting/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

troubleshooting-deployments-1.png

下面是一个示意图,可帮助你调试Kubernetes Deployment(你可以在此处下载它的PDF版本)。

troubleshooting-deployments-2.png

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

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

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

troubleshooting-deployments-3.png

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

troubleshooting-deployments-4.png

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

troubleshooting-deployments-5.png

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地址。

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

troubleshooting-deployments-6.png

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

troubleshooting-deployments-7.png

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

troubleshooting-deployments-8.png

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

troubleshooting-deployments-9.png

targetPort和containerPort应该始终保持匹配

troubleshooting-deployments-10.png

如果容器暴露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的 port
  • Ingress的 serviceName 应该匹配服务的 name

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

troubleshooting-deployments-11.png

你已经知道servive暴露一个port

troubleshooting-deployments-12.png

Ingress有一个字段叫servicePort

troubleshooting-deployments-13.png

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

troubleshooting-deployments-14.png

如果你为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的port
  • serivce的名称应与ingress中的serviceName字段匹配

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

出了问题后该怎么办?

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

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

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

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

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

troubleshooting-deployments-15.png

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

troubleshooting-deployments-16.png

如果Pod已就绪,则应调查service是否可以将流量分配给Pod。

troubleshooting-deployments-17.png

最后,你应该检查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可能会出现启动和运行时错误。

启动错误包括:

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

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

四. 总结

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

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

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

  • failing Job和CronJob
  • StatefulSets和DaemonSets

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

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

我爱发短信 :企业级短信平台定制开发专家 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

微信赞赏:

wechat-zanshang-code-512x512.jpg

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

© 2019,bigwhite. 版权所有.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK