Todos os produtos
Search
Central de documentação

Container Service for Kubernetes:Work with capacity scheduling

Última atualização: Jun 27, 2026

Configure cotas hierárquicas com mínimos garantidos para compartilhar a capacidade ociosa do cluster entre equipes.

O ResourceQuota nativo do Kubernetes impõe limites fixos de recursos e frequentemente deixa recursos ociosos quando as equipes não utilizam totalmente sua cota. O ACK implementa o agendamento de capacidade por meio da extensão do framework de agendamento e substitui esse modelo estático por grupos de cota elástica: recursos ociosos são compartilhados e recuperados quando os proprietários precisam deles. Isso melhora a utilização do cluster sem comprometer as garantias de recursos.

Pré-requisitos

Verifique se você tem:

Conceitos principais

ElasticQuotaTree é uma CustomResourceDefinition (CRD) que define uma hierarquia de grupos de cota elástica. Cada nó representa um limite de cota. Os nós folha mapeiam para um ou mais namespaces, e os pods nesses namespaces são agendados dentro dos limites de cota do respectivo nó folha.

Os dois campos principais em cada nó de cota são:

Campo

Significado

min

Recursos garantidos. O agendador garante essa quantidade disponível e recupera recursos emprestados se necessário.

max

Recursos máximos que o nó de cota pode usar, incluindo recursos ociosos emprestados de outros nós.

O empréstimo e a recuperação de recursos funcionam da seguinte forma:

  • Um pod é agendado se seus recursos solicitados, somados ao uso atual do nó, permanecerem dentro de max.

  • Se o uso do nó exceder min, o excedente é emprestado da capacidade ociosa em outra parte da árvore.

  • Quando outro nó de cota precisa recuperar seus recursos min, o agendador seleciona pods do nó mutuário para evicção com base em fatores como prioridade do job, disponibilidade e horário de criação.

Recursos

  • Cotas hierárquicas: Configure cotas elásticas em vários níveis (por exemplo, correspondendo à estrutura da sua organização). Cada nó folha pode mapear para vários namespaces, mas cada namespace pertence a apenas um nó folha.37

  • Empréstimo e recuperação de recursos: Outros nós de cota podem emprestar recursos min ociosos. A recuperação dos recursos emprestados ocorre automaticamente quando o proprietário original precisa deles.39

  • Suporte a recursos estendidos: Além de CPU e memória, o agendamento de capacidade suporta GPU (nvidia.com/gpu) e outros tipos de recursos suportados pelo Kubernetes.

  • Afinidade de nó com ResourceFlavor: Anexe um ResourceFlavor a um nó de cota para restringir os pods dessa cota a nós específicos. Consulte Configurar ResourceFlavor para afinidade de nó.

Configurar o agendamento de capacidade

Este exemplo utiliza um cluster com um único nó ecs.sn2.13xlarge (56 vCPUs e 224 GiB de memória).

Etapa 1: Crie namespaces

kubectl create ns namespace1
kubectl create ns namespace2
kubectl create ns namespace3
kubectl create ns namespace4

Etapa 2: Crie uma ElasticQuotaTree

Crie a ElasticQuotaTree no namespace kube-system. Este exemplo usa uma hierarquia de dois níveis com quatro nós de cota folha.

A ElasticQuotaTree só entra em vigor quando criada no namespace kube-system.
apiVersion: scheduling.sigs.k8s.io/v1beta1
kind: ElasticQuotaTree
metadata:
  name: elasticquotatree
  namespace: kube-system
