Implantar GPUs para cargas de trabalho em lote com o Dynamic Workload Scheduler


Nesta página, mostramos como otimizar a capacidade de acesso da GPU para cargas de trabalho em lote em grande escala com GPUs que usam o Dynamic Workload Scheduler e ProvisioningRequest.

Recomendamos o Dynamic Workload Scheduler para cargas de trabalho em lote de grande escala que podem ser executadas fora dos horários de pico com condições definidas de gerenciamento de capacidade da GPU. Essas cargas de trabalho podem ser de treinamento de modelo de aprendizado profundo ou uma simulação que precisa de grandes quantidades de GPUs com um modelo de provisionamento atômico, o que significa que todos os recursos são criados ao mesmo tempo.

Para executar cargas de trabalho de GPU no Google Kubernetes Engine (GKE) sem o programador de carga de trabalho dinâmico, consulte Executar GPUs em pools de nós padrão do GKE.

Quando usar o Dynamic Workload Scheduler

Recomendamos que você use o Programador de carga de trabalho dinâmico se as cargas de trabalho atenderem a todas as condições a seguir:

  • GPUs solicitadas para executar cargas de trabalho.
  • Sua capacidade de GPU é limitada ou não reservada e você quer melhorar a capacidade de acesso dos recursos da GPU.
  • Sua carga de trabalho é flexível por tempo e seu caso de uso pode esperar para ter toda a capacidade solicitada, por exemplo, quando o GKE aloca os recursos da GPU fora dos horários de maior movimento.
  • A carga de trabalho requer vários nós e não pode começar a ser executada até que todos os nós da GPU sejam provisionados e prontos ao mesmo tempo (por exemplo, treinamento distribuído de machine learning).

Antes de começar

Antes de começar, verifique se você realizou as tarefas a seguir:

  • Ativar a API Google Kubernetes Engine.
  • Ativar a API Google Kubernetes Engine
  • Se você quiser usar a Google Cloud CLI para essa tarefa, instale e, em seguida, inicialize a CLI gcloud. Se você instalou a CLI gcloud anteriormente, instale a versão mais recente executando gcloud components update.

Usar pools de nós com o Dynamic Workload Scheduler

É possível usar qualquer um dos três métodos a seguir para designar que o programador de carga de trabalho dinâmico pode funcionar com pools de nós específicos no cluster:

Criar um pool de nós

Crie um pool de nós com o Dynamic Workload Scheduler ativado usando a gcloud CLI:

gcloud beta container node-pools create NODEPOOL_NAME \
    --cluster=CLUSTER_NAME \
    --location=LOCATION \
     --enable-queued-provisioning \
    --accelerator type=GPU_TYPE,count=AMOUNT,gpu-driver-version=DRIVER_VERSION \
    --machine-type=MACHINE_TYPE \
    --enable-autoscaling  \
    --num-nodes=0   \
    --total-max-nodes TOTAL_MAX_NODES  \
    --location-policy=ANY  \
    --reservation-affinity=none  \
    --no-enable-autorepair

Substitua:

  • NODEPOOL_NAME: o nome escolhido para o pool de nós.
  • CLUSTER_NAME: o nome do cluster.
  • LOCATION: a região do Compute Engine do cluster, como us-central1.
  • GPU_TYPE: o tipo de GPU.
  • AMOUNT: o número de GPUs que serão anexadas aos nós do pool.
  • DRIVER_VERSION: a versão do driver NVIDIA a ser instalado. Será um dos seguintes valores:
    • default: instale a versão padrão do driver para a versão do GKE.
    • latest: instale a versão mais recente disponível do driver para a versão do GKE. Disponível apenas para nós que usam o Container-Optimized OS.
  • TOTAL_MAX_NODES: o número máximo de nós a serem escalonados automaticamente para todo o pool de nós.
  • MACHINE_TYPE: o tipo de máquina do Compute Engine para os nós. Recomendamos que você selecione um tipo de máquina otimizado para aceleradores.

