预配额外的计算容量以快速扩缩 Pod


本页面介绍如何在 Google Kubernetes Engine (GKE) 集群中预留额外的计算容量,以便工作负载可以在高流量事件期间快速纵向扩容,而无需等待新节点启动。您可以按照以下说明,预留持续可用的计算开销,或在特定事件之前预留计算开销。

为何备用容量预配十分有用

如果没有现有节点具有足够容量来运行新 Pod,GKE Autopilot 集群和启用了节点自动预配功能的 Standard 集群会创建新节点。每个新节点的启动时间大约为 80 到 120 秒。GKE 会等到节点启动后再将待处理 Pod 放在新节点上,然后 Pod 便可以启动。在 Standard 集群中,您也可以手动创建新的节点池,该节点池具有运行新 Pod 所需的额外容量。本页面适用于使用节点自动扩缩机制(例如 Autopilot 或节点自动预配)的集群。

在某些情况下,您可能希望 Pod 在纵向扩容事件期间更快地启动。例如,您要为热门的实时服务多人游戏发布新的扩展,缩短游戏服务器 Pod 的启动时间可以减少玩家在发布当天登录时的排队时间。再举一个例子,您运营着一个电子商务平台,并且计划进行一次限定时间的闪购,那么可以预计闪购期间会出现流量激增。

GKE 中备用容量预配的工作原理

如需预配备用容量,您可以使用 Kubernetes PriorityClass 和占位 Pod。PriorityClass 可用于告诉 GKE,某些工作负载的优先级低于其他工作负载。您可以部署使用低优先级 PriorityClass 的占位 Pod,并请求需要预留的计算容量。GKE 会创建新节点来容纳占位 Pod,从而增加集群的容量。

当生产工作负载纵向扩容时,GKE 会逐出优先级较低的占位 Pod,并将生产 Pod 的新副本(使用优先级较高的 PriorityClass)安排到它们的位置上。如果您有多个具有不同优先级的低优先级 Pod,则 GKE 会首先逐出优先级最低的 Pod。

容量预配方法

根据您的应用场景,您可以通过以下任一方式在 GKE 集群中预配额外的容量:

  • 持续容量预配:使用 Deployment 创建特定数量的低优先级占位 Pod,这些 Pod 在集群中持续运行。当 GKE 逐出这些 Pod 以运行生产工作负载时,Deployment 控制器会确保 GKE 预配更多容量来重新创建逐出的低优先级 Pod。此方法可提供跨多个纵向扩容和纵向缩容事件的持续容量开销,直至您删除 Deployment。
  • 一次性容量预配:使用作业在特定时间段内运行特定数量的低优先级并行占位 Pod。运行时间结束或 GKE 逐出所有作业副本后,预留的容量不再可用。此方法可在特定时间段内提供特定数量的可用容量。

价格

在 GKE Autopilot 中,您需要为正在运行的 Pod 的资源请求付费,包括您部署的低优先级工作负载。如需了解详情,请参阅 Autopilot 价格

在 GKE Standard 中,您需要为 GKE 预配的底层 Compute Engine 虚拟机付费,无论 Pod 是否使用该容量。如需了解详情,请参阅 Standard 价格

准备工作

在开始之前,请确保您已执行以下任务:

  • 启用 Google Kubernetes Engine API。
  • 启用 Google Kubernetes Engine API
  • 如果您要使用 Google Cloud CLI 执行此任务,请安装初始化 gcloud CLI。 如果您之前安装了 gcloud CLI,请运行 gcloud components update 以获取最新版本。
  • 确保您拥有 GKE Autopilot 集群或启用了节点自动预配的 GKE Standard 集群。
  • 请阅读容量预配的注意事项,以确保在容量请求中选择适当的值。

创建 PriorityClass

如需使用容量预配方法中所述的任一方法,您首先需要创建以下 PriorityClass:

  • 默认 PriorityClass:如果任何 Pod 未在 Pod 规范中明确设置其他 PriorityClass,则向其分配全局默认 PriorityClass。具有此默认 PriorityClass 的 Pod 可以逐出使用较低 PriorityClass 的 Pod。
  • 低 PriorityClass:非默认 PriorityClass,设置为 GKE 中允许的最低优先级。GKE 可以逐出具有此 PriorityClass 的 Pod 以运行具有更高 PriorityClass 的 Pod。
  1. 将以下清单保存为 priorityclasses.yaml

    apiVersion: scheduling.k8s.io/v1
    kind: PriorityClass
    metadata:
      name: low-priority
    value: -10
    preemptionPolicy: Never
    globalDefault: false
    description: "Low priority workloads"
    ---
    apiVersion: scheduling.k8s.io/v1
    kind: PriorityClass
    metadata:
      name: default-priority
    value: 0
    preemptionPolicy: PreemptLowerPriority
    globalDefault: true
    description: "The global default priority."
    

    此清单包含以下字段:

    • preemptionPolicy:指定使用某个 PriorityClass 的 Pod 是否可以逐出优先级更低的 Pod。low-priority PriorityClass 使用 Neverdefault PriorityClass 使用 PreemptLowerPriority
    • value:使用 PriorityClass 的 Pod 的优先级。default PriorityClass 使用 0low-priority PriorityClass 使用 -1。在 Autopilot 中,您可以将此值设置为小于 default PriorityClass 优先级的任何值。

      在 Standard 中,如果将此值设置为小于 -10,则使用该 PriorityClass 的 Pod 不会触发新节点创建并保持为待处理状态。

      如需获得有关确定优先级的适当值的帮助,请参阅选择优先级

    • globalDefault:指定 GKE 是否将该 PriorityClass 分配给未在 Pod 规范中明确设置 PriorityClass 的 Pod。low-priority PriorityClass 使用 falsedefault PriorityClass 使用 true

  2. 应用清单:

    kubectl apply -f priorityclasses.yaml
    

