写点什么

云原生容器编排问题盘点,总结分享年度使用 Kubernetes 的坑和陷阱

作者:洛神灬殇
  • 2023-12-31
    江苏
  • 本文字数:3877 字

    阅读完需:约 13 分钟

云原生容器编排问题盘点,总结分享年度使用Kubernetes的坑和陷阱

Kubernetes 与云原生

随着云原生的兴起,越来越多的应用选择基于 Kubernetes 进行部署,可以说 Kubernetes 是最流行的容器编排和部署平台。它的强大功能特性,可以保障在生产中可靠地运行容器化应用程序,相关的 DevOps 等工具也应运而生,下面就是小编简单化了一个 Kubernetes 的逻辑架构图。



如何开发面向 Kubernetes 部署和运维的微服务应用是很多开发者与架构师要解决的问题。通过本文的阅读,作者介绍了在 Kubernetes 体系下构建高效、可靠的微服务应用所遇到的种种问题。希望这篇文章能够对您有所帮助。


总体问题大纲分布如下说是:



在接下来的内容中,我们将探讨今年个人遇到的 5 个常见的 Kubernetes 问题和错误。通过识别并避免这些挑战,您将能够提高应用程序的可扩展性、可靠性和安全性,同时更好地控制集群及其部署。

性能问题:忽略节点选择器导致调度效率低下

整个集群效能的表现关键在于 Pod 是否能被精准地部署至适宜的节点上。在众多的集群配置中,常常包含多样化的节点类型,比如那些专为常规应用程序设计的小型内存和低配 CPU 节点以及针对高密度后台服务所配置的大型内存和高配 CPU 节点

问题排查和分析

  • 首先,我们一定要侧重分析当前节点池的利用率和资源分配情况,确定是否存在未充分利用的较小节点。

  • 如果存在未充分利用的较小节点,使用自动化工具进行节点重分配。将该节点上运行的负载迁移到其他节点上,以实现节点资源的最优使用。

  • 最后,在节点迁移之前,需再三确保目标节点有足够的资源来承载额外的负载。


注意:考虑负载迁移对运行中应用的影响,并确保其在迁移过程中不会中断

解决方案

为了避免出现这个问题,我们可以使用一种有效的方法来管理 Pod 的调度,即通过在节点上设置标签,并使用节点选择器将 Pod 分配给兼容的节点。这种方法可以确保 Pod 被正确地调度到具备所需资源和能力的节点上。


案例介绍

首先,我们为节点设置适当的标签。标签可以根据节点的特性、硬件配置或其他自定义需求进行定义。


例如,可以为具备高性能 GPU 的节点设置一个标签,或者为具备特定版本的软件组件的节点设置一个标签。


接下来,在 Pod 的定义中添加一个节点选择器。节点选择器是一组标签键值对,用于指定 Pod 所需的节点属性或条件。


例如,可以指定 Pod 需要运行在具备某个特定标签的节点上。


当调度程序接收到新的 Pod 创建请求时,它将根据 Pod 的节点选择器进行匹配,并将 Pod 分配给满足条件的节点。这样,Pod 就能够被正确、高效地调度到合适的节点上,避免了资源浪费和性能问题。


apiVersion: v1kind: Podmetadata:  name: pod-node-selector-samplespec:  containers:    - name: tomcat      image: tomcat:latest  nodeSelector:    node-class: middleLevel
复制代码


为了确保 Pod 只会调度到设置了标签的节点(例如 node-class: middleLevel),我们可以使用 kubectl 命令在匹配的节点上设置标签。


首先我们先使用 kubectl 命令列出当前可用的节点


kubectl get nodes
复制代码


之后,找到您想要为其添加标签的特定节点。使用 kubectl 命令在该节点上设置标签。你可以使用以下命令格式:


kubectl label nodes <节点名称> <标签键>=<标签值>
复制代码


此外,如果节点的名称是 node-1,要将标签 node-class 设置为 middleLevel,您可以运行以下命令:


kubectl label nodes node-1 node-class=middleLevel
复制代码


这将在节点 node-1 上设置了一个标签 node-class,其值为 middleLevel。

配置问题:应用服务端口与 Service(KubectlProxy)控制的端口不一致

在我们的运行环境中,确保 Service 将流量路由到对应的 Pod 上的正确端口非常重要。为了解决这个问题,您需要确保 Service 的端口定义与 Pod 容器的端口一致。


以下是一个错误的配置 Service 和 Pod 的配置文件:


apiVersion: v1kind: Podmetadata:  name: test-pod  labels:    app: test-appspec:  image: tomcat:latest  ports:    - containerPort: 8081
---
apiVersion: v1kind: Servicemetadata: name: test-servicespec: selector: app: test-app ports: - protocol: TCP port: 9000 targetPort: 8082
复制代码


在上述示例中,Service 的目标端口(targetPort)设置为 8081,与 Pod 容器的实际端口(containerPort:8081)不一致。通过调整 Service 的配置,确保正确路由流量到 Pod 的适当端口,将有助于保证应用程序的正常运行和可靠性。


正确的配置如下所示:


apiVersion: v1kind: Podmetadata:  name: test-pod  labels:    app: test-appspec:  image: tomcat:latest  ports:    - containerPort: 8081
---
apiVersion: v1kind: Servicemetadata: name: test-servicespec: selector: app: test-app ports: - protocol: TCP port: 9000 targetPort: 8081
复制代码

隔离问题:容器组件部署到 K8S 集群错误的命名空间或者默认空间(建议)

Kubernetes 命名空间在集群中提供了一定程度的隔离,将一组服务逻辑分组在一起。在使用命名空间时,请记住为每个服务和 kubectl 命令指定目标命名空间。如果没有指定目标命名空间,将默认使用 default 命名空间。


