K8s 生产最佳实践-限制 NameSpace 资源用量
前言
想象一下这个场景:多个系统运行在同一套 K8s 集群上,有重要系统,也有不太重要的系统。但是某一天,某个不重要的系统突然占用了该 K8s 集群的所有资源,导致该集群上的其他系统的正常运行受到影响。本文介绍了 Kubernetes 平台如何管理容量,以及作者对管理员的注意事项和建议。
Kubernetes 资源限制概述
我们寿险了解 Kubernetes 平台如何在容器和节点级别应用资源约束。 为了讨论合理规模,我们将专门关注 CPU 和内存,尽管还有其他因素需要考虑。
可以为每个容器和 Pod 指定 resource requests 和 limits。 Requests 是为 pod 预留的有保证的资源,而 limits 则是旨在保护集群整体架构的安全措施。 在 Kubernetes 中,pod 的 requests 和 limits 之间的关系被配置为服务质量(QoS)。 在节点上,kubelet(可以监控资源的代理)将此信息传递给容器运行时,容器运行时使用内核 cgroups 来应用资源约束。
要调度新的 pod,Kubernetes 调度程序将确定可用节点上的有效位置,并考虑现有 pod 资源限制。 Kubernetes 会预配置一些系统预留,以留出资源供操作系统和 Kubernetes 系统组件使用(具体如下图)。 剩余量被定义为可分配的,调度程序将其视为节点的容量。 调度器可以基于所有单元的合计资源请求来调度单元到节点的容量。 请注意,所有单元的聚合资源限制可以大于节点容量,这种做法称为超额使用(超配 or 超卖)。

在管理节点容量时,我们要尽量避免两种情况。 在第一种情况下,实际内存利用率达到容量,并且 kubelet 基于驱逐信号触发节点压力驱逐。 如果节点在 kubelet 可以回收内存之前用完内存,则节点 oom-killer 将做出响应,根据从每个 pod 的 QoS 计算的 oom_score_adj 值选择要删除的 pod。 因此,构成这些 pod 的应用程序会受到影响。
在 CPU 上过量使用的底层机制与内存的行为不同,因为它将 CPU 时间分配给各个容器。 高 CPU 利用率会导致 CPU 节流 (CPU throttling),但不会触发节点压力驱逐,也不会自动导致 Kubernetes 终止 Pod。 但是也请注意,CPU 耗尽仍可能导致应用程序 pod 降级、活动探测失败并重新启动。
我们还希望避免另一种情况。 在节点级别,requests 是有保证的资源,并且必须小于容量,因为 Kubernetes 调度程序不会超额预订。 如果 requests 明显且始终大于实际使用的资源,则多余的容量基本上未被使用。 虽然可能需要为高峰处理时间保留资源,但管理员应在这一点与运行可能不需要的过剩容量的重复成本之间进行平衡。 根据实际使用情况配置请求是一种平衡行为,应考虑应用程序的风险管理(平衡可用性和成本).
Kubernetes 管理员能做啥
Kubernetes 管理员的一个主要关注点是管理和合理调整集群容量,我们可以在 Web 上利用 Prometheus + Grafana 仪表盘和命令行捕获集群利用率指标,供管理员使用。
但是 Kubernetes 管理员还面临一个棘手的大问题:正在运行的应用程序(的资源管理)。 解决特定问题的应用程序可以由不同的开发人员以不同的方式编写,从而导致不同的性能(比如 java 编写的可能消耗内存较多,golang 消耗内存相对较少)。 每个应用程序都是独特的,没有一种适合所有应用程序的方法。 管理员对开发人员的应用程序的控制能力较弱,在大型企业中,单个管理团队可能很难接触到众多的开发团队。 因此,管理员的重点应该是设置护栏,以允许开发人员(在护栏内)调整自己的应用程序。
配置 LimitRange
绕了这么久,终于进入正题了。
为此,管理员可以为每个 NameSpace 配置不同的 LimitRanges,为开发人员提供针对各个容器和 pod 的建议大小限制。 以下是 LimitRange 的示例。 由于每个集群和应用程序都有不同的业务和风险要求,因此各位读者实际应用时数字会有所不同。
apiVersion: v1
kind: LimitRange
metadata:
name: "resource-limits"
spec:
limits:
- max:
cpu: "2"
memory: 4Gi
min:
cpu: 125m
memory: 128Mi
type: Pod
- default:
cpu: "0.5"
memory: 1Gi
defaultRequest:
cpu: 250m
memory: 256Mi
max:
cpu: "2"
memory: 4Gi
maxLimitRequestRatio:
cpu: "25"
memory: "4"
min:
cpu: 125m
memory: 128Mi
type: Container
在 Kubernetes 中进行开发的良好实践是创建微服务应用程序,而不是大型的巨石应用程序。 为了鼓励微服务的开发,应该应用 limits 来约束 pod 的最大大小。 节点的物理容量可能会决定此最大大小,因为它应该可以轻松地容纳几个最大的 pod。 还是类似这个图:

