启用容器内存QoS

Kubernetes支持以Request和Limit的形式来描述容器内存资源需求,应用的内存性能会受到多种因素影响,例如Page Cache回收、其他容器的过度使用,甚至引发节点资源不足而导致整机OOM,影响服务质量。ack-koordinator提供容器内存QoS功能,支持根据优先级为容器设置服务质量参数,可以在确保内存资源公平性的前提下,优先保障高优先级应用的性能。

说明

为了帮助您更好地理解本文档并使用本功能,推荐您参见Kubernetes官方文档了解Pod Qos类为容器和 Pod 分配内存资源等概念。

功能介绍

为什么需要容器内存QoS

在Kubernetes集群,为了确保工作负载Pod能够高效、安全地运行,Kubernetes在资源使用层面引入了资源请求Request和资源限制Limit模型,容器内存情况如下图所示。

  • 内存Request(requests.memory):作用于调度阶段,以确保为Pod找到一个具有足够资源的合适节点。

  • 内存Limit(requests.memory):在单个节点层面进行约束,限制Pod内存使用总量,对应cgroup文件参数memory.limit_in_bytes,表示内存使用上限。

image

容器在使用内存时,其服务质量会受自身用量以及节点用量两个方面的影响。

  • 自身内存限制:当Pod自身的内存使用(含Page Cache)接近声明的Limit值时,会触发内存子系统(Memcg)级别的直接内存回收,阻塞进程执行。如果此时的内存申请速度超过回收速度,容器会因触发OOM而被异常终止。

  • 节点内存限制:容器的Limit参数可以高于Request。当多个容器部署在同一节点上时,可能会导致容器内存Limit之和超出节点物理总量。当整机内存用量较高时,同样会触发内存资源回收,影响应用性能,甚至在资源不足时,应用会因整机OOM而被异常终止。

功能特性

为了提高应用运行时性能和节点的稳定性,ack-koordinator组件结合不同Alibaba Cloud Linux内核版本提供了容器内存QoS保障的能力,支持依据Pod参数自动配置内存子系统(Memcg),为容器开启Memcg QoSMemcg后台异步回收Memcg全局最低水位线分级等特性,以保障容器的内存资源QoS和公平性。

容器内存回收及保障策略

容器的内存QoS支持多个cgroup参数配置。

  • memory.limit_in_bytes:内存使用上限。

  • memory.high:内存限流阈值,内核会尽量回收内存,避免内存超过该值。

  • memory.wmark_high:内存后台异步回收阈值(wmarkRatio),异步回收当前可以回收的内存,以确保当前内存使用量处于安全水位。

  • memory.min:内存使用锁定阈值,可配置绝对锁定阈值(minLimitPercent)和相对锁定阈值(lowLimitPercent)。

关于参数的详细介绍及配置说明,请参见下文高级参数配置

image

容器内存QoS功能可以带来以下功能特性。

  • Pod内存使用接近Limit限制时,优先在后台异步回收一部分内存,缓解直接内存回收带来的性能影响。

  • Pod之间实施更公平的内存回收。当整机内存资源不足时,优先从内存超用的Pod中回收内存,避免个别Pod造成整机内存资源质量下降。

  • 整机资源回收时,优先保障延时敏感型LS(Latency-Sensitive)Pod,即Guaranteed、Burstable Pod的内存运行质量。

灵活配置、多环境适配

Kubernetes在社区1.22版本中提供的容器内存QoS(Memory QoS)特性,仅支持cgroup v2接口,您需要在kubelet上手动配置开启,启用后会对所有节点上的所有容器生效,无法进行更细粒度的配置。相比之下,ack-koordinator的容器内存QoS功能有以下能力优化。

  • 结合Alibaba Cloud Linux,相比社区额外提供内存后台回收、最低水位线分级等QoS增强能力,并自适应兼容cgroup v1和cgroup v2接口。更多关于ACK容器内存QoS启用的内核能力,请参见Alibaba Cloud Linux的内核功能与接口概述

  • 支持通过Pod Annotation或ConfigMap实现指定Pod维度的、集群全局维度的或者命名空间维度的容器内存QoS配置,更加灵活,使用简单。

前提条件

费用说明

ack-koordinator组件本身的安装和使用是免费的,不过需要注意的是,在以下场景中可能产生额外的费用:

  • ack-koordinator是非托管组件,安装后将占用Worker节点资源。您可以在安装组件时配置各模块的资源申请量。

  • ack-koordinator默认会将资源画像、精细化调度等功能的监控指标以Prometheus的格式对外透出。若您配置组件时开启了ACK-Koordinator开启Prometheus监控指标选项并使用了阿里云Prometheus服务,这些指标将被视为自定义指标并产生相应费用。具体费用取决于您的集群规模和应用数量等因素。建议您在启用此功能前,仔细阅读阿里云Prometheus计费说明,了解自定义指标的免费额度和收费策略。您可以通过账单和用量查询,监控和管理您的资源使用情况。

配置说明

当您为Pod开启容器内存QoS时,内存cgroup参数将基于系数配置和Pod参数自适应调整。您可以参见下文,在单个Pod维度、集群全局维度或集群下命名空间维度开启容器内存QoS并配置相关参数。

通过Annotation为指定Pod配置

您可以通过Pod Annotation为指定Pod开启容器内存QoS功能。

annotations:
  # 设置为auto,表示开启该Pod的容器内存QoS功能。
  koordinator.sh/memoryQOS: '{"policy": "auto"}'
  # 设置为none,表示关闭该Pod的容器内存QoS功能。
  koordinator.sh/memoryQOS: '{"policy": "none"}'

通过ConfigMap在集群维度配置

您可以通过ConfigMap配置,在集群维度开启容器内存QoS,针对集群全局生效。您可以根据应用特征,使用koordinator.sh/qosClass来统一管理内存QoS参数,并在Pod Labelkoordinator.sh/qosClass配置Pod的QoS级别为LSBE等级,无需通过Pod Annotation重复配置。

  1. 参见以下示例创建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
  2. 在Pod YAML中指定QoS等级为LSBE

    说明

    如果Pod中没有指定koordinator.sh/qosClassack-koordinator将参考Pod原生的QoSClass来设置参数,其中Guaranteed为系统内部默认值,Burstable和Besteffort分别使用ConfigMap中LSBE的默认配置。

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-demo
      labels:
        koordinator.sh/qosClass: 'LS' # 指定Pod的QoS级别为LS。
  3. 查看命名空间kube-system下是否存在ConfigMap ack-slo-config

    • 存在:使用PATCH方式进行更新,避免干扰ConfigMap中其他配置项。

      kubectl patch cm -n kube-system ack-slo-config --patch "$(cat configmap.yaml)"
    • 不存在:执行以下命令创建ConfigMap。

      kubectl apply -f configmap.yaml
  4. (可选)参见下文高级参数配置配置高级参数。

通过ConfigMap在NameSpace维度配置

若您需要开启或关闭部分命名空间下LSBE Pod的参数设置,可以参见以下流程,为指定Namespace内的Pod开启或禁用容器内存QoS功能,在命名空间级别生效。

  1. 参见以下示例创建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
  2. 使用以下ConfigMap内容,创建ack-slo-pod-config.yaml文件。

    下方代码以命名空间kube-system为例,展示如何开启或禁用kube-system中Pod的容器内存QoS功能。

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: ack-slo-pod-config
      namespace: kube-system # 首次使用时需要先手动创建该Namespace。
    data:
      # 单独开启或关闭部分Namespace的Pod。
      memory-qos: |
        {
          "enabledNamespaces": ["allow-ns"],
          "disabledNamespaces": ["block-ns"]
        }
  3. 执行以下命令,更新ConfigMap。

    kubectl patch cm -n kube-system ack-slo-pod-config --patch "$(cat ack-slo-pod-config.yaml)"
  4. (可选)参见下文高级参数配置配置高级参数。

操作步骤

本小节以一个Redis应用为例,使用以下评测环境进行测试,展示在内存超卖场景下,关闭或开启ACK容器内存QoS时,示例应用的时延和吞吐性能表现差异。

  • 使用ACK集群Pro版

  • 集群内包含两个节点:压测节点(规格为8 Core 32 GB)、测试节点(规格为8 Core 32 GB)。

验证步骤

  1. 使用以下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
  2. 执行以下命令,部署Redis Server作为目标评测应用。

    您可通过Service redis-demo进行集群内访问。

    kubectl apply -f redis-demo.yaml
  3. 模拟内存超卖场景。

    使用Stress工具制造较大的节点内存压力,触发系统内存回收,节点上已分配Pod的内存Limit之和已超过整机大小。

    1. 使用以下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的节点相同。
    2. 使用以下命令,部署stress-demo。

      kubectl apply -f stress-demo.yaml
  4. 使用以下命令,查看系统的全局最低水位线。

    说明

    由于系统的全局最低水位线较低,对于内存超卖场景可能来不及回收就触发整机OOM,因此通常配合较高的全局最低水位线使用。以内存32 GiB的测试机为例,设置最低水位线为4000000 KB。

    cat /proc/sys/vm/min_free_kbytes

    预期输出:

    4000000
  5. 在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。
  6. 使用以下命令,收集memtier-benchmark测试结果。

    kubectl logs -f memtier-demo
  7. 在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。
                            

结果分析

当功能全部关闭或开启容器内存QoS时,指标的数据结果如下。

  • 全部关闭:不开启容器内存QoS,Pod配置为none

  • 开启容器内存QoS:Pod配置为auto,全部使用自适应配置。

重要

以下结果数据仅为理论值(参考值),实际数据以您的操作环境为准。

指标

全部关闭

开启容器内存QoS

Latency-avg

51.32 ms

47.25 ms

Throughput-avg

149.0 MB/s

161.9 MB/s

由对比数据可得,在内存超卖场景下,开启容器内存QoS后,Redis应用的时延(Latency)下降了7.9%,吞吐(Throughput)上涨了8.7%,时延和吞吐指标都得到一定的改善。

高级参数配置

容器内存QoS支持Pod级别和集群级别的精细化配置,对于同时支持在Pod Annotation和ConfigMap配置的参数,Pod Annotation配置优先级大于ConfigMap配置。如果Pod Annotation中没有对应配置,ack-koordinator会进一步参考Namespace维度的ConfigMap配置;如果Namespace维度没有对应的配置,ack-koordinator会以集群维度的ConfigMap配置为准。

说明

AnnotationConfigMap两列分别代表是否允许通过Pod Annotation或ConfigMap进行配置。其中,对代表支持,错代表不支持。

参数

类型

取值范围

说明

Pod Annotation

ConfigMap

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,容器内存绝对锁定、不被全局回收的比例。适合对Page Cache敏感的场景开启,保留一部分文件缓存不被回收以改善读写性能。更多信息,请参见Alibaba Cloud Linux cgroup v1接口支持memcg QoS功能

计算方式:memory.min=request*minLimitPercent/100。例如,容器Memory Request=100MiB,当minLimitPercent=100时,memory.min=104857600

对

对

lowLimitPercent

Int

0~100

单位为百分比,默认值为0,表示关闭。

相较于内存Request,容器内存相对锁定、优先不被回收的比例。更多信息,请参见Alibaba Cloud Linux cgroup v1接口支持memcg QoS功能

计算方式为memory.low=request*lowLimitPercent/100。例如,容器Memory Request=100MiB,当lowLimitPercent=100时,memory.low=104857600

对

对

throttlingPercent

Int

0~100

单位为百分比,默认值为0,表示关闭。

相较于内存Limit,容器内存使用触发限流的比例。超出限流阈值时,容器将进入较大压力的主动内存回收状态。适用于应用内存超卖的场景(Request<Limit),以规避cgroup级别的OOM。更多信息,请参见Alibaba Cloud Linux cgroup v1接口支持memcg QoS功能

计算方式为memory.high=limit*throttlingPercent/100。例如,容器Memory Limit=100MiB,当throttlingPercent=80时,memory.high=83886080(80 MiB)

对

对

wmarkRatio

Int

0~100

单位为百分比,默认值为950表示关闭。超出异步回收阈值时,容器将启动后台内存回收。

相较于内存Limit和memory.high,容器触发异步内存异步回收的比例。更多信息,请参见Alibaba Cloud Linux Memcg后台异步回收

计算方式为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)

对

对

wmarkMinAdj

Int

-25~50

单位为百分比,默认值依据QoS,LS对应-25BE对应500表示关闭。

相较于(整机)全局内存最低水位线,容器所做出的调整比例。负数值让容器更晚进入全局内存回收,正数值让容器更早进入全局内存回收。更多信息,请参见Alibaba Cloud Linux Memcg全局最低水位线分级

例如,如果Pod的QoS等级为LS,按默认配置,最低水位线分级系数memory.wmark_min_adj=-25,相应容器的最低水位线将下调25%。

对

对

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-koordinatorack-koordinator将对以上旧版本协议兼容至2023年7月30日,我们建议您将原协议资源字段及时升级到新版本。

ack-koordinator各版本对内存QoS功能的适配如下。

组件版本

alibabacloud.com协议

koordinator.sh协议

≥0.3.0且<0.8.0

支持

不支持

≥0.8.0

支持

支持