如果服务没有部署在合适的命名空间下,就会导致相关的服务器请求无法到达,在这里给大家看一个逻辑结构图就可以了解到:



为了避免这个问题,请确保在部署和管理服务时,建议大家始终使用正确的命名空间。在执行 kubectl 命令时,可以使用--namespace参数来指定目标命名空间。例如:


kubectl get pods --namespace=test-namespace
复制代码


这将列出位于 test-namespace 命名空间下的 Pods。此外,在创建和管理服务时,也要确保正确指定目标命名空间。例如,在创建 Deployment 时,可以在 yaml 文件中使用namespace字段指定目标命名空间:


apiVersion: apps/v1kind: Deploymentmetadata:  name: test-app  namespace: test-namespace...
复制代码


确保 Deployment 部署在 test-namespace 命名空间下。通过正确使用命名空间,可以保证每个服务被正确隔离,并且相关的服务器请求可以顺利到达。

资源问题:不进行设置资源请求和限制的 Pod(命名空间也没有控制)

在 Kubernetes 集群中,正确地管理资源对于保持整个集群的稳定性非常重要。默认情况下,Pod 并没有任何资源限制,除非我们显式地对其进行适当的配置,否则可能会导致 Node 节点的 CPU 和内存资源耗尽。

解决方案

在所有的 Pod 上设置适当的资源请求和限制,以减少资源竞争的问题。通过指定资源请求,Kubernetes 可以为我们的 Pod 预留特定数量的资源,从而避免将其调度到无法提供足够容量的节点上。

设置资源限制

限制 Pod 使用的最大资源量,当 Pod 超过 CPU 或内存限制时,Kubernetes 会对其进行限制,例如,限制超过 CPU 限制的 Pod 的处理能力,或者当达到内存限制时触发内存不足 (OOM) 来终止 Pod 的运行。下面是一个示例,展示如何在 Pod 的配置中设置资源请求和限制的参数:


apiVersion: v1kind: Podmetadata:  name: test-podspec:  containers:  - name: test-server    image: test-image    resources:      requests:        cpu: "0.5"        memory: "1Gi"      limits:        cpu: "1"        memory: "2Gi"
复制代码

参数解释:

  • limits:Pod 对 CPU 和内存的最大限制。

  • requests:Pod 对 CPU 和内存的最小需求

  • cpu: 0.5 或 500m:表示 0.5 个 CPU 核心或 500 毫核(mCPU)的 CPU 资源请求/限制。

  • 1:表示 1 个整数 CPU 核心的 CPU 资源请求/限制。


注意:请根据具体需求和应用程序的资源消耗情况,来设置适当的资源请求和限制,以确保集群的稳定性和有效的资源利用。

状态问题:优化和使用 Liveness 和 Readiness 探针

Kubernetes 探针是一种能够增加应用程序弹性的重要工具。它们可以向 Kubernetes Pod 报告应用程序的健康状况。

Liveness 探针

当容器出现问题时(例如内存溢出)或 Liveness 探针的请求超时,Liveness 探针会通知 Kubernetes 重新启动容器,以确保应用程序的可用性。

Readiness 探针

Kubernetes 提供了 Readiness 探针来发现并处理这些情况。容器所在的 Pod 会报告其未就绪状态的信息,并且将不接收来自 Kubernetes Service 的流量。


例如:应用程序在启动时可能需要加载大量数据或配置文件,或者在启动后需要等待外部服务。在这种情况下,我们既不希望停止应用程序的运行,也不希望将请求发送到它。


通过合理配置 Liveness 和 Readiness 探针,我们能够更好地监测和管理应用程序的状态,提高应用程序的可用性,并确保容器在适当的时候进行重新启动,从而提高整体系统的稳定性。


以下是一个示例,展示了如何在 Pod 的配置中设置 Liveness 和 Readiness 探针:


apiVersion: v1kind: Podmetadata:  name: test-podspec:  containers:  - name: test-server    image: test-image    livenessProbe:      httpGet:        path: /health        port: 80      initialDelaySeconds: 3      periodSeconds: 5    readinessProbe:      httpGet:        path: /readiness        port: 80      initialDelaySeconds: 5      periodSeconds: 10
复制代码


所以首先探针一定要配置,此外要应对 IO 密集型以及 CPU 密集型等场景进行设置对应的参数值,切勿一套配置 Cover 主全场,那是不可能的。对此我深有体会,总是出现服务负荷过高的时候,总是被探针 Kill 掉。


我的建议是:对于敏感度不高,且不是核心数据服务(无状态化的),可以酌量调大一些。肺腑之言。

最后总结

以上是作者根据个人经验和使用中出现的一些问题和错误的总结。希望其他开发者能够从中受益,避免犯类似的错误。我们选择使用 Kubernetes 的原因之一就是它具备弹性扩容的能力。正确的配置可以使 Kubernetes 在需求高峰期自动添加新的 Pod 和节点,实现动态的水平扩容和垂直扩容。然而,不幸的是,许多团队在自动扩容方面存在一些不可预测性的问题。

发布于: 刚刚阅读数: 6
用户头像

洛神灬殇

关注

🏆 InfoQ写作平台-签约作者 🏆 2020-03-25 加入

👑 后端技术架构师,前优酷资深工程师 📕 个人著作《深入浅出Java虚拟机—JVM原理与实战》 💻 10年开发经验,参与过多个大型互联网项目,定期分享技术干货和项目经验

评论

发布
暂无评论
云原生容器编排问题盘点,总结分享年度使用Kubernetes的坑和陷阱_Kubernetes_洛神灬殇_InfoQ写作社区