spec:
  root:
    name: root
    max:
      cpu: 40
      memory: 40Gi
      nvidia.com/gpu: 4
    min:
      cpu: 40
      memory: 40Gi
      nvidia.com/gpu: 4
    children:
      - name: root.a
        max:
          cpu: 40
          memory: 40Gi
          nvidia.com/gpu: 4
        min:
          cpu: 20
          memory: 20Gi
          nvidia.com/gpu: 2
        children:
          - name: root.a.1
            namespaces:
              - namespace1
            max:
              cpu: 20
              memory: 20Gi
              nvidia.com/gpu: 2
            min:
              cpu: 10
              memory: 10Gi
              nvidia.com/gpu: 1
          - name: root.a.2
            namespaces:
              - namespace2
            max:
              cpu: 20
              memory: 40Gi
              nvidia.com/gpu: 2
            min:
              cpu: 10
              memory: 10Gi
              nvidia.com/gpu: 1
      - name: root.b
        max:
          cpu: 40
          memory: 40Gi
          nvidia.com/gpu: 4
        min:
          cpu: 20
          memory: 20Gi
          nvidia.com/gpu: 2
        children:
          - name: root.b.1
            namespaces:
              - namespace3
            max:
              cpu: 20
              memory: 20Gi
              nvidia.com/gpu: 2
            min:
              cpu: 10
              memory: 10Gi
              nvidia.com/gpu: 1
          - name: root.b.2
            namespaces:
              - namespace4
            max:
              cpu: 20
              memory: 20Gi
              nvidia.com/gpu: 2
            min:
              cpu: 10
              memory: 10Gi
              nvidia.com/gpu: 1
Importante

A ElasticQuotaTree deve satisfazer estas restrições:

  • Dentro de cada nó de cota: minmax

  • Para cada nó pai: soma dos valores min dos filhos ≤ valor min do pai

  • Para o nó raiz: min = max ≤ total de recursos do cluster

  • Cada namespace pertence exatamente a um nó folha; um nó folha pode conter vários namespaces

Etapa 3: Verifique a ElasticQuotaTree

kubectl get ElasticQuotaTree -n kube-system

Saída esperada:

NAME               AGE
elasticquotatree   68s

Observar o empréstimo e a recuperação de recursos

Estes cenários mostram como o empréstimo e a recuperação funcionam à medida que as cargas de trabalho são implantadas nos quatro namespaces.

Emprestar recursos ociosos

  1. Implante uma carga de trabalho em namespace1. Este Deployment solicita 5 réplicas, cada uma usando 5 vCPUs (25 vCPUs no total).

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx1
      namespace: namespace1
      labels:
        app: nginx1
    spec:
      replicas: 5
      selector:
        matchLabels:
          app: nginx1
      template:
        metadata:
          name: nginx1
          labels:
            app: nginx1
        spec:
          containers:
          - name: nginx1
            image: nginx
            resources:
              limits:
                cpu: 5
              requests:
                cpu: 5
  2. Verifique o status dos pods em namespace1.

    kubectl get pods -n namespace1

    Saída esperada:

    NAME                     READY   STATUS    RESTARTS   AGE
    nginx1-744b889544-52dbg   1/1     Running   0          70s
    nginx1-744b889544-6l4s9   1/1     Running   0          70s
    nginx1-744b889544-cgzlr   1/1     Running   0          70s
    nginx1-744b889544-w2gr7   1/1     Running   0          70s
    nginx1-744b889544-zr5xz   0/1     Pending   0          70s

    root.a.1 (namespace1) tem min=10 CPU e max=20 CPU. Os 5 pods solicitam 25 vCPUs no total, excedendo max=20. Os primeiros 4 pods (20 vCPUs) são executados — 10 do min garantido e 10 emprestados da capacidade ociosa. O 5º pod permanece Pending porque as solicitações totais excedem max.

  3. Implante uma carga de trabalho em namespace2. Este Deployment solicita 5 réplicas, cada uma usando 5 vCPUs.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx2
      namespace: namespace2
      labels:
        app: nginx2
    spec:
      replicas: 5
      selector:
        matchLabels:
          app: nginx2
      template:
        metadata:
          name: nginx2
          labels:
            app: nginx2
        spec:
          containers:
          - name: nginx2
            image: nginx
            resources:
              limits:
                cpu: 5
              requests:
                cpu: 5
  4. Verifique o status dos pods em ambos os namespaces.

    kubectl get pods -n namespace1

    Saída esperada:

    NAME                     READY   STATUS    RESTARTS   AGE
    nginx1-744b889544-52dbg   1/1     Running   0          111s
    nginx1-744b889544-6l4s9   1/1     Running   0          111s
    nginx1-744b889544-cgzlr   1/1     Running   0          111s
    nginx1-744b889544-w2gr7   1/1     Running   0          111s
    nginx1-744b889544-zr5xz   0/1     Pending   0          111s
    kubectl get pods -n namespace2

    Saída esperada:

    NAME                     READY   STATUS    RESTARTS   AGE
    nginx2-556f95449f-4gl8s   1/1     Running   0          111s
    nginx2-556f95449f-crwk4   1/1     Running   0          111s
    nginx2-556f95449f-gg6q2   0/1     Pending   0          111s
    nginx2-556f95449f-pnz5k   1/1     Running   0          111s
    nginx2-556f95449f-vjpmq   1/1     Running   0          111s

    A mesma lógica de empréstimo se aplica a namespace2. root.a.2 tem min=10 e max=20, então 4 pods são executados e 1 permanece Pending. Agora, namespace1 e namespace2 juntos consomem todas as 40 vCPUs alocadas para root (root.max.cpu=40).

