当前位置: 欣欣网 > 码农

Kubernetes 防不胜防的 11 个陷阱

2024-05-07码农

引入

如何开启应用Kubernetes的第一步?Kubernetes是一套功能强大的工具,用于管理分布式云原生应用程序,并保证其自动扩展和高可用性。然而,许多用户在使用Kubernetes时往往会犯一些常见错误。

本文旨在揭示在使用Kubernetes时经常遇到的误区,并提供避免错误的建议。

1. 不设置资源请求

资源请求设置,无疑是值得最多关注的一项。 CPU请求经常出现未设置,或者设置得过低 (以便在单个节点上安装大量pod),导致节点超负荷运行。需求高峰时,节点的CPU资源会被充分利用,而工作负载只能获得有限的资源,导 致CPU受到限制 ,进而导致应用程序延迟增加或超时等问题。

以下情况为演示案例,请勿随意尝试:

  • BestEffort配置 (pod的requests与limits均为0)

  • resources: {}

  • CPU 性能极低的情况:

  • resources: requests: cpu: "1m"

    另一方面,即使CPU未被充分利用,不恰当的CPU限制也会限制pod的运行,同样会导致延迟。

    关于Linux内核中的CPU CFS配额,以及基于CPU设置的CPU节流和关闭CFS配额的问题,曾有过公开讨论: 过度限制CPU带来的问题比它解决的问题多得多

    内存过度使用会给你带来更多麻烦。达到CPU限制会导致限制,达到内存限制会导致pod被终止。如何减少 OOMkill 情况发生的频率?在使用Kubernetes时,应合理设置内存请求,不要过度占用内存,并通过保证QoS(服务质量),设置内存请求与限制量相等。

    如下例:

  • Burstable (可能更频繁地被 OOMkilled)

  • resources:requests:memory: "128Mi"cpu: "500m"limits:memory: "256Mi"cpu: 2

  • Guaranteed:

  • resources:requests:memory: "128Mi"cpu: 2limits:memory: "128Mi"cpu: 2

    如何优化资源设置?

    使用 metrics-server,查看 pod(及其容器)当前的 CPU 和内存使用情况。

    你很可能已经在运行这项程序,需要执行以下代码:

    kubectltop pods kubectltop pods --containers kubectltop nodes

    然而,这些数据只反映了当前的使用状态。为了更全面地了解资源使用情况, 及时看到不同时间段 (比如高峰期、昨天早上等) 的使用指标 ,可以采用Prometheus等工具。这些工具能够收集并存储指标数据,方便后续的查询和图表绘制。

    VerticalPodAutoscaler(VPA)可以帮助 自动完成 这项手动过程——根据查看到的CPU/内存使用情况,动态调整请求和限制。

    2. 忽略健康检查

    在Kubernetes环境中,健康检查对于维护服务的稳定性至关重要。然而,健康检查功能的利用率普遍较低。通过健康检查,可以实时监测pod及其容器的健康状况。

    Kubernetes 有三种主要工具可用于健康检查:

  • 配置存活检查(Liveness Check) 用于检查应用程序是否存活。节点上运行的Kubelet 代理都会使用该探针,确保容器按预期运行。

  • 就绪检查(Readiness Checks) 用于确定容器何时准备好接收流量,容器的整个周期内都会运行。

  • 启动探针(Startup Probe) 确定容器应用何时成功启动,检查到启动失败后,即会重新启动pod。


  • 3. 滥用latest标签

    使用latest标签没有明确目的性,难以统一管理。Kubernetes文档对于在生产环境中使用docker images:latest标签有明确规定:

    在生产环境中应避免使用latest标签部署容器,因为这会增加跟踪镜像版本和回滚的难度。

    在生产环境中部署容器时,应避免使用 :latest 标签,因为它很难跟踪运行的是哪个版本的镜像,也很难回滚。

    随着经验积累,我们已经逐渐减少了对 :latest 标签的使用,转而采用别的替代方案。

    4. 权限过高的容器

    为容器赋予过多权限(如访问普通容器无法访问的资源),是开发人员在使用 Kubernetes时的常见错误,这会增加安全风险。

    比如,在Docker容器内运行Docker守护进程,就可能构成特权容器的安全风险。

    为了避免这种情况,建议避免为容器提供 CAP_SYS_ADMIN 功能,因为它包含所有内核漏洞的25%以上。

    此外,避免向容器授予完全权限和赋予容器的主机文件系统权限也很重要。这意味着可以利用容器可以通过恶意二进制文件替换二进制文件,进而危害整个主机。

    为防止容器权限过高,必须谨慎配置权限设置,切勿以高于所需权限的状态运行进程。

    此外,注意使用监控和日志来检测和解决问题。

    5. 缺乏监控和日志记录

    Kubernetes 环境中缺乏监控和日志记录会损害其安全性和整体性能。在故障排查和响应工作中,缺乏日志记录和监控会导致问题难以定位及解决。

    一个常见的陷阱是,由于缺乏相关日志或指标,无法找到 Kubernetes 平台和应用程序中的故障点。

    因此,必须 设置适当的监控和日志工具 ,如 Prometheus、Grafana、Fluentd 和 Jaeger等,以 收集、分析和可视化指标、日志和跟踪信息 ,进一步了解 Kubernetes 环境的性能和健康状况。

    通过实施严格的监控和日志记录实践,企业能够高效地整合信息,获取更深刻的洞察,并成功应对Kubernetes环境因其动态性和复杂性所带来的挑战。

    6. 所有对象的默认命名空间

    在Kubernetes环境中,如果对所有对象使用默认的命名空间,会导致组织和管理上的不便。由于default命名空间是默认创建服务和应用程序的地方,并且在没有明确指定的情况下会成为活动的命名空间,过度依赖默认设置会使得集群内的不同组件或团队在资源隔离和组织上显得混乱。这进一步导致资源管理、访问控制和可见性方面的难题。

    因此, 建议根据不同的项目、团队或应用程序需求,创建自定义的命名空间 ,从而在Kubernetes集群中实现更清晰的组织结构、更合理的资源分配和更严格的访问控制。

    通过利用多个命名空间,用户能更有条理地划分和管理资源,提高 Kubernetes 环境的整体运行效率和安全性。

    7. 安全配置不足

    在部署应用程序时,安全性应始终是首要考虑的因素。使用集群外部可访问的端点、不保护机密、不考虑如何安全运行有权限容器等,都是安全方面需要重视的关键点。

    Kubernetes的安全性是任何部署都不可或缺的一环。面临的安全挑战包括:

  • 授权: 身份验证和授权机制对于控制Kubernetes集群中的资源访问至关重要。

  • 网络: Kubernetes网络涉及管理覆盖网络和服务端点,以确保容器间的流量在集群内安全地路由。

  • 存储: 确保集群的存储安全,包括确保数据不会被未经授权的用户或进程访问,并保障数据的安全。

  • Kubernetes API服务器提供了一个REST接口,可以访问存储的所有信息,这意味着,用户只需向 API 发送 HTTP 请求,即可访问 API 中存储的任何信息。因此必须采取措施防止未经身份验证的用户访问这些数据。这可以通过使用用户名/密码或基于令牌的身份验证等支持的方法为API服务器配置身份验证来实现。


    这不仅关系到集群自身的安全,还关系到集群上存储的机密和配置安全。为防范潜在的漏洞攻击,必须在集群上实施一套完善的安全控制。

    使用基于角色的访问控制(RBAC)是一种保护Kubernetes集群的强大安全机制:根据分配给用户的角色(比如「管理员」或「操作员」等不同的权限级别)来限制对资源的访问,从而保护集群的安全。

    管理员角色拥有完整的访问权限,而操作员角色对集群内的资源拥有有限的权限。通过这种方法,我们能够更有效地控制和管理访问集群的所有用户权限。

    8. poddisruptionbudget缺失

    当您在Kubernetes上运行生产工作负载时,节点和集群可能需要经常进行升级或退役。PodDisruptionBudget(PDB)是集群管理员和集群用户之间的服务保证API,它确保了服务的高可用性。

    为避免因节点耗尽而导致的不必要服务中断,强烈建议您创建PDB。

    apiVersion: policy/v1kind: PodDisruptionBudgetmetadata:name: db-pdbspec:minAvailable: 2selector:matchLabels:app: database

    作为集群用户,你可以这样告诉集群管理员:「嘿,我这里有一个数据库服务,无论你要做什么,我都希望至少有两个副本始终可用。」

    9. pod 的自我反亲缘性(self anti-affinities)

    例如,当运行某个部署的多个Pod副本时,可能会遇到由于节点宕机而导致所有副本同时宕机的情况。

    这是因为Kubernetes调度器默认不会为您的Pod强制执行anti-affinity策略。为了解决这个问题,需要明确地在Pod定义中指定affinity和podAntiAffinity规则:

    ......labels:app: db......affinity:podAntiAffinity:requiredDuringSchedulingIgnoredDuringExecution:-labelSelector:matchExpressions:-key: "app"operator: Invalues:-dbtopologyKey: "kubernetes.io/hostname"

    如上,这将确保 pod 被调度到不同的节点上(仅在调度时检查,而不是在执行时,因此需要在调度时忽略执行期间检查),从而提高系统的可用性和容错能力。

    我们说的是不同节点名称上的 podAntiAffinity 而不是不同的可用性区域,即 topologyKey:"kubernetes.io/hostname。如果您真的需要适当的 HA,请深入了解这一主题。

    10. 每个 HTTP 服务的负载均衡

    你的集群中可能有更多的希望向外界公开的HTTP服务。

    如果将 kubernetes 服务公开为 type:LoadBalancer ,其控制器(供应商特定)将提供并协调外部LoadBalancer,而在创建许多资源的情况下,这些资源可能会变得昂贵(外部静态 IPv4 地址、按秒计价......)。

    在这种情况下,共享一个外部负载均衡可能更有意义,将服务公开为 type:NodePort 公开,或者部署如 nginx-ingress-controller (或traefik、Istio等)作为暴露给外部负载平衡器的单一NodePort端点,并基于kubernetes ingress 资源在集群中路由流量。

    集群内其他相互对话的(微)服务可通过ClusterIP服务和开箱即用的DNS服务发现进行对话。

    注意:请避免使用服务的公共DNS/IP进行通信,因为这会增加延迟并提升云成本。

    11. 未感知集群自动扩展

    对于非Kubernetes感知的集群自动扩展,单纯依赖简单的指标(如CPU利用率)来添加或删除节点是不够的。在调度pod时,需要综合考虑众多 调度约束 条件,如pod和节点的亲和性、污点和容忍度、资源请求以及QoS等。如果外部自动扩展工具不了解这些约束,可能会导致调度问题。

    例如,当所有可用CPU资源均被请求时,即使有新的pod需要调度,也可能因为资源不足而陷入 Pending状态 。如果外部自动扩展器仅基于当前使用的CPU平均值(而非已请求的资源)来决策,可能就不会触发扩展,从而导致pod无法调度。

    向内扩展(即从集群中移除节点)同样复杂。特别是对于有状态的pod(如绑定了持久卷的pod),由于 持久卷 通常与 特定可用区域 绑定,自定义自动扩展器若移除带有此类pod的节点,可能导致调度器无法在其他节点上重新调度该pod,因为其他节点可能没有相应的持久卷。

    幸运的是,社区中有成熟的cluster-autoscaler(CA组件),它们与主流公有云供应商的API集成,了解上述各种限制,并能在不影响既定约束的条件下进行优雅伸缩,从而帮助节省计算成本。

    总结

    总之,Kubernetes 是管理容器化应用程序的强大工具,但使用时也需注意其带来的挑战。为避免常见错误和陷阱,需深入了解Kubernetes的工作原理及其与已部署服务的交互方式。

    不要期望一切都能自动运行,投入时间和精力让您的应用真正云原生化,将有助于提高Kubernetes环境的稳定性、性能和安全性。

    作者丨Seifeddine Rajhi 编译丨onehunnit

    来源丨medium.com/@seifeddinerajhi/most-common-mistakes-to-avoid-when-using-kubernetes-anti-patterns-️-f4d37586528d

    *本文为dbaplus社群编译整理,如需转载请取得授权并标明出处! 欢迎广大技术人员投稿,投稿邮箱:[email protected]

    活动推荐

    2024 XCOPS智能运维管理人年会·广州站将于5月24日举办 ,深究大模型、AI Agent等新兴技术如何落地于运维领域,赋能企业智能运维水平提升,构建全面运维自治能力! 码上报名,享早鸟优惠。