Opcionalmente, é possível usar as seguintes flags:

  • --no-enable-autoupgrade: recomendado. Desativa os upgrades automáticos de nós. Com suporte apenas em clusters do GKE que não estão registrados em um canal de lançamento. Para saber mais, consulte Desativar upgrades automáticos de um pool de nós atual.
  • --node-locations=COMPUTE_ZONES: a lista separada por vírgulas de uma ou mais zonas em que o GKE cria os nós da GPU. As zonas precisam estar na mesma região que o cluster. Escolha zonas que tenham GPUs disponíveis.
  • --enable-gvnic: essa flag ativa a gVNIC nos pools de nós de GPU para aumentar a velocidade do tráfego de rede.

Esse comando cria um pool de nós com a seguinte configuração:

  • O GKE ativa o provisionamento de filas e o escalonamento automático de clusters.
  • Inicialmente, o pool de nós não tem nós.
  • A flag --enable-queued-provisioning ativa o Dynamic Workload Scheduler e adiciona o taint cloud.google.com/gke-queued ao pool de nós.
  • As flags --no-enable-autorepair e --no-enable-autoupgrade desativam o reparo e o upgrade automáticos de nós, o que pode interromper as cargas de trabalho em execução nos nós reparados ou que fizeram upgrade. Só é possível desativar o upgrade automático de nós em clusters que não estejam inscritos em um canal de lançamento.

Atualizar o pool de nós atual e ativar o Programador de carga de trabalho dinâmico

Ativar o Dynamic Workload Scheduler para um pool de nós. Revise os pré-requisitos para configurar o pool de nós corretamente.

Pré-requisitos

  • Crie um pool de nós com a flag --reservation-affinity=none. Essa flag é necessária para ativar o Programador de carga de trabalho dinâmico mais tarde, porque não é possível alterar a afinidade de reserva após a criação do pool de nós.

  • Mantenha pelo menos um pool de nós sem o processamento do Dynamic Workload Scheduler ativado para que o cluster funcione corretamente.

  • Verifique se o pool de nós está vazio. É possível redimensionar o pool de nós para zerar a quantidade de nós.

  • Verifique se o escalonamento automático está ativado e configurado corretamente.

  • Verifique se os reparos automáticos estão desativados.

Ativar o programador de carga de trabalho dinâmico para o pool de nós atual

É possível ativar o Dynamic Workload Scheduler para um pool de nós atual usando a gcloud:

gcloud beta container node-pools update NODEPOOL_NAME \
    --cluster=CLUSTER_NAME \
    --location=LOCATION \
     --enable-queued-provisioning

Substitua:

  • NODEPOOL_NAME: nome do pool de nós selecionado.
  • CLUSTER_NAME: nome do cluster.
  • LOCATION: a região do Compute Engine do cluster, como us-central1.

Esse comando de atualização do pool de nós resulta nas seguintes alterações de configuração:

  • A flag --enable-queued-provisioning ativa o Dynamic Workload Scheduler e adiciona o taint cloud.google.com/gke-queued ao pool de nós.

Se preferir, atualize as seguintes configurações do pool de nós:

  • Desativar upgrades automáticos de nós: recomendamos desativar os upgrades automáticos de nós, já que os upgrades do pool de nós não são compatíveis com o Programador de carga de trabalho dinâmico. Para desativar os upgrades automáticos de nós, verifique se o cluster do GKE não está inscrito em um canal de lançamento.
  • Ativar gVNIC nos pools de nós da GPU: a placa de rede virtual do Google (gVNIC, na sigla em inglês) aumenta a velocidade do tráfego de rede para os nós da GPU.

Ativar o provisionamento automático de nós para criar pools de nós para o Dynamic Workload Scheduler

É possível usar o provisionamento automático de nós para gerenciar pools de nós do Dynamic Workload Scheduler para clusters que executam a versão 1.29.2-gke.1553000 ou posterior. Quando você ativa o provisionamento automático de nós e ativa o Dynamic Workload Scheduler, o GKE cria pools de nós com os recursos necessários para a carga de trabalho associada.

Para ativar o provisionamento automático de nós, considere as seguintes configurações e conclua as etapas em Configurar limites de GPU:

Executar cargas de trabalho em lote com o Dynamic Workload Scheduler

