×

巨页的作用

内存以称为页面的块进行管理。在大多数系统上,一个页面是 4Ki。1Mi 内存等于 256 个页面;1Gi 内存是 256,000 个页面,依此类推。CPU 具有内置的内存管理单元,用于在硬件中管理这些页面的列表。转换旁路缓冲区 (TLB) 是虚拟到物理页面映射的小型硬件缓存。如果在硬件指令中传递的虚拟地址可以在 TLB 中找到,则可以快速确定映射。如果没有,则会发生 TLB 未命中,系统将回退到较慢的基于软件的地址转换,从而导致性能问题。由于 TLB 的大小是固定的,因此减少 TLB 未命中机会的唯一方法是增加页面大小。

巨页是一个大于 4Ki 的内存页面。在 x86_64 架构上,有两种常见的巨页大小:2Mi 和 1Gi。其他架构上的大小各不相同。要使用巨页,必须编写代码,以便应用程序能够识别它们。透明巨页 (THP) 尝试在无需应用程序了解的情况下自动管理巨页,但它们有局限性。特别是,它们仅限于 2Mi 页面大小。由于 THP 的碎片整理工作可能会锁定内存页面,因此 THP 可能会导致内存利用率高或碎片严重的节点的性能下降。因此,某些应用程序可能被设计为(或建议)使用预分配的巨页而不是 THP。

在 OpenShift Container Platform 中,Pod 中的应用程序可以分配和使用预分配的巨页。

应用程序如何使用巨页

节点必须预先分配巨页,以便节点报告其巨页容量。一个节点只能为单个大小预分配巨页。

巨页可以通过使用资源名称 `hugepages-` 的容器级资源需求来使用,其中 size 是特定节点上支持的整数值得最紧凑的二进制表示法。例如,如果节点支持 2048KiB 页面大小,则它会公开可调度的资源 `hugepages-2Mi`。与 CPU 或内存不同,巨页不支持过度分配。

apiVersion: v1
kind: Pod
metadata:
  generateName: hugepages-volume-
spec:
  containers:
  - securityContext:
      privileged: true
    image: rhel7:latest
    command:
    - sleep
    - inf
    name: example
    volumeMounts:
    - mountPath: /dev/hugepages
      name: hugepage
    resources:
      limits:
        hugepages-2Mi: 100Mi (1)
        memory: "1Gi"
        cpu: "1"
  volumes:
  - name: hugepage
    emptyDir:
      medium: HugePages
1 将 `hugepages` 的内存量指定为要分配的确切量。不要将此值指定为 `hugepages` 内存量乘以页面大小。例如,给定 2MB 的巨页大小,如果您想为您的应用程序使用 100MB 的巨页支持的 RAM,则您将分配 50 个巨页。OpenShift Container Platform 将为您处理计算。如上例所示,您可以直接指定 `100MB`。

分配特定大小的巨页

某些平台支持多种巨页大小。要分配特定大小的巨页,请在巨页启动命令参数前加上巨页大小选择参数 `hugepagesz=`。` ` 值必须以字节为单位指定,并带有一个可选的比例后缀 [kKmMgG]。默认巨页大小可以使用 `default_hugepagesz=` 启动参数定义。

巨页需求

  • 巨页请求必须等于限制。如果指定了限制但未指定请求,则这是默认值。

  • 巨页在 Pod 范围内隔离。容器隔离计划在未来的迭代中实现。

  • 由巨页支持的 `EmptyDir` 卷不得消耗超过 Pod 请求的巨页内存。

  • 通过 `shmget()` 和 `SHM_HUGETLB` 使用巨页的应用程序必须使用与 **`proc/sys/vm/hugetlb_shm_group`** 匹配的补充组运行。

使用下行 API 使用巨页资源

您可以使用下行 API 来注入有关容器使用的巨页资源的信息。

您可以将资源分配作为环境变量、卷插件或两者都注入。您在容器中开发和运行的应用程序可以通过读取环境变量或指定卷中的文件来确定可用的资源。

步骤
  1. 创建一个类似于以下示例的 `hugepages-volume-pod.yaml` 文件

    apiVersion: v1
    kind: Pod
    metadata:
      generateName: hugepages-volume-
      labels:
        app: hugepages-example
    spec:
      containers:
      - securityContext:
          capabilities:
            add: [ "IPC_LOCK" ]
        image: rhel7:latest
        command:
        - sleep
        - inf
        name: example
        volumeMounts:
        - mountPath: /dev/hugepages
          name: hugepage
        - mountPath: /etc/podinfo
          name: podinfo
        resources:
          limits:
            hugepages-1Gi: 2Gi
            memory: "1Gi"
            cpu: "1"
          requests:
            hugepages-1Gi: 2Gi
        env:
        - name: REQUESTS_HUGEPAGES_1GI (1)
          valueFrom:
            resourceFieldRef:
              containerName: example
              resource: requests.hugepages-1Gi
      volumes:
      - name: hugepage
        emptyDir:
          medium: HugePages
      - name: podinfo
        downwardAPI:
          items:
            - path: "hugepages_1G_request" (2)
              resourceFieldRef:
                containerName: example
                resource: requests.hugepages-1Gi
                divisor: 1Gi
    1 指定从 `requests.hugepages-1Gi` 读取资源使用情况,并将值作为 `REQUESTS_HUGEPAGES_1GI` 环境变量公开。
    2 指定从 `requests.hugepages-1Gi` 读取资源使用情况,并将值作为文件 `/etc/podinfo/hugepages_1G_request` 公开。
  2. 从 `hugepages-volume-pod.yaml` 文件创建 Pod

    $ oc create -f hugepages-volume-pod.yaml