Devolver recursos emprestados

  1. Implante uma carga de trabalho em namespace3. Este Deployment solicita 5 réplicas, cada uma usando 5 vCPUs.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx3
      namespace: namespace3
      labels:
        app: nginx3
    spec:
      replicas: 5
      selector:
        matchLabels:
          app: nginx3
      template:
        metadata:
          name: nginx3
          labels:
            app: nginx3
        spec:
          containers:
          - name: nginx3
            image: nginx
            resources:
              limits:
                cpu: 5
              requests:
                cpu: 5
  2. Verifique o status dos pods nos três namespaces.

    kubectl get pods -n namespace1

    Saída esperada:

    NAME                      READY   STATUS    RESTARTS   AGE
    nginx1-744b889544-52dbg   1/1     Running   0          6m17s
    nginx1-744b889544-cgzlr   1/1     Running   0          6m17s
    nginx1-744b889544-nknns   0/1     Pending   0          3m45s
    nginx1-744b889544-w2gr7   1/1     Running   0          6m17s
    nginx1-744b889544-zr5xz   0/1     Pending   0          6m17s
    kubectl get pods -n namespace2

    Saída esperada:

    NAME                      READY   STATUS    RESTARTS   AGE
    nginx2-556f95449f-crwk4   1/1     Running   0          4m22s
    nginx2-556f95449f-ft42z   1/1     Running   0          4m22s
    nginx2-556f95449f-gg6q2   0/1     Pending   0          4m22s
    nginx2-556f95449f-hfr2g   1/1     Running   0          3m29s
    nginx2-556f95449f-pvgrl   0/1     Pending   0          3m29s
    kubectl get pods -n namespace3

    Saída esperada:

    NAME                     READY   STATUS    RESTARTS   AGE
    nginx3-578877666-msd7f   1/1     Running   0          4m
    nginx3-578877666-nfdwv   0/1     Pending   0          4m10s
    nginx3-578877666-psszr   0/1     Pending   0          4m11s
    nginx3-578877666-xfsss   1/1     Running   0          4m22s
    nginx3-578877666-xpl2p   0/1     Pending   0          4m10s

    root.b.1 (namespace3) tem um mínimo garantido de min=10 CPU. Para fornecer essa garantia, o agendador recupera 10 vCPUs que root.a havia emprestado de root.b. Ele seleciona pods para evicção sob root.a com base em fatores como prioridade do job, disponibilidade e horário de criação para liberar as 10 vCPUs. Como resultado, nginx3 obtém seu mínimo de 10 vCPUs: 2 pods são executados e 3 permanecem Pending.

  3. Implante uma carga de trabalho em namespace4. Este Deployment solicita 5 réplicas, cada uma usando 5 vCPUs.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx4
      namespace: namespace4
      labels:
        app: nginx4
    spec:
      replicas: 5
      selector:
        matchLabels:
          app: nginx4
      template:
        metadata:
          name: nginx4
          labels:
            app: nginx4
        spec:
          containers:
          - name: nginx4
            image: nginx
            resources:
              limits:
                cpu: 5
              requests:
                cpu: 5
  4. Verifique o status dos pods nos quatro namespaces.

    kubectl get pods -n namespace1

    Saída esperada:

    NAME                      READY   STATUS    RESTARTS   AGE
    nginx1-744b889544-cgzlr   1/1     Running   0          8m20s
    nginx1-744b889544-cwx8l   0/1     Pending   0          55s
    nginx1-744b889544-gjkx2   0/1     Pending   0          55s
    nginx1-744b889544-nknns   0/1     Pending   0          5m48s
    nginx1-744b889544-zr5xz   1/1     Running   0          8m20s
    kubectl get pods -n namespace2

    Saída esperada:

    NAME                      READY   STATUS    RESTARTS   AGE
    nginx2-556f95449f-cglpv   0/1     Pending   0          3m45s
    nginx2-556f95449f-crwk4   1/1     Running   0          9m31s
    nginx2-556f95449f-gg6q2   1/1     Running   0          9m31s
    nginx2-556f95449f-pvgrl   0/1     Pending   0          8m38s
    nginx2-556f95449f-zv8wn   0/1     Pending   0          3m45s
    kubectl get pods -n namespace3

    Saída esperada:

    NAME                     READY   STATUS    RESTARTS   AGE
    nginx3-578877666-msd7f   1/1     Running   0          8m46s
    nginx3-578877666-nfdwv   0/1     Pending   0          8m56s
    nginx3-578877666-psszr   0/1     Pending   0          8m57s
    nginx3-578877666-xfsss   1/1     Running   0          9m8s
    nginx3-578877666-xpl2p   0/1     Pending   0          8m56s
    kubectl get pods -n namespace4

    Saída esperada:

    NAME                      READY   STATUS    RESTARTS   AGE
    nginx4-754b767f45-g9954   1/1     Running   0          4m32s
    nginx4-754b767f45-j4v7v   0/1     Pending   0          4m32s
    nginx4-754b767f45-jk2t7   0/1     Pending   0          4m32s
    nginx4-754b767f45-nhzpf   0/1     Pending   0          4m32s
    nginx4-754b767f45-tv5jj   1/1     Running   0          4m32s

    A mesma lógica de recuperação se aplica a root.b.2 (namespace4): o agendador recupera 10 vCPUs emprestadas por root.a, e nginx4 obtém seu mínimo de 10 vCPUs — 2 pods são executados, 3 permanecem Pending. Todos os quatro nós de cota agora operam com seus recursos min garantidos, sem capacidade ociosa restante no cluster.

