注意事项 佩雷夫 :本文是learnk8s项目免费提供的材料的一部分,该项目教公司和个人管理员如何使用Kubernetes。 在其中,项目经理Daniele Polencic明确说明了在K8s集群中运行的应用程序遇到一般问题时应采取的步骤。
TL; DR:这是一个可以帮助您调试Kubernetes中的部署的图表:
查找和修复集群中错误的流程图。 原文(英文)以PDF和图像形式提供 。将应用程序部署到Kubernetes时,通常需要定义三个组件:
- 部署是创建称为Pod的应用程序副本的秘诀。
- 服务 -内部负载平衡器,用于在Pod之间分配流量;
- 入口 -描述流量如何从外界流向服务。
这是一个简短的图形摘要:
1)在Kubernetes中,应用程序通过两层负载均衡器从内部世界接收流量:内部和外部。

2)内部平衡器称为Service,外部平衡器称为Ingress。

3)部署会创建吊舱并对其进行监视(它们不是手动创建的)。

假设您要在
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?
- 是否应该为每个服务创建一个新端口,以免它们冲突?
- 标签名称重要吗? 他们到处都应该一样吗?
在专注于调试之前,让我们回顾一下这三个组件如何相互关联。 让我们从部署和服务开始。
连接部署'a和服务'a
您会感到惊讶,但是部署和服务没有以任何方式连接。 而是,服务直接指向Pod,绕过部署。
因此,我们对Pod和服务如何相互关联感兴趣。 要记住的三件事:
- 服务
selector
必须至少匹配一个Pod标签。 targetPort
必须与Pod内的containerPort
targetPort
相匹配。port
服务可以是任何东西。 不同的服务可以使用同一端口,因为它们具有不同的IP地址。
下图以图形形式表示以上所有内容:
1)想象一下,该服务将流量定向到某个吊舱:

2)创建容器时,必须为容器中的每个容器指定
containerPort
:

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
“部署”部分顶部的“ track: canary
? 应该匹配吗?该标签指的是部署,服务不使用它来路由流量。 换句话说,可以将其删除或分配其他值。
那么matchLabels
选择器呢?它应始终与Pod的标签匹配 ,因为Deployment会使用它来跟踪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
标签。
有什么困难吗?您可以连接到吊舱! 为此,请在kubectl中使用
port-forward
命令。 它允许您连接到服务并检查连接。
kubectl port-forward service/<service name> 3000:80
在这里:
service/<service name>
-服务名称; 就我们而言,这是my-service
;- 3000-您要在计算机上打开的端口;
- 80-服务的
port
字段中指定的port
。
如果能够建立连接,则说明设置正确。
如果无法建立连接,则说明标签有问题或端口不匹配。
服务与入口的连接
提供对应用程序访问权限的下一步与配置Ingress有关。 入口应该知道如何找到服务,然后找到豆荚并将流量引向它们。 Ingress通过名称和开放端口找到所需的服务。
在Ingress和Service的描述中,两个参数必须匹配:
- Ingress中的
servicePort
必须与Service中的port
参数匹配; - Ingress中的
serviceName
必须与Service中的name
字段匹配。
下图总结了端口的连接:
1)如您所知,Service在特定
port
上侦听:

2)Ingress有一个名为
servicePort
的参数:

3)此参数(
servicePort
)应始终与服务定义中的
port
匹配:

4)如果在服务中指定了端口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窗格(它可能引用其他名称空间),然后运行
describe
命令找出端口号:
kubectl describe pod nginx-ingress-controller-6fc5bcc \ --namespace kube-system \ | grep Ports Ports: 80/TCP, 443/TCP, 18080/TCP
最后,连接到吊舱:
kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system
现在,每次您将请求发送到计算机上的端口3000时,它将使用Ingress控制器重定向到Pod的端口80。 通过转到
http:// localhost:3000 ,您应该看到该应用程序创建的页面。
端口摘要
让我们再次记住哪些端口和标签应该匹配:
- 服务定义中的选择器必须与pod标签匹配;
- 服务定义中
targetPort
必须与pod内的containerPort
containerPort相匹配; - 服务定义中的
port
可以是任何东西。 不同的服务可以使用同一端口,因为它们具有不同的IP地址。 servicePort
入口必须与服务定义中的port
匹配;- 服务名称必须与Ingress中的
serviceName
字段匹配。
las,仅仅知道如何正确构造YAML配置还不够。
出问题了怎么办?吊舱可能无法启动或崩溃。
解决Kubernetes中应用程序故障的3个步骤
在调试部署之前,您需要对Kubernetes的工作方式有充分的了解。
由于下载到K8s的每个应用程序中都有三个组件,因此应该从最底部开始以一定顺序对其进行调试。
- 首先,您需要确保吊舱能够正常工作,然后...
- 检查服务是否将流量传递到Pod,然后...
- 检查是否正确配置了Ingress。
视觉呈现:
1)开始寻找问题应该从底部开始。 首先检查Pod是否具有
Ready
和
Running
状态:

2)如果Pod为
Ready
,则应确定服务是否在Pod之间分配流量:

3)最后,您需要分析服务与Ingress之间的连接:

1.吊舱诊断
在大多数情况下,问题出在豆荚上。 确保吊舱处于“
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则没有。
如何理解出了什么问题?有四个用于诊断Pod的有用命令:
kubectl logs < pod'>
允许您从pod的容器中提取日志;kubectl describe pod < pod'>
允许您查看与pod相关的事件列表;kubectl get pod < pod'>
允许您获取存储在Kubernetes中的kubectl get pod < pod'>
的YAML配置;kubectl exec -ti < pod'> bash
允许您在其中一个pod容器中运行交互式命令shell
选择哪一个?事实是,没有通用团队。 这些应结合使用。
常见吊舱问题
pod错误主要有两种类型:启动错误和运行时错误。
启动错误:
ImagePullBackoff
ImageInspectError
ErrImagePull
ErrImageNeverPull
RegistryUnavailable
InvalidImageName
运行时错误:
CrashLoopBackOff
RunContainerError
KillContainerError
VerifyNonRootError
RunInitContainerError
CreatePodSandboxError
ConfigPodSandboxError
KillPodSandboxError
SetupNetworkError
TeardownNetworkError
有些错误比其他错误更常见。 以下是一些常见错误以及解决方法。
ImagePullBackOff
当Kubernetes无法获取其中一个Pod容器的图像时,将出现此错误。 这是最常见的三个原因:
- 图像名称指定不正确-例如,您在其中输入错误,或者图像不存在;
- 指定了不存在的图像标签;
- 该图像存储在私有注册表中,Kubernetes无权访问它。
前两个原因很容易消除-只需修复图像名称和标签即可。 如果是后者,则需要在Secret中输入私有注册表的凭据,并在pod中添加指向它的链接。 Kubernetes文档中
有一个如何完成此操作
的示例 。
CrashLoopBackOff
如果容器无法启动,则Kubenetes将引发CrashLoopBackOff错误。 通常在以下情况下发生:
- 应用程序中存在一个错误,无法启动。
- 容器配置不正确 ;
- 活力测试失败太多次了。
您必须尝试从容器中获取日志,以找出其失败的原因。 如果难以访问日志,因为容器重启太快,可以使用以下命令:
kubectl logs <pod-name> --previous
它显示来自先前容器轮回的错误消息。
RunContainerError
当容器无法启动时,会发生此错误。 它对应于应用程序启动之前的时刻。 通常,其原因是配置不正确,例如:
- 尝试挂载不存在的卷,例如ConfigMap或Secrets;
- 尝试将只读卷挂载为读写卷。
kubectl describe pod <pod-name>
命令非常适合分析此类错误。
暂挂的豆荚
创建后,窗格将保持“
Pending
状态。
为什么会这样呢?以下是可能的原因(我认为调度程序工作正常):
- 群集没有足够的资源(例如处理能力和内存)来运行Pod。
ResourceQuota
对象安装在相应的名称空间中,创建pod会使该名称空间超出配额。- Pod与Pending
PersistentVolumeClaim
绑定。
在这种情况下,建议使用
kubectl describe
命令并检查“
Events
部分:
kubectl describe pod <pod name>
如果发生与
ResourceQuotas
相关的错误,建议使用以下命令查看集群日志
kubectl get events --sort-by=.metadata.creationTimestamp
未准备好豆荚
如果窗格列出为“正在
Running
,但未处于“
Ready
状态,则“就绪”
探针将失败。
发生这种情况时,pod无法连接到服务,流量也不会流向该服务。 准备就绪测试由于应用程序问题而失败。 在这种情况下,要查找错误,您需要分析
kubectl describe
命令输出中的“
Events
部分。
2.服务诊断
如果pod列为
Running
and
Ready
,但应用程序仍然没有响应,则应检查服务设置。
服务根据其标签将流量路由到Pod。 因此,要做的第一件事是检查服务中有多少个Pod。 为此,您可以检查服务中的端点:
kubectl describe service <service-name> | grep Endpoints
端点是一对格式为
<IP-:>
,并且输出中必须存在至少一对这样的值(即,至少一个pod与该服务一起使用)。
如果“
Endpoins
部分
Endpoins
空,则可能有两个选项:
- 没有带有正确标签的Pod(提示:检查是否正确选择了名称空间);
- 选择器中的服务标签有错误。
如果您看到端点列表,但仍然无法访问应用程序,则可能的罪魁祸首是服务描述中
targetPort
中的错误。
如何检查服务的可服务性?无论服务类型如何,都可以使用
kubectl port-forward
命令连接到该服务:
kubectl port-forward service/<service-name> 3000:80
在这里:
<service-name>
- <service-name>
;- 3000-您在计算机上打开的端口;
- 80-服务侧的端口。
3.入口诊断
如果您读过这个地方,那么:
- 豆荚列为“
Running
和“ Ready
; - 该服务成功在Pod之间分配流量。
但是,您仍然无法“扩展”到该应用程序。
这意味着很可能是Ingress控制器的配置不正确。 由于Ingress控制器是集群中的第三方组件,因此根据其类型有多种调试方法。
但是在使用特殊工具配置Ingress之前,您可以做一些非常简单的事情。 Ingress使用
serviceName
和
servicePort
连接到服务。 您必须验证它们是否正确配置。 您可以使用以下命令执行此操作:
kubectl describe ingress <ingress-name>
如果“
Backend
空,则很可能出现配置错误。 如果后端到位,但是仍然无法访问该应用程序,则问题可能与以下方面有关:
- 来自公共Internet的入口可访问性设置;
- 来自公共Internet的群集可访问性设置。
您可以通过直接连接到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
使用
describe
命令设置端口:
kubectl describe pod nginx-ingress-controller-6fc5bcc --namespace kube-system \ | grep Ports
最后,连接到吊舱:
kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system
现在,计算机上对端口3000的所有请求都将重定向到端口80 pod。
现在可以用吗?- 如果是这样,则问题出在基础架构上。 有必要准确找出流量如何路由到群集。
- 如果不是,则问题出在Ingress控制器上。
如果您无法使Ingress控制器正常工作,则必须对其进行调试。
Ingress控制器的种类很多。 最受欢迎的是Nginx,HAProxy,Traefik等
(有关现有解决方案的更多信息,请参见我们的评论 -大约翻译)。您应该使用相应控制器文档中的故障排除指南。 由于
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
-检查日志。
请注意,在某些情况下,可能需要使用
--namespace <name>
标志为Ingress控制器指定正确的名称空间。
总结
如果您不知道从哪里开始,那么诊断Kubernetes可能是一项艰巨的任务。 应该始终按照自下而上的原则来解决该问题:首先是Pod,然后再转到服务和Ingress。 本文中描述的调试方法可以应用于其他对象,例如:
- 闲置的乔布斯和CronJobs;
- StatefulSets和DaemonSets。
感谢
Gergely Risko ,
Daniel Weibel和
Charles Christyraj提出的宝贵意见和补充。
译者的PS
另请参阅我们的博客: