笔记。 翻译。:本文是在公共领域发布的项目材料的一部分
TL;DR:这是一个图表,可以帮助您调试 Kubernetes 中的部署:
查找和修复集群中错误的流程图。 原文(英文)可在
将应用程序部署到 Kubernetes 时,通常需要定义三个组件:
- 部署 - 这是一种创建应用程序副本的方法,称为 pod;
- 服务 — 在 Pod 之间分配流量的内部负载均衡器;
- 入口 — 流量如何从外部世界到达服务的描述。
这是一个快速的图形摘要:
1)在Kubernetes中,应用程序通过两层负载均衡器接收来自外界的流量:内部和外部。
2)内部均衡器称为Service,外部均衡器称为Ingress。
3) Deployment 创建 Pod 并监控它们(它们不是手动创建的)。
假设您想部署一个简单的应用程序 你好世界。 它的 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?
- 我应该为每个服务创建一个新端口,这样它们就不会发生冲突吗?
- 标签名称重要吗? 它们应该在任何地方都一样吗?
在专注于调试之前,让我们记住这三个组件如何相互关联。 让我们从部署和服务开始。
部署与服务的关系
您会感到惊讶,但部署和服务根本没有联系。 相反,Service 绕过 Deployment 直接指向 Pod。
因此,我们感兴趣的是 Pod 和 Services 如何相互关联。 要记住三件事:
- 选择器(
selector
) 服务必须至少匹配一个 Pod 标签。 -
targetPort
必须匹配containerPort
Pod 内的容器。 -
port
服务可以是任何东西。 不同的服务可以使用相同的端口,因为它们具有不同的IP地址。
下图以图形形式表示了上述所有内容:
1) 假设该服务将流量定向到某个 pod:
2)创建pod时,必须指定 containerPort
对于 Pod 中的每个容器:
3)创建服务时,必须指定 port
и targetPort
. 但使用哪一个来连接容器呢?
4)通过 targetPort
。 必须匹配 containerPort
.
5) 假设容器中打开了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 # <<<
标签怎么样 track: canary
在部署部分的顶部? 应该匹配吗?
此标签是特定于部署的,服务不使用它来路由流量。 换句话说,它可以被删除或分配不同的值。
选择器怎么样 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! 为此,您需要使用命令 port-forward
在 kubectl. 它允许您连接到服务并检查连接。
kubectl port-forward service/<service name> 3000:80
在这里:
-
service/<service name>
- 服务名称; 在我们的例子中是my-service
; - 3000是电脑上需要开放的端口;
- 80 - 字段中指定的端口
port
服务。
如果连接已建立,则设置正确。
如果连接失败,则表明标签有问题或端口不匹配。
Service与Ingress的关系
提供对应用程序的访问的下一步涉及设置 Ingress。 Ingress 需要知道如何找到服务,然后找到 Pod 并将流量定向到它们。 Ingress 通过名称和开放端口查找所需的服务。
Ingress 和 Service 的描述中两个参数必须匹配:
-
servicePort
Ingress 中必须匹配参数port
服务中; -
serviceName
Ingress 中的字段必须匹配name
在服务中。
下图总结了端口连接:
1) 如您所知,Service 会监听特定的 port
:
2)Ingress有一个参数叫 servicePort
:
3)该参数(servicePort
) 必须始终匹配 port
在服务定义中:
4) 如果Service中指定了80端口,则需要 servicePort
也等于 80:
实际操作中,需要注意以下几行:
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 控制器而不是服务。
首先,您需要使用 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(它可能位于不同的命名空间中)并运行命令 describe
找出端口号:
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。 通过去
端口总结
让我们再次记住哪些端口和标签必须匹配:
- Service 定义中的选择器必须与 pod 的标签匹配;
-
targetPort
定义中的服务必须匹配containerPort
pod 内的容器; -
port
定义中的服务可以是任何东西。 不同的服务可以使用相同的端口,因为它们具有不同的IP地址; -
servicePort
入口必须匹配port
在服务的定义中; - 服务名称必须与字段匹配
serviceName
在入口处。
不幸的是,仅仅知道如何正确构建 YAML 配置是不够的。
当出现问题时会发生什么?
Pod 可能无法启动或崩溃。
诊断 Kubernetes 中应用程序问题的 3 个步骤
在开始调试部署之前,您需要充分了解 Kubernetes 的工作原理。
由于K8s中下载的每个应用程序都具有三个组件,因此应该按照一定的顺序对它们进行调试,从最底层开始。
- 首先,您需要确保 Pod 正常工作,然后......
- 检查服务是否向 Pod 提供流量,然后...
- 检查 Ingress 配置是否正确。
视觉表现:
1)你应该从最底层开始寻找问题。 首先检查 pod 是否有状态 Ready
и Running
:
2) 如果 Pod 准备就绪 (Ready
),您应该查明该服务是否在 Pod 之间分配流量:
3)最后需要分析服务与Ingress的连接:
1. Pod 的诊断
大多数情况下,问题与 Pod 有关。 确保 Pod 列为 Ready
и Running
。 您可以使用以下命令进行检查:
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:
-
kubectl logs <имя pod'а>
允许您从 pod 中的容器中提取日志; -
kubectl describe pod <имя pod'а>
允许您查看与 pod 关联的事件列表; -
kubectl get pod <имя pod'а>
允许您获取存储在 Kubernetes 中的 pod 的 YAML 配置; -
kubectl exec -ti <имя pod'а> bash
允许您在 pod 容器之一中启动交互式命令 shell
你应该选择哪一个?
事实是,不存在通用命令。 应结合使用这些方法。
典型的 Pod 问题
Pod 错误主要有两种类型:启动错误和运行时错误。
启动错误:
-
ImagePullBackoff
-
ImageInspectError
-
ErrImagePull
-
ErrImageNeverPull
-
RegistryUnavailable
-
InvalidImageName
运行时错误:
-
CrashLoopBackOff
-
RunContainerError
-
KillContainerError
-
VerifyNonRootError
-
RunInitContainerError
-
CreatePodSandboxError
-
ConfigPodSandboxError
-
KillPodSandboxError
-
SetupNetworkError
-
TeardownNetworkError
有些错误比其他错误更常见。 以下是一些最常见的错误以及解决方法。
图像拉回关闭
当 Kubernetes 无法获取其中一个 pod 容器的映像时,就会出现此错误。 以下是三个最常见的原因:
- 图片名称不正确 - 例如,您输入错误,或者图片不存在;
- 为图像指定了不存在的标签;
- 该镜像存储在私有注册表中,Kubernetes 无权访问它。
前两个原因很容易消除 - 只需更正图像名称和标签即可。 对于后者,您需要在 Secret 中输入封闭注册表的凭据,并在 pod 中添加指向它的链接。 在 Kubernetes 文档中
崩溃循环回退
Kubenetes 抛出错误 CrashLoopBackOff
,如果容器无法启动。 这通常发生在以下情况:
- 应用程序中存在一个错误,导致其无法启动;
- 容器
配置不正确 ; - 活性测试失败次数过多。
您必须尝试从容器中获取日志以找出其失败的原因。 如果由于容器重启太快导致日志访问困难,可以使用以下命令:
kubectl logs <pod-name> --previous
它显示容器先前版本的错误消息。
运行容器错误
当容器无法启动时会出现此错误。 它对应于应用程序启动之前的时刻。 通常是由于设置不正确造成的,例如:
- 尝试挂载不存在的卷,例如 ConfigMap 或 Secrets;
- 尝试将只读卷安装为读写。
该团队非常适合分析此类错误 kubectl describe pod <pod-name>
.
Pod 处于 Pending 状态
创建后,Pod 保持在状态 Pending
.
这是为什么发生?
以下是可能的原因(我假设调度程序工作正常):
- 集群没有足够的资源(例如处理能力和内存)来运行 Pod。
- 该对象安装在适当的命名空间中
ResourceQuota
并且创建 pod 会导致命名空间超出配额。 - Pod 绑定为 Pending
PersistentVolumeClaim
.
在这种情况下,建议使用命令 kubectl describe
并检查该部分 Events
:
kubectl describe pod <pod name>
如果出现与以下相关的错误 ResourceQuotas
,建议使用命令查看集群日志
kubectl get events --sort-by=.metadata.creationTimestamp
Pod 尚未就绪
如果 pod 列为 Running
,但未处于状态 Ready
,意味着检查其准备情况 (就绪探针) 失败。
发生这种情况时,Pod 不会连接到服务,并且没有流量流向它。 就绪测试失败是由应用程序中的问题引起的。 在这种情况下,要找到错误,您需要分析该部分 Events
在命令输出中 kubectl describe
.
2. 服务诊断
如果 pod 列为 Running
и Ready
,但应用程序仍然没有响应,您应该检查服务设置。
服务负责根据 Pod 的标签将流量路由到 Pod。 因此,您需要做的第一件事是检查有多少 Pod 使用该服务。 为此,您可以检查服务中的端点:
kubectl describe service <service-name> | grep Endpoints
端点是一对形式的值 <IP-адрес:порт>
,并且输出中必须至少存在一对这样的(即,至少有一个 Pod 与该服务一起工作)。
如果节 Endpoins
空,有两种选择:
- 没有具有正确标签的 Pod(提示:检查命名空间是否选择正确);
- 选择器中的服务标签有错误。
如果您看到端点列表但仍然无法访问应用程序,则可能的罪魁祸首是 targetPort
在服务描述中。
如何检查服务的功能?
无论服务类型如何,都可以使用命令 kubectl port-forward
连接到它:
kubectl port-forward service/<service-name> 3000:80
在这里:
-
<service-name>
- 服务名称; - 3000是你在电脑上打开的端口;
- 80 - 服务端端口。
3. 入口诊断
如果您已经读到这里,那么:
- pod 列为
Running
иReady
; - 该服务成功在 Pod 之间分配流量。
但是,您仍然无法访问该应用程序。
这意味着 Ingress 控制器很可能未正确配置。 由于Ingress控制器是集群中的第三方组件,因此根据其类型有不同的调试方法。
但在使用特殊工具来配置 Ingress 之前,您可以做一些非常简单的事情。 入口用途 serviceName
и servicePort
连接到该服务。 您需要检查它们是否配置正确。 您可以使用以下命令执行此操作:
kubectl describe ingress <ingress-name>
如果列 Backend
为空,很有可能配置错误。 如果后端已就位,但应用程序仍然无法访问,则问题可能与以下内容有关:
- 从公共互联网进入辅助功能设置;
- 来自公共互联网的集群可访问性设置。
您可以通过直接连接到 Ingress Pod 来识别基础设施的问题。 为此,首先找到 Ingress Controller 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
使用命令 describe
设置端口:
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 控制器有很多种。 最流行的是 Nginx、HAProxy、Traefik 等。 (有关现有解决方案的更多信息,请参阅
调试 Ingress Nginx 控制器
Ingress-nginx项目有官方的 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 开始,然后继续到服务和 Ingress。 本文中描述的调试技术可以应用于其他对象,例如:
- 空闲作业和 CronJobs;
- StatefulSet 和 DaemonSet。
我表达我的感激之情
译者PS
另请阅读我们的博客:
- «
用于在 Kubernetes Pod 中进行调试的 kubectl-debug 插件 “; - «
Kubernetes 运行中的 6 个有趣的系统错误 [及其解决方案] “; - «
为 Kubernetes 上运行的应用程序开发人员提供的工具 “; - «
SRE 日常生活中的 6 个实用故事 “。
来源: habr.com