Para usar o Dynamic Workload Scheduler, recomendamos que você use o Kueue. O Kueue implementa o enfileiramento de jobs para decidir quando eles devem ficar em espera e quando devem começar, com base em cotas e em uma hierarquia para compartilhar recursos de maneira uniforme entre as equipes. Isso simplifica a configuração necessária para usar VMs na fila.

É possível usar o Dynamic Workload Scheduler sem Kueue ao usar suas próprias ferramentas ou plataforma interna de programação em lote. Para configurar o Dynamic Workload Scheduler para jobs sem Kueue, consulte Dynamic Workload Scheduler para jobs sem Kueue.

Programador de carga de trabalho dinâmico para jobs com Kueue

As seções a seguir mostram como configurar o Dynamic Workload Scheduler para jobs com o Kueue. É possível definir as seguintes configurações comuns de pool de nós:

  • Configuração do pool de nós do Dynamic Workload Scheduler.
  • Configuração do pool de nós do Dynamic Workload Scheduler e da reserva.

Esta seção usa os exemplos no diretório dws-examples do repositório ai-on-gke. Publicamos os exemplos no diretório dws-examples sob a licença do Apache2.

Prepare o ambiente

  1. No Cloud Shell, execute este comando:

    git clone https://github.com/GoogleCloudPlatform/ai-on-gke
    cd ai-on-gke/tutorials-and-examples/workflow-orchestration/dws-examples
    
  2. Instale a versão mais recente do Kueue no cluster:

    VERSION=v0.7.0
    kubectl apply --server-side -f https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/manifests.yaml
    

Para saber mais sobre a instalação do Kueue, consulte Instalação.

Criar os recursos do Kueue para a configuração somente do pool de nós do Dynamic Workload Scheduler

Com o manifesto a seguir, crie uma fila no nível do cluster chamada dws-cluster-queue e o namespace LocalQueue chamado dws-local-queue. Os jobs que se referem à fila dws-cluster-queue nesse namespace usam o Dynamic Workload Scheduler para receber os recursos da GPU.

apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: "default-flavor"
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: AdmissionCheck
metadata:
  name: dws-prov
spec:
  controllerName: kueue.x-k8s.io/provisioning-request
  parameters:
    apiGroup: kueue.x-k8s.io
    kind: ProvisioningRequestConfig
    name: dws-config
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ProvisioningRequestConfig
metadata:
  name: dws-config
spec:
  provisioningClassName: queued-provisioning.gke.io
  managedResources:
  - nvidia.com/gpu
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "dws-cluster-queue"
spec:
  namespaceSelector: {} 
  resourceGroups:
  - coveredResources: ["cpu", "memory", "nvidia.com/gpu"]
    flavors:
    - name: "default-flavor"
      resources:
      - name: "cpu"
        nominalQuota: 10000  # Infinite quota.
      - name: "memory"
        nominalQuota: 10000Gi # Infinite quota.
      - name: "nvidia.com/gpu"
        nominalQuota: 10000  # Infinite quota.
  admissionChecks:
  - dws-prov
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: "default"
  name: "dws-local-queue"
spec:
  clusterQueue: "dws-cluster-queue"
---

Implante a LocalQueue:

kubectl create -f ./dws-queues.yaml

O resultado será assim:

resourceflavor.kueue.x-k8s.io/default-flavor created
admissioncheck.kueue.x-k8s.io/dws-prov created
provisioningrequestconfig.kueue.x-k8s.io/dws-config created
clusterqueue.kueue.x-k8s.io/dws-cluster-queue created
localqueue.kueue.x-k8s.io/dws-local-queue created

Se você quiser executar jobs que usam o Dynamic Workload Scheduler em outros namespaces, crie mais LocalQueues usando o modelo anterior.

Executar o job

No manifesto a seguir, o job de exemplo usa o Dynamic Workload Scheduler:

apiVersion: batch/v1
kind: Job
metadata:
  name: sample-job
  namespace: default
  labels:
    kueue.x-k8s.io/queue-name: dws-local-queue
  annotations:
    provreq.kueue.x-k8s.io/maxRunDurationSeconds: "600"