预配额外的计算容量

以下部分显示了一个示例,您将为单个事件预配容量,或预配长时间持续可用的容量。

使用 Deployment 进行持续容量预配

  1. 将以下清单保存为 capacity-res-deployment.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: capacity-res-deploy
    spec:
      replicas: 10
      selector:
        matchLabels:
          app: reservation
      template:
        metadata:
          labels:
            app: reservation
        spec:
          priorityClassName: low-priority
          terminationGracePeriodSeconds: 0
          containers:
          - name: ubuntu
            image: ubuntu
            command: ["sleep"]
            args: ["infinity"]
            resources:
              requests:
                cpu: 500m
                memory: 500Mi
    

    此清单包含以下字段:

    • spec.replicas:更改此值以满足您的要求。
    • spec.resources.requests:更改 CPU 和内存请求以满足您的要求。使用选择容量大小中的指导信息来帮助您确定适当的请求值。
    • spec.containers.commandspec.containers.args:告诉 Pod 保持活跃状态,直到被 GKE 逐出。
  2. 应用清单:

    kubectl apply -f capacity-res-deployment.yaml
    
  3. 获取 Pod 状态:

    kubectl get pods -l app=reservation
    

    等待所有副本的状态变为 Running

使用作业进行一次性容量预配

  1. 将以下清单保存为 capacity-res-job.yaml

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: capacity-res-job
    spec:
      parallelism: 4
      backoffLimit: 0
      template:
        spec:
          priorityClassName: low-priority
          terminationGracePeriodSeconds: 0
          containers:
          - name: ubuntu-container
            image: ubuntu
            command: ["sleep"]
            args: ["36000"]
            resources:
              requests:
                cpu: "16"
          restartPolicy: Never
    

    此清单包含以下字段:

    • spec.parallelism:更改为为预留容量而需要并行运行的作业数量。
    • spec.backoffLimit: 0:阻止作业控制器重新创建被逐出的作业。
    • template.spec.resources.requests:更改 CPU 和内存请求以满足您的要求。请参阅注意事项中的指导信息,以确定适当的值。
    • template.spec.containers.commandtemplate.spec.containers.args:告诉作业在需要额外容量的时间段内保持活跃状态(以秒为单位)。
  2. 应用清单:

    kubectl apply -f capacity-res-job.yaml
    
  3. 获取作业状态:

    kubectl get jobs
    

    等待所有作业状态变为 Running

测试容量预配和逐出

