ack-koordinator为容器提供内存服务质量QoS(Quality of Service)保障能力,在确保内存资源公平性的前提下,改善应用在运行时的内存性能。本文介绍如何使用容器内存QoS功能。
背景信息
容器在使用内存时主要有以下两个方面的约束:
自身内存限制:当容器自身的内存(含Page Cache)接近容器上限时,会触发内核的内存回收子系统,这个过程会影响容器内应用的内存申请和释放的性能。
节点内存限制:当容器内存超卖(Memory Limit>Request)导致整机内存不足,会触发内核的全局内存回收,这个过程对性能影响较大,极端情况甚至导致整机异常。
为了提高应用运行时性能和节点的稳定性,ACK引入容器内存QoS能力,通过ack-koordinator配合Alibaba Cloud Linux 2为应用提升内存性能。当功能开启时,ack-koordinator依据容器参数自适应配置内存子系统(Memcg),在保障节点内存资源公平的基础上,优化内存敏感型应用的性能。
前提条件
仅支持ACK Pro版集群。具体操作,请参见创建ACK Pro版集群。
已安装ack-koordinator。具体操作,请参见ack-koordinator(ack-slo-manager)。
使用限制
系统组件版本要求如下表所示。
组件 | 版本要求 |
Kubernetes | ≥1.18 |
≥0.8.0 | |
Helm版本 | ≥v3.0 |
操作系统 | Alibaba Cloud Linux 2(版本号详情,请参见内核接口说明:Memcg后台异步回收、cgroup v1接口支持memcg QoS功能、Memcg全局最低水位线分级) |
费用说明
ack-koordinator组件本身的安装和使用是免费的,不过需要注意的是,在以下场景中可能产生额外的费用:
ack-koordinator是非托管组件,安装后将占用Worker节点资源。您可以在安装组件时配置各模块的资源申请量。
ack-koordinator默认会将资源画像、精细化调度等功能的监控指标以Prometheus的格式对外透出。若您配置组件时开启了ACK-Koordinator开启Prometheus监控指标选项并使用了阿里云Prometheus服务,这些指标将被视为自定义指标并产生相应费用。具体费用取决于您的集群规模和应用数量等因素。建议您在启用此功能前,仔细阅读阿里云Prometheus计费说明,了解自定义指标的免费额度和收费策略。您可以通过账单和用量查询,监控和管理您的资源使用情况。
功能介绍
部署在Kubernetes集群中的应用,在资源使用层面遵循标准的K8s Request/Limit模型。如下图所示,Pod的内存Request仅用于调度时的容量参考,在单机层面使用Limit进行约束。下图中memory.limit_in_bytes表示内存使用上限。
当Pod自身的内存使用(含Page Cache)接近声明的Limit值时,会触发内存子系统(Memcg)级别的直接内存回收,阻塞进程执行。如果此时的内存申请速度超过回收速度,容器会触发OOMKilled并释放内存。应用管理员可能会调高应用的内存Limit,以降低Pod OOM等风险,但这也导致整机内存的Limit之和可能超出物理容量,即处于内存超卖状态。超卖状态下,某个Pod大量申请内存,可能造成整机内存不足,导致其他Pod申请内存时触发整机内存回收或OOM(Kubernetes默认不启用Swap),进而影响应用表现。在上述两种情形中,个别Pod可能影响其他Pod的内存访问性能,即使这些Pod的内存使用未超过Request声明,应用性能仍会受到较大影响。
ack-koordinator结合Alibaba Cloud Linux 2提供了容器内存QoS保障的能力,依据Pod参数自动配置内存子系统(Memcg),为容器开启Memcg QoS、内存后台回收和全局最低水位线分级特性,以保障容器的内存资源QoS和公平性。更多信息,请参见cgroup v1接口支持memcg QoS功能、Memcg后台异步回收和Memcg全局最低水位线分级。
通过启用容器内存QoS,您可以获得以下功能特性:
Pod内存使用接近Limit限制时,优先在后台异步回收一部分内存,缓解直接内存回收带来的性能影响。
Pod之间实施更公平的内存回收,整机内存资源不足时,优先从内存超用(Memory Usage>Request)的Pod中回收内存,避免个别Pod造成整机内存资源质量下降。
BestEffort内存超卖场景下,优先保障Guaranteed/Burstable Pod的内存运行质量。
当集群开启了差异化SLO混部时,系统将优先保障延时敏感型LS(Latency-Sensitive)Pod的内存QoS,延缓LS Pod触发整机内存回收的时机。下图中,memory.limit_in_bytes表示内存使用上限,memory.high表示内存限流阈值,memory.wmark_high表示内存后台回收阈值,memory.min表示内存使用锁定阈值。
关于ACK容器内存QoS启用的内核能力,详见Alibaba Cloud Linux 2的内核功能与接口概述。
Kubernetes在社区1.22版本中提供了MemoryQoS特性,可在Kubelet上配置开启,支持容器的内存锁定和主动限流,能够提高内存资源的公平性。该特性目前处于Alpha状态,仅支持Linux cgroups v2(要求内核版本不低于4.15,且和cgroups v1不兼容,启用会影响节点上所有容器)。ACK容器内存QoS在cgroups v1上支持了该特性,并额外提供内存后台回收和最低水位线分级等性能优化和差异化SLO能力。
操作步骤
当您为Pod开启容器内存QoS时,内存子系统(Memcg)将基于系数配置和Pod参数自适应调整。开启容器内存QoS的具体操作步骤如下。
在Pod YAML中添加以下Annotation,为Pod单独开启容器内存QoS功能。
annotations: #设置auto,表示开启该Pod的容器内存QoS功能。 koordinator.sh/memoryQOS: '{"policy": "auto"}' #设置none,表示关闭该Pod的容器内存QoS功能。 #koordinator.sh/memoryQOS: '{"policy": "none"}'
通过ConfigMap全局开启容器内存QoS。
使用以下ConfigMap示例,开启全集群的容器内存QoS功能。
apiVersion: v1 data: resource-qos-config: |- { "clusterStrategy": { "lsClass": { "memoryQOS": { "enable": true } }, "beClass": { "memoryQOS": { "enable": true } } } } kind: ConfigMap metadata: name: ack-slo-config namespace: kube-system
在Pod YAML中指定QoS等级,使用集群配置。
apiVersion: v1 kind: Pod metadata: name: pod-demo labels: koordinator.sh/qosClass: 'LS' #指定Pod的QoS级别为LS。
LS
和BE
表示两种典型的应用类型,可配合上述ConfigMap中的集群开关使用。当集群开关开启后,Pod只需指定koordinator.sh/qosClass
即可生效参数,不必在Pod Annotations中重复配置;对于未指定koordinator.sh/qosClass
的Pod,ack-koordinator将参考Pod原生的QoSClass来设置参数,其中Guaranteed
为系统内部默认值,Burstable和Besteffort分别使用ConfigMap中LS
和BE
的默认配置。关于默认配置的更多信息,请参见参数说明。推荐您使用
koordinator.sh/qosClass
来管理内存QoS参数,以便于统一管理。查看命名空间
kube-system
下是否存在ConfigMapack-slo-config
。若存在ConfigMap
ack-slo-config
,请使用PATCH方式进行更新,避免干扰ConfigMap中其他配置项。kubectl patch cm -n kube-system ack-slo-config --patch "$(cat configmap.yaml)"
若不存在ConfigMap
ack-slo-config
,请执行以下命令进行创建Configmap。kubectl apply -f configmap.yaml
通过ConfigMap指定Namespace开启部分Pod的容器内存QoS。
若您需要开启或关闭部分命名空间下
LS
和BE
Pod的参数设置,可以使用以下ConfigMap示例,为指定Namespace内的Pod开启或禁用容器内存QoS功能。使用以下ConfigMap内容,创建ack-slo-pod-config.yaml文件。
为命名空间为kube-system内的Pod开启或禁用容器内存QoS功能。
apiVersion: v1 kind: ConfigMap metadata: name: ack-slo-pod-config namespace: koordinator-system # 首次使用时需要先手动创建该namespace data: #单独开启或关闭部分Namespace的Pod。 memory-qos: | { "enabledNamespaces": ["allow-ns"], "disabledNamespaces": ["block-ns"] }
执行以下命令,更新ConfigMap。
kubectl patch cm -n kube-system ack-slo-pod-config --patch "$(cat ack-slo-pod-config.yaml)"
(可选)配置高级参数。
容器内存QoS支持Pod级别和集群级别的精细化配置,相关参数说明如下。若您有其他需求,请提交工单。
参数
类型
取值范围
说明
enable
Boolean
true
false
true
:集群全局开启容器内存QoS功能,相应QoS等级的容器启用内存子系统的推荐配置。false
:集群全局关闭容器内存QoS功能,相应QoS等级的容器的内存子系统配置会重置为创建初始值。
policy
String
auto
default
none
auto
:Pod开启容器内存QoS功能并启用推荐参数,生效优先级高于集群ConfigMap配置。default
:Pod继承集群ConfigMap配置。none
:Pod关闭容器内存QoS功能,相关内存子系统配置会重置为容器创建的初始值,生效优先级高于集群ConfigMap配置。
minLimitPercent
Int
0~100
单位为百分比,默认值为
0
,表示关闭。表示相较于内存Request,容器内存绝对锁定、不被全局回收的比例。计算方式为
memory.min=request*minLimitPercent/100
。适合对Page Cache敏感的场景开启,保留一部分文件缓存不被回收以改善读写性能。例如容器Memory Request=100MiB
,当minLimitPercent=100
时,memory.min=104857600
。更多信息,请参见Alibaba Cloud Linux 2 cgroup v1接口支持memcg QoS功能。lowLimitPercent
Int
0~100
单位为百分比,默认值为
0
,表示关闭。表示相较于内存Request,容器内存相对锁定、优先不被回收的比例。计算方式为
memory.low=request*lowLimitPercent/100
。例如容器Memory Request=100MiB
,当lowLimitPercent=100
时,memory.low=104857600
。更多信息,请参见Alibaba Cloud Linux 2 cgroup v1接口支持memcg QoS功能。throttlingPercent
Int
0~100
单位为百分比,默认值为
0
,表示关闭。表示相较于内存Limit,容器内存使用触发限流的比例。计算方式为
memory.high=limit*throttlingPercent/100
。超出限流阈值时,容器将进入较大压力的主动内存回收状态下。适合应用内存超卖的场景(Request<Limit)开启,规避cgroup级别的OOM。例如容器Memory Limit=100MiB
,当throttlingPercent=80
时,memory.high=83886080(80 MiB)
。更多信息,请参见Alibaba Cloud Linux 2 cgroup v1接口支持memcg QoS功能。wmarkRatio
Int
0~100
单位为百分比,默认值为
95
,0
表示关闭。表示相较于内存Limit和
memory.high
,容器触发异步内存回收的比例。计算方式为memory.wmark_high=limit(throttlingPercent开启时为memory.high)*wmarkRatio/100
。超出异步回收阈值时,容器将启动后台内存回收。例如容器Memory Limit=100MiB
,当wmarkRatio=95,throttlingPercent=80
时,内存限流阈值memory.high=83886080(80 MiB)
,后台回收系数memory.wmark_ratio=95
,后台回收触发阈值memory.wmark_high=79691776(76 MiB)
。更多信息,请参见Alibaba Cloud Linux 2 Memcg后台异步回收。wmarkMinAdj
Int
-25~50
单位为百分比,默认值依据QoS,
LS
对应-25
,BE
对应50
。0
表示关闭。表示相较于(整机)全局内存最低水位线,容器所做出调整的比例。负数值让容器更晚进入全局内存回收,正数值让容器更早进入全局内存回收。例如Pod的QoS等级为LS,按默认配置,最低水位线分级系数
memory.wmark_min_adj=-25
,相应容器的最低水位线将下调25%。更多信息,请参见Alibaba Cloud Linux 2 Memcg全局最低水位线分级。
使用示例
本示例的评测环境如下:
ACK Pro集群版本为1.20。
集群内包含两个节点:压测节点(规格为8 Core 32 GB)、测试节点(规格为8 Core 32 GB)。
使用以下YAML内容,创建redis-demo.yaml文件。
--- apiVersion: v1 kind: ConfigMap metadata: name: redis-demo-config data: redis-config: | appendonly yes appendfsync no --- apiVersion: v1 kind: Pod metadata: name: redis-demo labels: koordinator.sh/qosClass: 'LS' #指定Redis实例的QoS级别为LS。 annotations: koordinator.sh/memoryQOS: '{"policy": "auto"}' #增加容器内存QoS的配置。 spec: containers: - name: redis image: redis:5.0.4 command: - redis-server - "/redis-master/redis.conf" env: - name: MASTER value: "true" ports: - containerPort: 6379 resources: limits: cpu: "2" memory: "6Gi" requests: cpu: "2" memory: "2Gi" volumeMounts: - mountPath: /redis-master-data name: data - mountPath: /redis-master name: config volumes: - name: data emptyDir: {} - name: config configMap: name: redis-demo-config items: - key: redis-config path: redis.conf nodeName: #nodeName注意修改为测试节点的nodeName。 --- apiVersion: v1 kind: Service metadata: name: redis-demo spec: ports: - name: redis-port port: 6379 protocol: TCP targetPort: 6379 selector: name: redis-demo type: ClusterIP
执行以下命令,部署Redis Server作为目标评测应用。
您可通过Service redis-demo进行集群内访问。
kubectl apply -f redis-demo.yaml
模拟内存超卖场景。
使用Stress工具制造较大的节点内存压力,触发系统内存回收,节点上已分配Pod的内存Limit之和已超过整机大小。
使用以下Pod YAML内容,创建stress-demo.yaml文件。
apiVersion: v1 kind: Pod metadata: name: stress-demo labels: koordinator.sh/qosClass: 'BE' #指定Stress实例的QoS级别为BE。 annotations: koordinator.sh/memoryQOS: '{"policy": "auto"}' #增加Memory QoS功能的配置。 spec: containers: - args: - '--vm' - '2' - '--vm-bytes' - 11G - '-c' - '2' - '--vm-hang' - '2' command: - stress image: polinux/stress imagePullPolicy: Always name: stress restartPolicy: Always nodeName: #nodeName注意修改为测试节点的nodeName,与Redis的节点相同。
使用以下命令,部署stress-demo。
kubectl apply -f stress-demo.yaml
使用以下命令,查看系统的全局最低水位线。
说明由于系统的全局最低水位线较低,对于内存超卖场景可能来不及回收就触发整机OOM,因此通常配合较高的全局最低水位线使用。以内存32 GiB的测试机为例,设置最低水位线为4000000 KB。
cat /proc/sys/vm/min_free_kbytes
预期输出:
4000000
在Pod YAML中,使用memtier-benchmark压测工具发送请求。
apiVersion: v1 kind: Pod metadata: labels: name: memtier-demo name: memtier-demo spec: containers: - command: - memtier_benchmark - '-s' - 'redis-demo' - '--data-size' - '200000' - "--ratio" - "1:4" image: 'redislabs/memtier_benchmark:1.3.0' name: memtier restartPolicy: Never nodeName: #nodeName注意修改为压测节点的nodeName。
使用以下命令,收集memtier-benchmark测试结果。
kubectl logs -f memtier-demo
在Pod YAML中,通过修改Redis实例和Stress实例的Pod Annotation,测试不同配置下的性能结果。
apiVersion: v1 kind: Pod metadata: name: redis-demo labels: koordinator.sh/qosClass: 'LS' annotations: koordinator.sh/memoryQOS: '{"policy": "none"}' #配置关闭容器内存QoS。 spec: ... --- apiVersion: v1 kind: Pod metadata: name: stress-demo labels: koordinator.sh/qosClass: 'BE' annotations: koordinator.sh/memoryQOS: '{"policy": "none"}' #配置关闭容器内存QoS。
结果分析
当功能全部关闭或开启ACK容器内存QoS时,指标的数据结果如下。
全部关闭:不开启容器内存QoS,Pod配置为
none
。开启容器内存QoS:Pod配置为
auto
,全部使用自适应配置。
指标 | 全部关闭 | 开启容器内存QoS |
| 51.32 ms | 47.25 ms |
| 149.0 MB/s | 161.9 MB/s |
由以上对比数据可得,内存超卖场景下,开启容器内存QoS后,Redis应用的时延(Latency)下降了7.9%,吞吐(Throughput)上涨了8.7%,时延和吞吐指标都得到一定的改善。
FAQ
当前已经通过ack-slo-manager的旧版本协议使用了容器内存QoS功能,升级为ack-koordinator后是否继续支持容器内存QoS功能?
旧版本(≤0.8.0)的Pod协议包括两部分。
在Pod的Annotation中填写
alibabacloud.com/qosClass
。在Pod的Annotation中填写
alibabacloud.com/memoryQOS
。
ack-koordinator保持了对以上旧版本协议的兼容,您可以将组件无缝升级至ack-koordinator。ack-koordinator将对以上旧版本协议兼容至2023年7月30日,我们建议您将原协议资源字段及时升级到新版本。
ack-koordinator各版本对内存QoS功能的适配如下。
ack-koordinator版本 | alibabacloud.com协议 | koordinator.sh协议 |
≥0.3.0且<0.8.0 | 支持 | 不支持 |
≥0.8.0 | 支持 | 支持 |
- 本页导读 (1)