让我们继续上面的 LimitRange 示例。 最小 pod 和容器大小可能由正在运行的应用程序的需求确定,管理员不必强制执行。 为了简单起见,我们还鼓励开发人员在每个 pod 上运行一个容器(一个典型的例外是使用 sidecar 容器,如 Istio 的 sidecar)。 因此,上面的示例对 pod 和 container 使用相同的资源值。
默认 requests 和 limits 作为开发人员的建议值。 未显式声明容器大小的工作负载资源(即 pod)将继承默认值。 作为一种好的做法,开发人员应明确定义工作负载资源中的资源请求和限制,而不采用默认值。
CPU 和内存的 maxLimitRequestRatio 是开发人员的突发准则。 在开发环境中,当原型应用程序经常空闲运行,但在使用时需要合理的按需资源时,高 CPU maxLimitRequestRatio 会很好地工作。 开发人员可能只在工作时间工作,在自己的 IDE 中离线编码,偶尔测试单个微服务,或者测试 CI/CD 管道的不同阶段。 相比之下,如果许多最终用户在一天中同时访问应用程序,您将看到更高的基准利用率。 这可能更接近您的生产环境,并可能降低 maxLimitRequestRatio(可能是事件 1:1 的请求限制)。 由于管道各阶段的不同利用率模式将导致不同的请求和限制,因此在生产之前使用模拟工作负载进行测试以确定适当的单元大小非常重要。
开发人员将使用 maxLimitRequestRatio 作为适当调整大小的准则。 Kubernetes 调度程序基于资源请求做出调度决策,因此开发人员应配置资源 requests 以反映实际使用情况。 然后,基于应用程序的风险状况,开发人员将配置 limits 以遵守 maxLimitRequestRatio。 将 maxLimitRequestRatio 设置为 1 的管理员强制开发人员将 requests 配置为等于限制,这在生产中可能是理想的,以降低风险并优先考虑稳定性。
在本文前面,我们比较了内存和 CPU,并描述了这两种资源在负载下的不同行为,高内存可能导致 pod 逐出或从内存不足的情况重新启动。 因此,最好是谨慎行事,并为不同环境的内存配置较低的 maxLimitRequestRatio,以防止应用程序 pod 重新启动。 为 OpenJDK pod 配置内存时还应注意其他事项。 (如果没配置相应动态调整的参数), 容器和 pod 内部的 JVM heap 对容器的请求和限制一无所知,但应用于前者的资源约束将影响后者。
配置 ResourceQuota
管理员还可以配置 ResourceQuotas,它为 NameSpace 提供基于容量的限制,以指导开发人员根据预测的估计值来调整应用程序的规模。 下面是一个 ResourceQuota 示例。
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-resources
spec:
hard:
limits.memory: 20Gi
requests.cpu: "4"
requests.memory: 20Gi
在应用程序 NameSpace 的初始创建过程中,开发团队应与管理员一起预测其应用程序大小并应用适当的配额。 管理员应根据服务、副本的数量和 pod 的估计大小来预测应用程序大小。 为了简化对众多 NameSpace 的管理,管理员可考虑类似 AWS 的方法作为起始准则,其中 small、medium、large、xlarge 应用程序被给予对应的预定配额。
应用程序跨 CI/CD 管道的各个阶段进行运行,每个阶段都位于不同的集群或 NameSpace 中,并具有自己的配置配额。 在不考虑性能和高可用性的开发和测试 NameSpace 中,应用程序应配置最小的 pod,并为每个服务配置 1 个 pod 副本,以减少资源使用。 另一方面,在生产集群或 NameSpace 中,应使用更大的 pod 和每个服务至少 2 个单元副本,以处理更高的业务量并提供高可用性。 通过使用 CI/CD 管道中的模拟工作负载进行压力和性能测试,开发人员可以在生产发布之前确定适当的生产 pod 大小、副本数量和配额。
管理员应针对未来的扩展制定配额预算,并考虑应用程序的使用模式、峰值容量和已配置的 pod 或节点的 autoscaler(如果有)。 例如,可在快速添加新微服务的开发 NameSpace、用于确定适当生产 pod 大小的性能测试 NameSpace、或使用 HPA 来调整峰值容量的生产 NameSpace 中分配附加配额。 管理员应针对上述各种情况和其他情况提供足够的配额开销,同时平衡基础架构容量的风险并保护架构容量。
管理员和开发人员都应该预期会随着时间的推移调整配额。 开发人员无需管理员的帮助即可收回配额,方法是查看每项服务并减少 Pod requests 或 limits 以匹配实际使用量。 如果开发人员已经采取了这些步骤,但仍然需要额外的配额,那么他们应该联系管理员。 管理员应将开发人员的定期配额请求作为一个机会,根据以前预测的估计值分析实际消耗量,并相应地确认或调整配额大小和新的预测估计值。
另外再介绍在调整配额大小时的一些次要注意事项。 在确定 CPU 和内存的配额比率时,应考虑节点容量,以便有效地利用两者。 例如,m5.2xlarge 类型的 AWS EC2 实例为 8 个 vCPU、32 GiB RAM。 由 m5.2xlarge 节点组成的集群可以按照每 4 GB RAM 对应 1 个 vCPU 的比例分配应用配额(不考虑节点的系统保留空间),从而高效地使用 CPU 和内存。 如果应用程序工作负载(即 CPU 或内存密集型)与节点大小不匹配,则可以考虑使用不同的节点大小。
管理员对何时应用和不应用配额的 CPU limits 一直存在争议,这里我们将提供一些考虑事项,而不是正式的指导。 正如我们前面所讨论的,pod 的 CPU 不足会导致节流,但不一定会导致 pod 终止。 如果管理员倾向于过量使用并利用节点上的所有可用 CPU,则不应设置配额的 CPU limits。 相反,应设置配额 (resource quota) 的 CPU limits,以减少过度使用和应用程序性能风险,这可能是一个业务和成本决策,而不是技术决策。 与生产环境相比,开发环境可以容忍更高的风险和不可预测的性能,因此管理员可以考虑将 CPU limits 应用于生产而不是开发。
最后,在某些特殊情况下,不建议应用配额。 应用配额的目的是让管理员能够对自定义开发的应用程序的容量规划进行一定程度的控制。 配额不应应用于 Kubernetes 自身的组件,因为这些项目需要预先配置的资源量。 出于类似的原因,配额也不应适用于第三方供应商提供的企业版应用程序。
总结
在本文中,我们介绍了 Kubernetes 平台如何通过资源约束保护架构,包括:
- Pod 的 requests 和 limits
- Node 的资源分配
- NameSpace 级别的针对 Pod 和容器的 LimitRange
- NameSpace 级别的 ResourceQuota
并提供了在应用程序 NameSpace 中应用 limits 和 quota 的保护措施时的合理调整注意事项。 每个应用的风险偏好和 Kubernetes 集群的容量各不相同,需要综合考量再实施。
参考文档
K8s 生产最佳实践-限制 NameSpace 资源用量的更多相关文章
- k8s编排最佳实践
编排文件技巧 使用资源时指定最新稳定版的API version 编排文件应该纳入版本控制,这样可以在必要的时候快速回滚,同样也有利于资源恢复和重建 使用YAML格式而不是JSON格式,尽管两种格式的文 ...
- Nacos: Namespace 和 Endpoint 在生产环境下的最佳实践
随着使用 Nacos 的企业越来越多,遇到的最频繁的两个问题就是:如何在我的生产环境正确的来使用 namespace 以及 endpoint.这篇文章主要就是针对这两个问题来聊聊使用 nacos 过程 ...
- Kubernetes生产环境最佳实践
点击上方"开源Linux",选择"设为星标" 回复"学习"获取独家整理的学习资料! 众所周知,Kubernetes很难! 以下是在生产中使用 ...
- 生产环境容器落地最佳实践 --JFrog 内部K8s落地旅程
引言 Kubernetes已经成为市场上事实上领先的编配工具,不仅对技术公司如此,对所有公司都是如此,因为它允许您快速且可预测地部署应用程序.动态地伸缩应用程序.无缝地推出新特性,同时有效地利用硬件资 ...
- ELK:收集k8s容器日志最佳实践
简介 关于日志收集这个主题,这已经是第三篇了,为什么一再研究这个课题,因为这个课题实在太重要,而当今优秀的开源解决方案还不是很明朗: 就docker微服务化而言,研发有需求标准输出,也有需求文件输出, ...
- 一款不错的 Go Server/API boilerplate,使用 K8S+DDD+CQRS+ES+gRPC 最佳实践构建
Golang API Starter Kit 该项目的主要目的是使用最佳实践.DDD.CQRS.ES.gRPC 提供样板项目设置. 为开发和生产环境提供 kubernetes 配置.允许与反映生产的 ...
- 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生
[转].NET(C#):浅谈程序集清单资源和RESX资源 目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...
- K8S生产环境中实践高可靠的配置和技巧都有哪些?
K8S环境中实践高可靠的配置和技巧都有哪些? 磁盘类型及大小 磁盘类型: 推荐使用ssd 磁盘 对于worker节点,创建集群时推荐使用挂载数据盘.这个盘是专门给/var/lib/docker 存放本 ...
- 可能是Asp.net Core On host、 docker、kubernetes(K8s) 配置读取的最佳实践
写在前面 为了不违反广告法,我竭尽全力,不过"最佳实践"确是标题党无疑,如果硬要说的话 只能是个人最佳实践. 问题引出 可能很多新手都会遇到同样的问题:我要我的Asp.net ...
- 读取生产环境go语言的最佳实践展示
近期看了一篇关于go产品开发最佳实践的文章,go-in-procution.作者总结了他们在用go开发过程中的非常多实际经验,我们非常多事实上也用到了.鉴于此,这里就简单的写写读后感,兴许我也争取能将 ...
随机推荐
- Oracle 服务器迁移的一些经验
前言 通过此文章来分享一下 Oracle 服务器迁移过程中的一些经验,希望对大家有些许帮助. 本文旨在帮助更多的同学,会提及一些基本命令或技巧,但不赘述,后续有机会再进一步分享各个细节. 背景 之前因 ...
- 搭建eBackup对接NFS服务
环境准备 两个虚拟机需要是仅主机并且同一网段 先搭建一个eBackup环境虚拟机 搭建步骤可访问:(https://www.cnblogs.com/zhengyan6/p/16220774.html) ...
- RTSP播放器开发填坑之道
好多开发者提到,在目前开源播放器如此泛滥的情况下,为什么还需要做自研框架的RTSP播放器,自研和开源播放器,到底好在哪些方面?以下大概聊聊我们的一点经验,感兴趣的,可以关注 github: 1. 低延 ...
- spark 解决 java.util.Date is not a valid external type for schema of Date
出错伪代码如下: //出错的点在这里 import java.util.Date ... val t_rdd = t_frame.rdd.map(row => { val photo_url = ...
- LFS(Linux From Scratch)构建过程全记录(六):交叉编译临时工具
写在前面 本章将展示如何使用刚刚构建的跨工具链来交叉编译基本实用程序. M4安装 和前文一样,先进行解压,然后cd进入 注意:不需要构建build文件夹,直接输入以下配置文件 ./configure ...
- 《Java基础——线程类》
Java基础--线程类 一.线程的创建之Thread类: 规则: 通过声明一个新类作为子类继承 Thread 类,并复写 run() 方法,就可以启动新线程并执行自己定义的 run()方法 ...
- 静态文件:Static Files
官方文档地址:https://fastapi.tiangolo.com/zh/tutorial/static-files/ from fastapi import FastAPI from fasta ...
- Kubernetes(k8s)为容器设置启动时要执行的命令和参数
创建 Pod 时设置命令及参数 创建 Pod 时,可以为其下的容器设置启动时要执行的命令及其参数.如果要设置命令,就填写在配置文件的 command 字段下,如果要设置命令的参数,就填写在配置文件的 ...
- MySQL集群搭建(3)-MMM高可用架构
1 MMM 介绍 1.1 简介 MMM 是一套支持双主故障切换以及双主日常管理的第三方软件.MMM 由 Perl 开发,用来管理和监控双主复制,虽然是双主架构,但是业务上同一时间只允许一个节点进行写入 ...
- linux主机时间同步
yum -y install ntpdate ntp && ntpdate cn.pool.ntp.org systemctl start ntpd.service && ...