验证
  1. 检查 `REQUESTS_HUGEPAGES_1GI` 环境变量的值

    $ oc exec -it $(oc get pods -l app=hugepages-example -o jsonpath='{.items[0].metadata.name}') \
         -- env | grep REQUESTS_HUGEPAGES_1GI
    示例输出
    REQUESTS_HUGEPAGES_1GI=2147483648
  2. 检查 `/etc/podinfo/hugepages_1G_request` 文件的值

    $ oc exec -it $(oc get pods -l app=hugepages-example -o jsonpath='{.items[0].metadata.name}') \
         -- cat /etc/podinfo/hugepages_1G_request
    示例输出
    2

在启动时配置巨页

节点必须预先分配在 OpenShift Container Platform 集群中使用的巨页。预留巨页有两种方法:在启动时和在运行时。在启动时预留的成功率更高,因为内存尚未出现明显的碎片。节点调优运算符目前支持在特定节点上启动时分配巨页。

步骤

为了最大限度地减少节点重启次数,需要按照以下步骤的顺序进行操作

  1. 使用标签标记所有需要相同巨页设置的节点。

    $ oc label node <node_using_hugepages> node-role.kubernetes.io/worker-hp=
  2. 创建一个包含以下内容的文件,并将其命名为 `hugepages-tuned-boottime.yaml`

    apiVersion: tuned.openshift.io/v1
    kind: Tuned
    metadata:
      name: hugepages (1)
      namespace: openshift-cluster-node-tuning-operator
    spec:
      profile: (2)
      - data: |
          [main]
          summary=Boot time configuration for hugepages
          include=openshift-node
          [bootloader]
          cmdline_openshift_node_hugepages=hugepagesz=2M hugepages=50 (3)
        name: openshift-node-hugepages
    
      recommend:
      - machineConfigLabels: (4)
          machineconfiguration.openshift.io/role: "worker-hp"
        priority: 30
        profile: openshift-node-hugepages
    1 将 Tuned 资源的 `name` 设置为 `hugepages`。
    2 将 `profile` 部分设置为分配巨页。
    3 请注意,参数的顺序很重要,因为某些平台支持各种大小的巨页。
    4 启用基于机器配置池的匹配。
  3. 创建 Tuned `hugepages` 对象

    $ oc create -f hugepages-tuned-boottime.yaml
  4. 创建一个包含以下内容的文件,并将其命名为 `hugepages-mcp.yaml`

    apiVersion: machineconfiguration.openshift.io/v1
    kind: MachineConfigPool
    metadata:
      name: worker-hp
      labels:
        worker-hp: ""
    spec:
      machineConfigSelector:
        matchExpressions:
          - {key: machineconfiguration.openshift.io/role, operator: In, values: [worker,worker-hp]}
      nodeSelector:
        matchLabels:
          node-role.kubernetes.io/worker-hp: ""
  5. 创建机器配置池

    $ oc create -f hugepages-mcp.yaml

如果拥有足够的非碎片内存,则 `worker-hp` 机器配置池中的所有节点现在都应分配 50 个 2Mi 巨页。

$ oc get node <node_using_hugepages> -o jsonpath="{.status.allocatable.hugepages-2Mi}"
100Mi

TuneD 引导加载程序插件仅支持 Red Hat Enterprise Linux CoreOS (RHCOS) 工作节点。

禁用透明巨页 (THP)

透明巨页 (THP) 尝试自动化创建、管理和使用巨页的大部分方面。由于 THP 自动管理巨页,因此并非总是能针对所有类型的负载进行最佳处理。THP 可能会导致性能下降,因为许多应用程序会自行处理巨页。因此,请考虑禁用 THP。以下步骤描述了如何使用节点调优运算符 (NTO) 禁用 THP。

步骤
  1. 创建一个包含以下内容的文件,并将其命名为 thp-disable-tuned.yaml

    apiVersion: tuned.openshift.io/v1
    kind: Tuned
    metadata:
      name: thp-workers-profile
      namespace: openshift-cluster-node-tuning-operator
    spec:
      profile:
      - data: |
          [main]
          summary=Custom tuned profile for OpenShift to turn off THP on worker nodes
          include=openshift-node
    
          [vm]
          transparent_hugepages=never
        name: openshift-thp-never-worker
    
      recommend:
      - match:
        - label: node-role.kubernetes.io/worker
        priority: 25
        profile: openshift-thp-never-worker
  2. 创建 Tuned 对象

    $ oc create -f thp-disable-tuned.yaml
  3. 检查活动配置文件列表

    $ oc get profile -n openshift-cluster-node-tuning-operator
验证
  • 登录到其中一个节点并进行常规 THP 检查,以验证节点是否已成功应用配置文件

    $ cat /sys/kernel/mm/transparent_hugepage/enabled
    示例输出
    always madvise [never]