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

image.png

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

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


image.png

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是如何相互关联的。

你应该记住三件事:

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

1、考虑以下由 Service 公开的 Pod。

image.png

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


image.png

3、创建 Service时,你可以定义 一个port和 一个 targetPort。


image.png

4、targetPort和containerPort应该始终匹配。


image.png

5、如果你的容器端口是 3000,则targetPort应与该数字匹配。

image.png

如果你查看 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.

image.png

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


image.png

3、 Ingress中的service.port应该匹配Service的port


image.png

4、如果你决定将端口 80 分配给Service,你应该更改service.port为 80

image.png
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 的工作方式有一个明确定义的心理模型。
由于每个部署中都有三个组件,因此你应该从底部开始按顺序调试所有组件。
你应该确保你的 Pod 正在运行,然后
专注于让 Service 将流量路由到 Pod,然后
检查 Ingress 是否正确配置。
你应该从底部开始对Deployment进行故障排除。首先,检查 Pod 是否已就绪并正在运行。


image.png

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

image.png

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

image.png
  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

转载自:https://www.kubernetes.org.cn/9761.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容