如需确认容量预配按预期工作,请执行以下操作:

  1. 在您的终端中,查看容量预配工作负载的状态:

    1. 对于 Deployment,请运行以下命令:

      kubectl get pods --label=app=reservation -w
      
    2. 对于作业,请运行以下命令:

      kubectl get Jobs -w
      
  2. 打开终端窗口并执行以下操作:

    1. 将以下清单保存为 test-deployment.yaml

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: helloweb
        labels:
          app: hello
      spec:
        replicas: 5
        selector:
          matchLabels:
            app: hello
            tier: web
        template:
          metadata:
            labels:
              app: hello
              tier: web
          spec:
            containers:
            - name: hello-app
              image: us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0
              ports:
              - containerPort: 8080
              resources:
                requests:
                  cpu: 400m
                  memory: 400Mi
      
    2. 应用清单:

      kubectl apply -f test-deployment.yaml
      
  3. 在原始终端窗口中可以看到,GKE 终止部分容量预配工作负载以安排新副本,类似于以下示例:

    NAME                                         READY   STATUS    RESTARTS   AGE
    capacity-res-deploy-6bd9b54ffc-5p6wc         1/1     Running   0          7m25s
    capacity-res-deploy-6bd9b54ffc-9tjbt         1/1     Running   0          7m26s
    capacity-res-deploy-6bd9b54ffc-kvqr8         1/1     Running   0          2m32s
    capacity-res-deploy-6bd9b54ffc-n7zn4         1/1     Running   0          2m33s
    capacity-res-deploy-6bd9b54ffc-pgw2n         1/1     Running   0          2m32s
    capacity-res-deploy-6bd9b54ffc-t5t57         1/1     Running   0          2m32s
    capacity-res-deploy-6bd9b54ffc-v4f5f         1/1     Running   0          7m24s
    helloweb-85df88c986-zmk4f                    0/1     Pending   0          0s
    helloweb-85df88c986-lllbd                    0/1     Pending   0          0s
    helloweb-85df88c986-bw7x4                    0/1     Pending   0          0s
    helloweb-85df88c986-gh8q8                    0/1     Pending   0          0s
    helloweb-85df88c986-74jrl                    0/1     Pending   0          0s
    capacity-res-deploy-6bd9b54ffc-v6dtk   1/1     Terminating   0          2m47s
    capacity-res-deploy-6bd9b54ffc-kvqr8   1/1     Terminating   0          2m47s
    capacity-res-deploy-6bd9b54ffc-pgw2n   1/1     Terminating   0          2m47s
    capacity-res-deploy-6bd9b54ffc-n7zn4   1/1     Terminating   0          2m48s
    capacity-res-deploy-6bd9b54ffc-2f8kx   1/1     Terminating   0          2m48s
    ...
    helloweb-85df88c986-lllbd              0/1     Pending       0          1s
    helloweb-85df88c986-gh8q8              0/1     Pending       0          1s
    helloweb-85df88c986-74jrl              0/1     Pending       0          1s
    helloweb-85df88c986-zmk4f              0/1     Pending       0          1s
    helloweb-85df88c986-bw7x4              0/1     Pending       0          1s
    helloweb-85df88c986-gh8q8              0/1     ContainerCreating   0          1s
    helloweb-85df88c986-zmk4f              0/1     ContainerCreating   0          1s
    helloweb-85df88c986-bw7x4              0/1     ContainerCreating   0          1s
    helloweb-85df88c986-lllbd              0/1     ContainerCreating   0          1s
    helloweb-85df88c986-74jrl              0/1     ContainerCreating   0          1s
    helloweb-85df88c986-zmk4f              1/1     Running             0          4s
    helloweb-85df88c986-lllbd              1/1     Running             0          4s
    helloweb-85df88c986-74jrl              1/1     Running             0          5s
    helloweb-85df88c986-gh8q8              1/1     Running             0          5s
    helloweb-85df88c986-bw7x4              1/1     Running             0          5s
    

    此输出显示新的 Deployment 从“Pending”变为“Running”需要五秒钟。

容量预配的注意事项

持续容量预配

  • 请评估您需要多少个占位 Pod 副本以及每个副本中的请求大小。低优先级副本应至少请求与最大生产工作负载相同的容量,以便这些工作负载适配低优先级工作负载预留的容量。
  • 如果您大规模运行大量生产工作负载,请考虑将占位 Pod 的资源请求设置为预配足够容量来运行多个(而不是仅一个)生产工作负载的值。

一次性容量预配

  • 将占位作业的寿命设置为您需要额外容量的时间。例如,如果您在 24 小时的游戏发布日内需要额外容量,请将时长设置为 86400 秒。这样可确保预配容量的持续时间不会超过您需要的时长。
  • 设置与预留容量时长相同的维护窗口。这样可以防止低优先级作业在节点升级期间被逐出。当您预期工作负载会迎来高需求时,设置维护期也是一种很好的做法。
  • 如果您大规模运行大量生产工作负载,请考虑将占位作业的资源请求设置为预配足够容量来运行多个(而不是仅一个)生产工作负载的值。

预配的容量仅用于单个扩缩事件。如果您纵向扩容并使用容量,然后纵向缩容,则该容量将无法再用于其他纵向扩容事件。如果您预计有多个纵向扩容和纵向缩容事件,请使用持续容量预留方法,并根据需要调整预留大小。例如,在事件发生之前设置较大的 Pod 请求,并在事件结束之后减小或设置为零。

选择优先级

将 PriorityClass 中的优先级设置为小于 0。

您可以在集群中定义多个 PriorityClass,以用于具有不同要求的工作负载。例如,您可以创建一个优先级为 -10 的 PriorityClass 用于一次性容量预配,并创建一个优先级为 -9 的 PriorityClass 用于持续容量预配。然后,您可以使用优先级为 -9 的 PriorityClass 预配持续容量。当您需要为特殊事件提供更多容量时,则可以部署使用 -10 优先级 PriorityClass 的新作业。GKE 会首先逐出优先级最低的工作负载。

您还可以使用其他 PriorityClass 运行执行实际任务的低优先级非生产工作负载,例如容错批量工作负载。这些工作负载的优先级应低于生产工作负载,但高于占位 Pod。例如 -5。

选择容量大小

将占位工作负载的副本数量和资源请求设置为大于或等于生产工作负载在纵向扩容时可能需要的容量。

预配的总容量基于您部署的占位 Pod 的数量以及每个副本的资源请求。如果纵向扩容需要的容量大于 GKE 为占位 Pod 预配的容量,则某些生产工作负载将保持 Pending 状态,直到 GKE 可以预配更多容量。

后续步骤