Configure ResourceFlavor para afinidade de nó

ResourceFlavor é uma CRD do Kueue que vincula um nó de cota a nós específicos correspondendo aos rótulos dos nós.

Pré-requisitos

Verifique se você tem:

Apenas o campo nodeLabels tem efeito no ResourceFlavor.

Crie um ResourceFlavor

Este exemplo cria um ResourceFlavor chamado spot que tem como alvo nós rotulados com instance-type: spot.

apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: "spot"
spec:
  nodeLabels:
    instance-type: spot

Associar um ResourceFlavor a uma cota elástica

Para vincular um ResourceFlavor a um nó de cota, declare-o na ElasticQuotaTree usando o campo attributes.resourceflavors.

apiVersion: scheduling.sigs.k8s.io/v1beta1
kind: ElasticQuotaTree
metadata:
  name: elasticquotatree
  namespace: kube-system
spec:
  root:
    name: root
    max:
      cpu: 999900
      memory: 400000Gi
      nvidia.com/gpu: 100000
    min:
      cpu: 999900
      memory: 400000Gi
      nvidia.com/gpu: 100000
    children:
    - name: child
      namespaces:
      - default
      attributes:
        resourceflavors: spot
      max:
        cpu: 99
        memory: 40Gi
        nvidia.com/gpu: 10
      min:
        cpu: 99
        memory: 40Gi
        nvidia.com/gpu: 10

Com essa configuração, os pods no nó de cota child (namespace default) são agendados apenas para nós com o rótulo instance-type: spot.

Próximas etapas