spec:
  parallelism: 1
  completions: 1
  suspend: true
  template:
    spec:
      nodeSelector:
        cloud.google.com/gke-nodepool: NODEPOOL_NAME
      tolerations:
      - key: "nvidia.com/gpu"
        operator: "Exists"
        effect: "NoSchedule"
      containers:
      - name: dummy-job
        image: gcr.io/k8s-staging-perf-tests/sleep:v0.0.3
        args: ["120s"]
        resources:
          requests:
            cpu: "100m"
            memory: "100Mi"
            nvidia.com/gpu: 1
          limits:
            cpu: "100m"
            memory: "100Mi"
            nvidia.com/gpu: 1
      restartPolicy: Never

Esse manifesto inclui os seguintes campos relevantes para a configuração do Dynamic Workload Scheduler:

  • O rótulo kueue.x-k8s.io/queue-name: dws-local-queue informa ao GKE que o Kueue é responsável por orquestrar esse job. Esse rótulo também define a fila em que o job está.
  • A flag suspend: true instrui o GKE a criar o recurso do job, mas ainda não a programar os pods. O Kueue muda essa flag para false quando os nós estão prontos para a execução do job.
  • nodeSelector instrui o GKE a programar o job apenas no pool de nós especificado. O valor precisa corresponder a NODEPOOL_NAME, o nome do pool de nós com provisionamento em fila ativado.
  1. Executar o job:

    kubectl create -f ./job.yaml
    

    O resultado será assim:

    job.batch/sample-job created
    
  2. Verifique o status do job:

    kubectl describe job sample-job
    

    O resultado será assim:

    Events:
      Type    Reason            Age    From                        Message
      ----    ------            ----   ----                        -------
      Normal  Suspended         5m17s  job-controller              Job suspended
      Normal  CreatedWorkload   5m17s  batch/job-kueue-controller  Created Workload: default/job-sample-job-7f173
      Normal  Started           3m27s  batch/job-kueue-controller  Admitted by clusterQueue dws-cluster-queue
      Normal  SuccessfulCreate  3m27s  job-controller              Created pod: sample-job-9qsfd
      Normal  Resumed           3m27s  job-controller              Job resumed
      Normal  Completed         12s    job-controller              Job completed
    

A integração do Dynamic Workload Scheduler com Kueue também é compatível com outros tipos de carga de trabalho disponíveis no ecossistema de código aberto, como:

  • RayJob
  • JobSet v0.5.2 ou mais recente
  • Kubeflow MPIJob, TFJob, PyTorchJob.
  • Pods do Kubernetes usados com frequência por orquestradores de fluxo de trabalho
  • Minicluster de fluxo

Para saber mais sobre esse suporte, consulte Usuário em lote do Kueue.

Criar os recursos do Kueue para a configuração do pool de nós do Dynamic Workload Scheduler e da reserva

Com o manifesto abaixo, você cria dois ResourceFlavors vinculados a dois pools de nós diferentes: reservation-nodepool e dws-nodepool. Os nomes desses pools de nós são apenas exemplos. Modifique esses nomes conforme a configuração do pool de nós. Além disso, com a configuração ClusterQueue, os jobs de entrada tentarão usar reservation-nodepool, e, se não houver capacidade, eles usarão o Dynamic Workload Scheduler para acessar os recursos da GPU.

apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: "reservation"
spec:
  nodeLabels:
    cloud.google.com/gke-nodepool: "reservation-nodepool" # placeholder value
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: "dws"
spec:
  nodeLabels:
    cloud.google.com/gke-nodepool: "dws-nodepool" # placeholder value
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "cluster-queue"
spec:
  namespaceSelector: {} # match all.
  resourceGroups:
  - coveredResources: ["cpu", "memory", "nvidia.com/gpu"]
    flavors:
    - name: "reservation" # first we try reservation
      resources:
      - name: "cpu"
        nominalQuota: 9
      - name: "memory"
        nominalQuota: 36Gi
      - name: "nvidia.com/gpu"
        nominalQuota: 9
    - name: "dws"         # if reservation is saturated we try dws
      resources:
      - name: "cpu"
        nominalQuota: 10000   # Infinite quota.
      - name: "memory"
        nominalQuota: 10000Gi # Infinite quota.
      - name: "nvidia.com/gpu"
        nominalQuota: 10000   # Infinite quota.
  admissionChecksStrategy:
    admissionChecks:
      - name: "dws-prov"
        onFlavors: [dws]
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: "default"
  name: "user-queue"
spec:
  clusterQueue: "cluster-queue"
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: AdmissionCheck
metadata:
  name: dws-prov
spec:
  controllerName: kueue.x-k8s.io/provisioning-request
  parameters:
    apiGroup: kueue.x-k8s.io
    kind: ProvisioningRequestConfig
    name: dws-config
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ProvisioningRequestConfig
metadata:
  name: dws-config
spec:
  provisioningClassName: queued-provisioning.gke.io
  managedResources:
  - nvidia.com/gpu

Implante o manifesto usando o seguinte comando:

kubectl create -f ./dws_and_reservation.yaml

O resultado será assim:

resourceflavor.kueue.x-k8s.io/reservation created
resourceflavor.kueue.x-k8s.io/dws created
clusterqueue.kueue.x-k8s.io/cluster-queue created
localqueue.kueue.x-k8s.io/user-queue created
admissioncheck.kueue.x-k8s.io/dws-prov created
provisioningrequestconfig.kueue.x-k8s.io/dws-config created

Executar o job

Ao contrário da configuração anterior, esse manifesto não contém o campo nodeSelector, já que ele é preenchido pelo Kueue, dependendo da capacidade livre no ClusterQueue.

apiVersion: batch/v1
kind: Job
metadata:
  generateName: sample-job-
  namespace: default
  labels:
    kueue.x-k8s.io/queue-name: user-queue
  annotations:
    provreq.kueue.x-k8s.io/maxRunDurationSeconds: "600"
spec:
  parallelism: 1
  completions: 1
  suspend: true
  template:
    spec:
      tolerations:
      - key: "nvidia.com/gpu"
        operator: "Exists"
        effect: "NoSchedule"
      containers:
      - name: dummy-job
        image: gcr.io/k8s-staging-perf-tests/sleep:v0.0.3
        args: ["120s"]
        resources:
          requests:
            cpu: "100m"
            memory: "100Mi"
            nvidia.com/gpu: 1
          limits:
            cpu: "100m"
            memory: "100Mi"
            nvidia.com/gpu: 1
      restartPolicy: Never
  1. Executar o job:

    kubectl create -f ./job-without-node-selector.yaml
    

    O resultado será assim:

    job.batch/sample-job-v8xwm created
    

Para descobrir o pool de nós usado pelo job, você precisa saber qual ResourceFlavor o job usa.

Solução de problemas

Para saber mais sobre a solução de problemas do Kueue, consulte Solução de problemas da solicitação de provisionamento no Kueue.

Programador de carga de trabalho dinâmico para jobs sem Kueue

Crie uma solicitação usando a API ProvisioningRequest para cada job. O programador de carga de trabalho dinâmico não inicia os pods, apenas provisiona os nós.

  1. Crie o seguinte manifesto provisioning-request.yaml:

    apiVersion: v1
    kind: PodTemplate
    metadata:
      name: POD_TEMPLATE_NAME
      namespace: NAMESPACE_NAME
      labels:
        cloud.google.com/apply-warden-policies: "true"
    template:
      spec:
        nodeSelector:
            cloud.google.com/gke-nodepool: NODEPOOL_NAME
        tolerations:
            - key: "nvidia.com/gpu"
              operator: "Exists"
              effect: "NoSchedule"
        containers:
            - name: pi
              image: perl
              command: ["/bin/sh"]
              resources:
                limits:
                  cpu: "700m"
                  nvidia.com/gpu: 1
                requests:
                  cpu: "700m"
                  nvidia.com/gpu: 1
        restartPolicy: Never
    ---
    apiVersion: autoscaling.x-k8s.io/v1beta1
    kind: ProvisioningRequest
    metadata:
      name: PROVISIONING_REQUEST_NAME
      namespace: NAMESPACE_NAME
    spec:
      provisioningClassName: queued-provisioning.gke.io
      parameters:
        maxRunDurationSeconds: "MAX_RUN_DURATION_SECONDS"
      podSets:
      - count: COUNT
        podTemplateRef:
          name: POD_TEMPLATE_NAME
    

    Substitua:

    • NAMESPACE_NAME: o nome do namespace do Kubernetes. O namespace precisa ser igual ao namespace dos pods.
    • PROVISIONING_REQUEST_NAME: o nome do ProvisioningRequest. Esse nome será usado na anotação do pod.
    • MAX_RUN_DURATION_SECONDS: opcionalmente, o tempo de execução máximo de um nó em segundos, até o padrão de sete dias. Para saber mais, consulte Como funciona o programador de cargas de trabalho dinâmicas. Não será possível alterar esse valor após a criação da solicitação. Este campo está disponível em Prévia no GKE versão 1.28.5-gke.1355000 ou posterior.
    • COUNT: número de pods solicitados. Os nós são programados atomicamente em uma zona.
    • POD_TEMPLATE_NAME: um nome padrão do Kubernetes. Referências do GKE a esse valor no PodSet de solicitações de provisionamento.
    • NODEPOOL_NAME: o nome escolhido para o pool de nós.
  1. Aplique o manifesto:

    kubectl apply -f provisioning-request.yaml
    

Configurar os pods

Na especificação de Job, vincule os pods ao ProvisioningRequest usando as seguintes anotações:

apiVersion: batch/v1
kind: Job
spec:
  template:
    metadata:
      annotations:
        cluster-autoscaler.kubernetes.io/consume-provisioning-request: PROVISIONING_REQUEST_NAME
        cluster-autoscaler.kubernetes.io/provisioning-class-name: "queued-provisioning.gke.io"
    spec:
      ...

A chave de anotação do pod cluster-autoscaler.kubernetes.io/consume-provisioning-request define qual ProvisioningRequest consumir. O GKE usa as anotações consume-provisioning-request e provisioning-class-name para fazer o seguinte:

  • programar os pods apenas nos nós provisionados pelo programador de cargas de trabalho dinâmicas
  • Para evitar a contagem dupla de solicitações de recursos entre pods e o programador de carga de trabalho dinâmico no escalonador automático de cluster.
  • Injetar a anotação safe-to-evict: false, para impedir que o escalonador automático de cluster mova pods entre nós e interrompa os cálculos em lote. É possível alterar esse comportamento especificando safe-to-evict: true nas anotações do pod.

Observar o status do Dynamic Workload Scheduler

O status de um programador de carga de trabalho dinâmico define se um pod pode ser programado ou não. Use relógios do Kubernetes para observar as alterações com eficiência ou outras ferramentas já usadas para rastrear status de objetos do Kubernetes. A tabela a seguir descreve o possível status de um programador de carga de trabalho dinâmico e cada resultado possível:

Status do programador de carga de trabalho dinâmico Descrição Possível resultado
Pendente A solicitação ainda não foi vista e processada. Após o processamento, a solicitação passa para o estado Accepted ou Failed.
Accepted=true A solicitação foi aceita e está aguardando a disponibilização dos recursos. A solicitação precisa passar para o estado Provisioned, se os recursos tiverem sido encontrados e os nós provisionados, ou para o estado Failed, se isso não tiver sido possível.
Provisioned=true Os nós estão prontos. Você tem 10 minutos para iniciar os pods para consumir recursos provisionados. Depois disso, o escalonador automático de cluster considera os nós como não necessários e os remove.
Failed=true Não é possível provisionar os nós devido a erros. Failed=true é um estado terminal. Solucione a condição com base nas informações nos campos Reason e Message da condição. Crie e tente de novo uma nova solicitação do programador de carga de trabalho dinâmica.
Provisioned=false Os nós ainda não foram provisionados.

Se for Reason=NotProvisioned, este é um estado temporário antes que todos os recursos estejam disponíveis.

Se Reason=QuotaExceeded, solucione o problema da condição com base nesse motivo e nas informações no campo Message da condição. Talvez seja necessário solicitar mais cota. Para mais detalhes, consulte a seção Verificar se o programador de cargas de trabalho dinâmicas está limitado pela cota. Este Reason só está disponível com o GKE versão 1.29.2-gke.1181000 ou mais recente.

Iniciar os pods

Quando a solicitação do Programador de carga de trabalho dinâmico atingir o status Provisioned=true, será possível executar o job para iniciar os pods. Isso evita a proliferação de pods não programáveis para solicitações pendentes ou com falha, o que pode afetar o desempenho do kube-scheduler e do escalonador automático de cluster.

Alternativamente, se você não se importa em ter pods não programáveis, é possível criar pods em paralelo com o Dynamic Workload Scheduler.

Cancelar solicitação do programador de carga de trabalho dinâmica

Para cancelar a solicitação antes que ela seja provisionado, exclua a ProvisioningRequest:

kubectl delete provreq PROVISIONING_REQUEST_NAME -n NAMESPACE

Na maioria dos casos, a exclusão de ProvisioningRequest interrompe a criação dos nós. No entanto, dependendo do tempo, por exemplo, se os nós já estiverem sendo provisionados, eles ainda poderão ser criados. Nesses casos, o escalonador automático de cluster removerá os nós após 10 minutos, se nenhum pod for criado.

Como funciona o programador de carga de trabalho dinâmica

Com o ProvisioningRequest API, o programador de carga de trabalho dinâmico faz o seguinte:

  1. Você informa ao GKE que a carga de trabalho pode aguardar um tempo indeterminado, até que todos os nós necessários estejam prontos para uso de uma só vez.
  2. O escalonador automático de clusters aceita sua solicitação e calcula o número de nós necessários, tratando-os como uma única unidade.
  3. A solicitação espera até que todos os recursos necessários estejam disponíveis em uma única zona. Para clusters que executam a versão 1.29.1-gke.1708000 e mais recentes, essa zona é escolhida usando informações sobre a capacidade disponível para garantir tempos de espera mais curtos. Para clusters que executam versões anteriores, a zona foi escolhida sem essas informações, o que pode resultar em filas nas zonas em que os tempos de espera são muito mais longos.
  4. O escalonador automático de clusters provisiona os nós necessários quando disponíveis, todos de uma vez.
  5. Todos os pods da carga de trabalho podem ser executados juntos em nós recém-provisionados.
  6. Os nós provisionados são limitados a sete dias de ambiente de execução ou antes, se você definir o parâmetro maxRunDurationSeconds para indicar que as cargas de trabalho precisam de menos tempo para serem executadas. Para saber mais, consulte Limitar o ambiente de execução de uma VM (prévia). Esse recurso está disponível na versão 1.28.5-gke.1355000 ou mais recente do GKE. Depois disso, os nós e os pods em execução neles passam por interrupção forçada. Se os pods forem concluídos antes e os nós não forem utilizados, o escalonador automático de clusters os removerá de acordo com o perfil de escalonamento automático.
  7. Os nós não são reutilizados entre o programador de cargas de trabalho dinâmicas. Cada ProvisioningRequest ordena a criação de novos nós com o novo tempo de execução de sete dias.

Cota

Todas as VMs provisionadas pelas solicitações do Dynamic Workload Scheduler usam cotas preemptivas.

O número de ProvisioningRequests com o estado Accepted é limitado por uma cota dedicada. Você configura a cota para cada projeto, uma configuração de cota por região.

Verificar a cota no console do Google Cloud

Para verificar o nome do limite de cota e o uso atual no console do Google Cloud, siga estas etapas:

  1. Acesse a página Cotas no console do Google Cloud.

    Acesse Cotas

  2. Na caixa Filtro, selecione a propriedade Métrica, digite active_resize_requests e pressione Enter.

O valor padrão é 100. Para aumentar a cota, siga as etapas listadas em Guia para solicitar um limite de cota maior.

Verificar se o programador de carga de trabalho dinâmico está limitado pela cota

Se a solicitação do Dynamic Workload Scheduler estiver demorando mais do que o esperado para ser atendida, verifique se a solicitação não está limitada pela cota. Talvez seja necessário solicitar mais cota.

Para clusters que executam a versão 1.29.2-gke.1181000 ou posterior, verifique se limitações específicas de cota estão impedindo que a solicitação seja atendida:

kubectl describe provreq PROVISIONING_REQUEST_NAME \
    --namespace NAMESPACE

A saída é semelhante a esta:

…
Last Transition Time:  2024-01-03T13:56:08Z
    Message:               Quota 'NVIDIA_P4_GPUS' exceeded. Limit: 1.0 in region europe-west4.
    Observed Generation:   1
    Reason:                QuotaExceeded
    Status:                False
    Type:                  Provisioned
…

Neste exemplo, o GKE não pode implantar nós porque não há cota suficiente na região de europe-west4.

Definir configurações de interrupção para pools de nós com cargas de trabalho usando o Dynamic Workload Scheduler

As cargas de trabalho que exigem a disponibilidade de todos os nós, ou da maioria dos nós, em um pool de nós são sensíveis a remoções. O reparo ou o upgrade automático de um nó provisionado usando a ProvisioningRequest API não é aceito porque essas operações eliminam todas as cargas de trabalho em execução nesse nó e as tornam não programáveis.

Para minimizar a interrupção das cargas de trabalho em execução usando o Dynamic Workload Scheduler, recomendamos as seguintes medidas:

  • Dependendo do registro de canal de lançamento do cluster, use as seguintes práticas recomendadas para evitar que os upgrades automáticos de nós interrompam as cargas de trabalho:
  • Desative o reparo automático de nós.
  • Use janelas de manutenção e exclusões para minimizar a interrupção das cargas de trabalho em execução, garantindo que haja um período em que o GKE possa interromper o pool de nós para manutenção automática. Se você usar essas ferramentas de manutenção, precisará definir um período específico em que o GKE possa interromper o pool de nós. Portanto, recomendamos definir essa janela quando não houver cargas de trabalho em execução.
  • Para garantir que o pool de nós permaneça atualizado, recomendamos que você faça o upgrade manual do pool de nós quando não houver solicitações Dynamic Workload Scheduler ativas e o pool de nós estiver vazio.

Limitações

  • A antiafinidade entre pods não tem suporte. O escalonador automático de cluster não considera regras de antiafinidade entre pods durante o provisionamento de nós, o que pode levar a cargas de trabalho não programáveis. Isso pode acontecer quando os nós de dois ou mais objetos do Programador de carga de trabalho dinâmica foram provisionados no mesmo pool de nós.
  • Somente os nós da GPU são compatíveis.
  • Não há suporte para reservas com o Programador de carga de trabalho dinâmico. É preciso especificar --reservation-affinity=none ao criar o pool de nós. O programador de carga de trabalho dinâmico requer e oferece suporte apenas à política de local ANY para o escalonamento automático de clusters.
  • Uma única solicitação do programador de carga de trabalho dinâmica pode criar até 1.000 VMs, que é o número máximo de nós por zona para um único pool de nós.
  • O GKE usa a cota ACTIVE_RESIZE_REQUESTS do Compute Engine para controlar o número de solicitações pendentes do programador de cargas de trabalho dinâmicas em uma fila. Por padrão, essa cota tem um limite de 100 em um nível de projeto do Google Cloud. Se você tentar criar uma solicitação do programador de carga de trabalho dinâmica maior que essa cota, a nova solicitação falhará.
  • Os pools de nós que usam o Dynamic Workload Scheduler são sensíveis a interrupções, já que os nós são provisionados juntos. Para saber mais, consulte Definir configurações de interrupção para pools de nós com cargas de trabalho usando o Dynamic Workload Scheduler.
  • Talvez você veja outras VMs de curta duração listadas no console do Google Cloud. Esse comportamento ocorre porque o Compute Engine pode criar e remover VMs imediatamente até que a capacidade de provisionar todas as máquinas necessárias esteja disponível.
  • A integração do Dynamic Workload Scheduler aceita apenas um PodSet. Se você quiser misturar diferentes modelos de pod, use aquele com mais recursos solicitados. Não é possível misturar tipos de máquina diferentes, como VMs com tipos de GPU distintos.

A seguir