Melhores práticas de MLOps (operações de aprendizado de máquina) no AKS (Serviço de Kubernetes do Azure)

Aplica-se a: ✔️ AKS Automatic ✔️ AKS Standard

O MLOps é um conjunto de práticas para implantar, monitorar e gerenciar modelos de machine learning em ambientes de produção.

Principais práticas recomendadas do MLOps abordadas neste artigo:

  • IaC (Infraestrutura como Código)
  • Transporte em contêineres
  • Gerenciamento de modelos e controle de versão
  • Automação
  • Escalabilidade e gerenciamento de recursos
  • Confiabilidade de trabalho em lotes de execução longa
  • Segurança e conformidade

Este artigo descreve as melhores práticas e as considerações que devem ser levadas em conta ao usar MLOps no AKS. Para obter mais informações sobre MLOps, confira MLOps (operações de aprendizado de máquina) para fluxos de trabalho de IA e aprendizado de máquina.

Escolha seu modo AKS para MLOps

O AKS dá suporte a dois modos de cluster: AKS Automatic e AKS Standard. Escolha AKS Automatic quando quiser uma base pronta para produção com menos necessidade de gerenciamento da plataforma no dia 2. Escolha o AKS Standard quando precisar de um controle mais profundo sobre a infraestrutura de cluster e a configuração da plataforma.

As práticas de MLOps abordadas neste artigo aplicam-se a ambos os modos. No entanto, a responsabilidade de implementação difere por modo: o AKS Automatic fornece padrões mais pré-configurados, enquanto o AKS Standard normalmente requer configuração de plataforma mais explícita e propriedade do ciclo de vida.

Area AKS Automático AKS Standard
Configuração inicial do cluster Pools de nós pré-configurados, rede e monitoramento prontos para uso Requer configuração manual de pools de nós, CNI e pilha de observabilidade
Operações do conjunto de nós do sistema Provisionamento automático de nós e dimensionamento gerenciado pelo serviço O operador deve configurar o dimensionamento do pool de nós, regras de dimensionamento e janelas de manutenção
Controles de referência de segurança Políticas de rede, padrões de segurança de pod e identidade de carga de trabalho ativados por padrão Os operadores devem habilitar e definir explicitamente as políticas de rede, a segurança do pod e as configurações de identidade
Linha de base de rede Azure CNI Overlay pré-configurado com padrões de ingresso padrão Flexibilidade total para escolher o plug-in CNI, configurar controladores de entrada personalizados e definir a topologia de rede
Operações e atualizações Atualizações de imagem de nó automáticas e de versão do Kubernetes Operadores agendam e gerenciam o tempo de atualização e a estratégia de distribuição
Foco de implementação do MLOps Validar, governar e ajustar configurações padrão Projetar e configurar controles de plataforma
Capacidade e agendamento de GPU A capacidade de GPU qualificada pode ser provisionada automaticamente com base em solicitações de recursos da carga de trabalho e em restrições de agendamento, de acordo com a disponibilidade do SKU regional e da cota da assinatura Os operadores criam e gerenciam pools de nós de GPU, rótulos, taints, limites de dimensionador automático, drivers e configuração de plug-in de dispositivo
Treinamento distribuído O agendamento padrão do Kubernetes e o provisionamento automático de nós fornecem um ponto de partida; operadores de treinamento, agendadores de gangues e ajuste de topologia permanecem opções no nível da carga de trabalho Os operadores instalam explicitamente operadores de treinamento e agendadores, e definem pools de nós com GPU, configuração de rede, escalonamento e posicionamento para cargas de trabalho distribuídas

IaC (infraestrutura como código)

Principal conclusão: defina e versione modelos de IaC para cada estágio do seu pipeline de IA para garantir consistência, eficiência de custos e implantações mais rápidas.

A IaC habilita o provisionamento e o gerenciamento de infraestrutura consistentes e reproduzíveis para uma variedade de tipos de aplicativos. Com diferentes implantações de aplicativos, sua implementação de IaC pode mudar em todo o pipeline de IA, pois o poder de computação e os recursos necessários para inferência, serviço, treinamento e modelos de ajuste fino podem variar. Definir e versionar templates de IaC para suas equipes de desenvolvimento de IA pode ajudar a garantir consistência e eficiência de custos em diferentes tipos de cargas de trabalho, além de esclarecer os requisitos de hardware e acelerar o processo de implantação.

No AKS Automatic, a IaC pode se concentrar mais nas definições de carga de trabalho, nos verificadores de integridade de política e na consistência do ambiente em relação aos padrões da plataforma. No AKS Standard, a IaC geralmente inclui configurações de plataforma de cluster mais explícitas, como opções de rede, dimensionamento e configuração operacional.

Transporte em contêineres

Principal conclusão: empacote os pesos do modelo, metadados e configurações em imagens de contêiner para permitir a portabilidade, o versionamento simplificado e a redução de custos de armazenamento.

O gerenciamento de pesos, metadados e configurações do modelo em imagens de contêineres permite a portabilidade, o controle de versão simplificado e a redução dos custos de armazenamento ao longo do tempo. Com a conteinerização, você pode:

  • Use imagens de contêiner existentes, especialmente para LLMs (grandes modelos de linguagem) que variam em milhões a bilhões de parâmetros em tamanho e modelos de difusão estáveis, armazenados em registros de contêiner seguros.
  • Evite um único ponto de falha em seu pipeline usando vários contêineres leves que contêm as dependências exclusivas para cada tarefa em vez de manter uma imagem grande.
  • Armazene grandes conjuntos de dados de texto e imagem fora da imagem de contêiner base e faça referência a eles quando necessário no runtime. Comece a usar o KAITO (Operador de Cadeia de Ferramentas de IA do Kubernetes) para implantar uma LLM no AKS.

Os controles da cadeia de suprimentos de contêiner permanecem essenciais em ambos os modos. Mesmo com os padrões predefinidos da plataforma no AKS Automatic, a proveniência da imagem, a varredura e o reforço de segurança em runtime continuam sendo responsabilidades centrais de MLOps.

Padrão do Dockerfile para cargas de trabalho de ML:

Use uma tag de imagem base explícita, com suporte a CUDA, para cargas de trabalho de treinamento em GPU (por exemplo, pytorch/pytorch:2.4.1-cuda12.4-cudnn9-runtime) em vez de uma referência a uma imagem base sem tag. Fixe a imagem pelo resumo em produção para garantir compilações reproduzíveis e comportamento previsível do CUDA/cuDNN. Mantenha variantes de imagem de CPU e GPU separadas para que o posicionamento do agendador e as dependências de runtime permaneçam explícitos.

Gerenciamento de modelos e controle de versão

Principal conclusão: controle a versão de modelos sistematicamente para manter a consistência entre ambientes e permitir iterações mais rápidas com métodos de ajuste fino com eficiência de parâmetros.

O gerenciamento de modelos e o controle de versão são essenciais para acompanhar as alterações em seus modelos ao longo do tempo. Ao fazer o controle de versão de seus modelos, você pode:

  • Manter a consistência em seus contêineres de modelo para facilitar a implantação em ambientes diferentes.
  • Use métodos de PEFT (ajuste fino eficiente em termos de parâmetros) para iterar mais rapidamente sobre um subconjunto dos pesos do modelo e manter novas versões em contêineres leves.

No AKS Automatic, as linhas de base de plataforma pré-configuradas podem simplificar a paridade do ambiente. No AKS Standard, as equipes geralmente precisam impor a paridade mais explicitamente por meio da configuração de plataforma e implantação.

Automação

Principal conclusão: automatize a ingestão de dados, o monitoramento do desempenho do modelo e os pipelines de retreinamento para reduzir erros manuais e garantir consistência ao longo de todo o ciclo de vida do ML.

A automação ajuda a reduzir erros manuais, aumentar a eficiência e garantir a consistência em todo o ciclo de vida de ML. Ao automatizar tarefas, você pode:

  • Integre ferramentas de alerta para disparar um fluxo de ingestão de vetor à medida que novos dados fluem para seu aplicativo.
  • Defina limiares de desempenho do modelo para monitorar degradações e disparar pipelines de retreinamento.

Em ambos os modos do AKS, inclua automação para validação de políticas, detecção de descompasso de configuração e governança de liberações, além de gatilhos de qualidade do modelo.

Escalabilidade e gerenciamento de recursos

Principal conclusão: otimize o uso de recursos por meio de computação distribuída, dimensionamento automático e planejamento de recuperação de desastres para lidar, de maneira econômica, com as demandas variáveis dos pipelines de IA.

A escalabilidade e o gerenciamento de recursos são essenciais para garantir que o pipeline de IA possa lidar com as demandas do aplicativo. Ao otimizar o uso do recurso, você pode:

  • Integre ferramentas que usam com eficiência seus recursos de CPU, GPU e memória alocados por meio de computação distribuída e vários níveis de paralelismo, como paralelismo de dados, modelo e pipeline.
  • Habilitar a escala automática em seus recursos de computação para dar suporte a altos volumes de solicitações de modelos em horários de pico e reduzir a escala em horários fora do pico.
  • Planeje a recuperação de desastre seguindo as práticas recomendadas de resiliência e confiabilidade do AKS.

O AKS Automatic pode reduzir a sobrecarga de instalação para padrões comuns de dimensionamento e operações, enquanto o AKS Standard fornece um controle mais profundo para arquiteturas de dimensionamento personalizadas.

Executar tarefas em lote de execução longa

Principal conclusão: execute uma carga de trabalho finita como um trabalho do Kubernetes Job, persista o progresso fora do pod, trate os sinais de terminação e suponha que o trabalho possa ser iniciado mais de uma vez.

Atualizações de nó, reinicializações, eventos de redução de escala, pressão de recursos, preempção e falhas de infraestrutura podem interromper tarefas em lote de longa duração. Um Job Kubernetes substitui um pod com falha ou excluído, mas a substituição é iniciada em outro nó sem os arquivos locais do pod anterior ou a memória do processo. Projete o aplicativo para retomar a partir de um estado persistente e repetir o trabalho com segurança.

Use essas práticas para tarefas em lote comuns com uso intensivo de CPU, memória ou E/S. As diretrizes de agendamento de gangues e fila de carga de trabalho se aplicam quando uma carga de trabalho distribuída exige que vários trabalhadores iniciem juntos ou quando as equipes precisam de cotas compartilhadas. Um trabalhador em lote com um único trabalho ou paralelizado de forma independente normalmente não precisa de escalonamento em grupo.

Configurar pontos de verificação, repetições e término

O exemplo a seguir processa uma sequência de itens de trabalho e registra o próximo item em um volume persistente Arquivos do Azure. Ele restaura o ponto de verificação após uma substituição de pod e salva o progresso quando o processo recebe SIGTERM:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: batch-checkpoints
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: azurefile-csi
  resources:
    requests:
      storage: 10Gi
---
apiVersion: batch/v1
kind: Job
metadata:
  name: checkpointed-batch
spec:
  backoffLimit: 6
  activeDeadlineSeconds: 86400
  ttlSecondsAfterFinished: 86400
  podFailurePolicy:
    rules:
    - action: Ignore
      onPodConditions:
      - type: DisruptionTarget
  template:
    metadata:
      labels:
        app: checkpointed-batch
    spec:
      restartPolicy: Never
      terminationGracePeriodSeconds: 120
      containers:
      - name: worker
        image: ubuntu:24.04
        command:
        - /bin/bash
        - -c
        - |
          set -euo pipefail

          CHECKPOINT=/checkpoints/next-item
          NEXT_ITEM=1

          if [[ -f "${CHECKPOINT}" ]]; then
            NEXT_ITEM="$(cat "${CHECKPOINT}")"
            echo "Resuming at item ${NEXT_ITEM}."
          fi

          save_checkpoint() {
            printf '%s\n' "${NEXT_ITEM}" > "${CHECKPOINT}.tmp"
            mv "${CHECKPOINT}.tmp" "${CHECKPOINT}"
            echo "Saved checkpoint for item ${NEXT_ITEM}."
          }

          terminate() {
            echo "Received a termination signal."
            save_checkpoint
            exit 143
          }

          trap terminate TERM INT

          while (( NEXT_ITEM <= 1000 )); do
            echo "Processing item ${NEXT_ITEM}."
            sleep 10
            NEXT_ITEM=$((NEXT_ITEM + 1))
            save_checkpoint
          done

          echo "Batch completed."
        resources:
          requests:
            cpu: "1"
            memory: 1Gi
          limits:
            cpu: "1"
            memory: 1Gi
        volumeMounts:
        - name: checkpoints
          mountPath: /checkpoints
      volumes:
      - name: checkpoints
        persistentVolumeClaim:
          claimName: batch-checkpoints

Substitua o loop de exemplo pela sua aplicação em lote e selecione o armazenamento que atenda aos seus requisitos de taxa de transferência e recuperação. Use a identidade da carga de trabalho em vez de credenciais inseridas quando o aplicativo faz pontos de verificação diretamente para Armazenamento de Blobs do Azure ou outro serviço do Azure.

Aplique estes controles de confiabilidade deliberadamente:

  • Pontos de verificação e idempotência: salve o progresso em intervalos que atendam ao seu objetivo de ponto de recuperação. Armazene os pontos de verificação e a saída com commit em armazenamento persistente, não no sistema de arquivos do contêiner, emptyDir, nem no armazenamento local do nó. Use escritas atômicas, chaves de idempotência, saída transacional ou deduplicação, porque o mesmo programa Trabalho pode às vezes ser iniciado mais de uma vez.
  • Comportamento de reinicialização: um Job permite restartPolicy: Never ou OnFailure. Com Never, um contêiner com falha produz um pod com falha, e o controlador de trabalho cria um substituto. Com OnFailure, o kubelet pode reiniciar o contêiner no mesmo pod. Use Never quando pods e logs com falha separados facilitam o diagnóstico e tornam qualquer caminho seguro para retomar.
  • Limites de repetição: definido backoffLimit com base no número de falhas transitórias que a carga de trabalho pode tolerar. Use podFailurePolicy para falhar imediatamente em relação a códigos de saída conhecidos sem repetição ou, como no exemplo, impedir que interrupções voluntárias consumam o orçamento de repetição. Uma interrupção ainda pode parar o pod; a política só afeta a forma como o trabalho contabiliza essa falha.
  • Prazos: definir activeDeadlineSeconds quando o Trabalho deve parar após um runtime total máximo. O prazo inclui repetições e tem precedência sobre backoffLimit. Não defina um prazo menor do que o tempo de execução esperado mais tempo de repetição e recuperação. Um trabalho com falha não é reiniciado automaticamente como um novo trabalho.
  • Término normal: trate SIGTERM no aplicativo e defina terminationGracePeriodSeconds com tempo suficiente para parar de aceitar novos trabalhos, concluir ou abandonar a unidade atual com segurança, liberar a saída e salvar um ponto de verificação. O Kubernetes envia SIGKILL se o processo exceder o período de carência, portanto, os pontos de verificação periódicos permanecem necessários.
  • Limpeza: defina ttlSecondsAfterFinished ou configure os limites de histórico do CronJob para impedir que trabalhos e pods concluídos se acumulem. Mantenha os registros de Job por tempo suficiente para a coleta de logs e a investigação de incidentes.

Planejar para remoções e manutenção de nós

Não trate o bloqueio de remoção como a principal estratégia de recuperação para uma tarefa de execução longa. Para um trabalho em lote reinicializável, normalmente não se cria um PodDisruptionBudget (PDB); o controlador do trabalho cria um pod de substituição após uma interrupção voluntária. Um PDB protege somente contra remoções voluntárias e não protege contra falhas de nó, remoção por pressão de recursos ou preempção. Um PDB que não permite nenhuma interrupção também pode bloquear a drenagem de nós e atrasar atualizações do AKS.

Use um PDB somente quando uma carga de trabalho em lote paralela deve manter um número mínimo de trabalhadores em execução simultânea e você testou seu comportamento de drenagem. Da mesma forma, use cluster-autoscaler.kubernetes.io/safe-to-evict: "false" apenas para tarefas excepcionais que não podem ser reiniciadas. A anotação pode impedir a redução da escala e aumentar o custo, mas não protege contra falha de nó nem contra todos os eventos de manutenção.

O AKS atualiza o isolamento e drena os nós antigos antes de substituí-los. Configure janelas de manutenção planejada para alinhar a manutenção com suporte do AKS a períodos de menor impacto, mas mantenha a carga de trabalho reiniciável, porque o agendamento da manutenção não elimina interrupções não planejadas. Para o AKS Standard, execute trabalhos em lotes em um pool de nós de usuário dedicado quando precisar de tamanhos de VM separados, limites de dimensionamento, taints ou tempo de atualização. Evite pools de nós Spot para trabalhos que não possam se recuperar de preempções. Antes da manutenção de nós, certifique-se de que os pontos de verificação recentes sejam utilizáveis e que outro pool de nós qualificado tenha cota e capacidade suficientes para executar pods de substituição.

Monitorar o progresso e a recuperação do trabalho

Use o status, os eventos e os logs do Kubernetes juntos ao investigar um trabalho de longa execução:

kubectl get job checkpointed-batch --watch
kubectl describe job checkpointed-batch
kubectl get pods -l batch.kubernetes.io/job-name=checkpointed-batch
kubectl logs job/checkpointed-batch

Habilite o serviço gerenciado do Azure Monitor para Prometheus e Container Insights para reter logs e correlacionar o status do trabalho com reinicializações de pods, remoções, tempo em estado pendente, integridade do nó e utilização de recursos. Instrumente o aplicativo com sinais de progresso de negócios, como itens concluídos, itens com falha, hora do último ponto de verificação bem-sucedido, taxa de processamento, contagem de repetições e tempo de conclusão estimado. Alerta em um trabalho com falha, um trabalho que excede a duração esperada, nenhum ponto de verificação dentro do objetivo do ponto de recuperação, substituições repetidas de pod, pods pendentes sustentados e taxa de transferência de processamento paralisada.

Segurança e conformidade

Principal conclusão: implemente varredura de CVEs, trilhas de auditoria e controles de conformidade para proteger seus dados e atender aos requisitos regulatórios, como SOC 2, HIPAA e GDPR.

A segurança e a conformidade são essenciais para proteger seus dados e garantir que o pipeline de IA atenda aos requisitos regulatórios. Ao implementar as melhores práticas de segurança e conformidade, você pode:

  • Integre a verificação de CVEs (Vulnerabilidades e Exposições Comuns) para detectar vulnerabilidades comuns em imagens de contêiner de modelo de código aberto.
  • Use o Microsoft Defender para Contêineres para imagens de contêiner do modelo armazenadas no Registro de Contêiner do Azure.
  • Mantenha uma trilha de auditoria dos dados ingeridos, alterações de modelo e métricas para permanecer em conformidade com suas políticas organizacionais.
  • Suporte a estruturas de conformidade, como SOC 2 (por meio de controles de log e acesso de auditoria da Azure), HIPAA (por meio de criptografia em repouso e em trânsito, isolamento de rede) e RGPD (por meio de opções de residência de dados e políticas de gerenciamento de acesso).

No AKS Automatic, os padrões de segurança pré-configurados melhoram a postura de linha de base, mas os controles de segurança no nível do modelo, no nível de dados e no nível do pipeline permanecem necessários.

Agendar cargas de trabalho de GPU

Principal conclusão: exponha GPUs por meio do plug-in de dispositivo da NVIDIA, solicite GPUs de forma explícita e isole nós de GPU de alto custo com rótulos, taints e tolerations.

O Kubernetes programa GPUs como recursos estendidos. Depois que o plug-in do dispositivo NVIDIA registra as GPUs em um nó, um pod solicita uma GPU definindo nvidia.com/gpu em resources.requests e resources.limits. O pod a seguir solicita uma GPU e visa um pool de nós com o rótulo accelerator=nvidia:

apiVersion: v1
kind: Pod
metadata:
  name: gpu-training-pod
  labels:
    app: gpu-training
spec:
  restartPolicy: Never
  nodeSelector:
    accelerator: nvidia
  tolerations:
  - key: sku
    operator: Equal
    value: gpu
    effect: NoSchedule
  containers:
  - name: trainer
    image: nvcr.io/nvidia/cuda:12.4.1-runtime-ubuntu22.04
    command:
    - /bin/bash
    - -c
    - |
      set -e
      nvidia-smi
      echo "GPU is available to the training container."
    resources:
      requests:
        cpu: "1"
        memory: 2Gi
        nvidia.com/gpu: 1
      limits:
        cpu: "1"
        memory: 2Gi
        nvidia.com/gpu: 1

Os recursos estendidos não estão superalocados. O Kubernetes trata um limite de GPU como uma solicitação de GPU quando apenas o limite é especificado, mas definir ambos os campos torna a intenção de carga de trabalho explícita e ajuda na validação da política.

Criar um pool de nós de GPU no AKS

No AKS Standard, crie um pool dedicado de nós de usuário com um tamanho de VM com GPU NVIDIA compatível. O seguinte exemplo da CLI do Azure cria um pool de nós dimensionado Standard_NC4as_T4_v3 automaticamente, aplica o rótulo usado pelo pod anterior e usa taints dos nós para repelir cargas de trabalho que não toleram explicitamente nós de GPU:

RESOURCE_GROUP=myResourceGroup
AKS_CLUSTER=myAKSCluster

az aks nodepool add \
  --resource-group "$RESOURCE_GROUP" \
  --cluster-name "$AKS_CLUSTER" \
  --name gpunp \
  --mode User \
  --node-vm-size Standard_NC4as_T4_v3 \
  --node-count 0 \
  --enable-cluster-autoscaler \
  --min-count 0 \
  --max-count 4 \
  --labels accelerator=nvidia workload=training \
  --node-taints sku=gpu:NoSchedule

Antes de criar o pool de nós, verifique se o SKU da VM está disponível na região do cluster e se a assinatura tem cota regional suficiente e cota suficiente para a família de VMs. Selecione um tamanho da série NC com base na memória da GPU, na quantidade de GPUs, na proporção entre CPU e GPU, no armazenamento local, na capacidade de rede e nos recursos de CUDA exigidos pelo framework de treinamento. Por exemplo, os tamanhos do NCas T4 v3 são adequados para muitas cargas de trabalho de treinamento de GPU única e menores, enquanto os tamanhos NC A100 v4 dão suporte a modelos maiores e configurações de GPU de várias instâncias.

O AKS Automático pode provisionar capacidade de GPU qualificada em resposta a pods pendentes. A carga de trabalho ainda deve solicitar nvidia.com/gpu, e todos os seletores de nó, afinidades, tolerâncias, restrições de topologia, cotas de assinatura e disponibilidade de SKU regional devem ser satisfatórias. Use o AKS Standard quando você precisar de um SKU de VM fixo, ciclo de vida do pool de nós personalizado ou controle detalhado sobre o dimensionamento e a topologia do pool de GPU.

Verificar ou instalar o plug-in do dispositivo NVIDIA

As configurações de GPU do AKS podem oferecer drivers de GPU gerenciados e integração com o plug-in de dispositivo. Verifique a configuração efetiva antes de instalar outro plug-in:

kubectl get nodes -L accelerator,kubernetes.azure.com/agentpool
kubectl get daemonsets --all-namespaces | grep -i nvidia
kubectl describe node | grep -A5 -E "Capacity:|Allocatable:|nvidia.com/gpu"

Não execute vários DaemonSets do plug-in de dispositivo da NVIDIA nos mesmos nós. Se a configuração do AKS não gerenciar o plugin, o DaemonSet autônomo a seguir registrará as GPUs NVIDIA no kubelet. Instale uma versão do plug-in de dispositivo compatível com o driver NVIDIA e as versões do Kubernetes:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nvidia-device-plugin
  namespace: kube-system
  labels:
    app.kubernetes.io/name: nvidia-device-plugin
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: nvidia-device-plugin
  updateStrategy:
    type: RollingUpdate
  template:
    metadata:
      labels:
        app.kubernetes.io/name: nvidia-device-plugin
    spec:
      priorityClassName: system-node-critical
      nodeSelector:
        accelerator: nvidia
      tolerations:
      - operator: Exists
      containers:
      - name: nvidia-device-plugin
        image: nvcr.io/nvidia/k8s-device-plugin:v0.17.1
        args:
        - --fail-on-init-error=false
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
        volumeMounts:
        - name: device-plugin
          mountPath: /var/lib/kubelet/device-plugins
      volumes:
      - name: device-plugin
        hostPath:
          path: /var/lib/kubelet/device-plugins
          type: Directory

Confirme se nvidia.com/gpu aparece nos recursos alocáveis de cada nó de GPU antes de enviar os trabalhos de treinamento.

Partição de GPUs A100 e H100 com MIG

A GPU de Várias Instâncias (MIG) da NVIDIA pode particionar uma GPU A100 ou H100 com suporte em instâncias de GPU isoladas. O MIG é útil para ajuste de hiperparâmetro e trabalhos de ajuste fino menores que não exigem uma GPU física inteira. Use GPUs completas para cargas de trabalho que exijam toda a memória de GPU, largura de banda de interconexão máxima ou um perfil que não é compatível com a GPU instalada.

Use o Operador de GPU NVIDIA e o Gerenciador de MIG quando precisar de gerenciamento declarativo do ciclo de vida do MIG. O operador deve ser o responsável pelo plugin de dispositivo e pela configuração de MIG dos nós afetados; não o combine com um segundo plugin de dispositivo independente. Os nomes de perfil exatos e o número de instâncias dependem do modelo de GPU e da capacidade de memória. A configuração a seguir cria sete 1g.10gb instâncias em configurações A100 ou H100 de 80 GB com suporte:

apiVersion: v1
kind: Namespace
metadata:
  name: gpu-operator
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: mig-parted-config
  namespace: gpu-operator
data:
  config.yaml: |
    version: v1
    mig-configs:
      all-disabled:
      - devices: all
        mig-enabled: false
      hptuning-1g10gb:
      - devices: all
        mig-enabled: true
        mig-devices:
          "1g.10gb": 7
---
apiVersion: v1
kind: Namespace
metadata:
  name: ml-training
---
apiVersion: batch/v1
kind: Job
metadata:
  name: mig-hyperparameter-trial
  namespace: ml-training
  labels:
    workload: hyperparameter-tuning
spec:
  backoffLimit: 2
  template:
    metadata:
      labels:
        workload: hyperparameter-tuning
    spec:
      restartPolicy: Never
      nodeSelector:
        accelerator: nvidia
        nvidia.com/mig.config: hptuning-1g10gb
        nvidia.com/mig.config.state: success
      tolerations:
      - key: sku
        operator: Equal
        value: gpu
        effect: NoSchedule
      containers:
      - name: trial
        image: nvcr.io/nvidia/cuda:12.4.1-runtime-ubuntu22.04
        command:
        - /bin/bash
        - -c
        - |
          set -e
          nvidia-smi
          echo "Starting one hyperparameter trial on a MIG instance."
          sleep 30
        resources:
          requests:
            cpu: "2"
            memory: 8Gi
            nvidia.com/mig-1g.10gb: 1
          limits:
            cpu: "2"
            memory: 8Gi
            nvidia.com/mig-1g.10gb: 1

Configure o MIG Manager do Operador de GPU para consumir o ConfigMap mig-parted-config, usar a estratégia MIG mixed quando as cargas de trabalho solicitarem perfis MIG nomeados e, depois, rotular os nós de destino:

kubectl label nodes \
  -l accelerator=nvidia \
  nvidia.com/mig.config=hptuning-1g10gb \
  --overwrite

Alterar um layout MIG interrompe cargas de trabalho que já usam a GPU. Isole e drene o nó de destino, aplique a configuração durante uma janela de manutenção e confirme se o perfil solicitado existe em recursos alocáveis do nó antes de enviar trabalhos.

Isolar cargas de trabalho de GPU com taints e tolerâncias

Um taint NoSchedule mantém pods de aplicativo comuns longe de nós de GPU caros. O exemplo de criação do pool de nós usa sku=gpu:NoSchedule. O seguinte trabalho completo contém a tolerância correspondente e um seletor de nós:

apiVersion: batch/v1
kind: Job
metadata:
  name: isolated-gpu-training
spec:
  backoffLimit: 3
  template:
    metadata:
      labels:
        app: isolated-gpu-training
    spec:
      restartPolicy: Never
      nodeSelector:
        accelerator: nvidia
        workload: training
      tolerations:
      - key: sku
        operator: Equal
        value: gpu
        effect: NoSchedule
      containers:
      - name: trainer
        image: nvcr.io/nvidia/cuda:12.4.1-runtime-ubuntu22.04
        command:
        - /bin/bash
        - -c
        - |
          set -e
          nvidia-smi
          sleep 60
        resources:
          requests:
            cpu: "4"
            memory: 16Gi
            nvidia.com/gpu: 1
          limits:
            cpu: "4"
            memory: 16Gi
            nvidia.com/gpu: 1

Uma tolerância permite o agendamento, mas não força o pod a entrar em um nó de GPU. Emparelhe tolerâncias com uma solicitação de recurso de GPU e um seletor de nó ou afinidade de nó. Verifique se os DaemonSets de plataforma necessários, como rede, monitoramento, armazenamento e o plug-in do dispositivo, toleram a taint de GPU.

Executar cargas de trabalho de treinamento distribuído

Principal conclusão: use um operador de treinamento para gerenciar as identidades dos trabalhadores e o ciclo de vida do trabalho, validar a comunicação de rede entre vários nós e usar a admissão em grupo quando todos os trabalhadores precisarem iniciar juntos.

O treinamento distribuído usa vários processos para dividir dados, estado do modelo ou estágios de pipeline. No Kubernetes, um operador pode criar pods de trabalho, injetar configuração de encontro, acompanhar o status da réplica, reiniciar trabalhos com falha e limpar o trabalho. Instale e controle a versão de operadores de treinamento por meio da IaC e do processo de lançamento da sua plataforma, em vez de permitir que equipes individuais instalem CRDs não governadas em todo o cluster.

Criar uma imagem de treinamento CUDA portátil

O Dockerfile a seguir expande o padrão Dockerfile identificado na seção Conteinerização. A imagem contém o ambiente de execução do CUDA e as bibliotecas do PyTorch, enquanto o driver compatível da NVIDIA permanece no nó de GPU do AKS:

FROM pytorch/pytorch:2.4.1-cuda12.4-cudnn9-runtime

WORKDIR /workspace

COPY requirements.txt .
RUN python -m pip install --no-cache-dir -r requirements.txt

COPY train.py .

ENTRYPOINT ["python", "/workspace/train.py"]

Fixe a imagem base por um resumo imutável na produção. Mantenha os conjuntos de dados e os pontos de verificação alterados com frequência fora da imagem e verifique a imagem resultante antes de enviá-la ao Registro de Contêiner do Azure.

Executar um PyTorchJob

O Operador de Treinamento do Kubeflow disponibiliza o CRD PyTorchJob. Instale uma versão do Operador de Treinamento compatível com sua versão do Kubernetes antes de aplicar esse manifesto. O exemplo a seguir, com duas réplicas, executa uma operação NCCL de all-reduce entre um mestre e um trabalhador:

apiVersion: v1
kind: Namespace
metadata:
  name: ml-training
  labels:
    purpose: ml-training
---
apiVersion: kubeflow.org/v1
kind: PyTorchJob
metadata:
  name: pytorch-nccl-example
  namespace: ml-training
spec:
  runPolicy:
    cleanPodPolicy: None
  pytorchReplicaSpecs:
    Master:
      replicas: 1
      restartPolicy: OnFailure
      template:
        metadata:
          labels:
            training-job: pytorch-nccl-example
        spec:
          nodeSelector:
            accelerator: nvidia
          tolerations:
          - key: sku
            operator: Equal
            value: gpu
            effect: NoSchedule
          containers:
          - name: pytorch
            image: pytorch/pytorch:2.4.1-cuda12.4-cudnn9-runtime
            command:
            - python
            - -c
            - |
              import os
              import torch
              import torch.distributed as dist

              torch.cuda.set_device(0)
              dist.init_process_group(backend="nccl")
              value = torch.tensor(
                  [float(dist.get_rank() + 1)],
                  device="cuda"
              )
              dist.all_reduce(value)
              print(
                  f"rank={dist.get_rank()} "
                  f"world_size={dist.get_world_size()} "
                  f"all_reduce_sum={value.item()}"
              )
              dist.destroy_process_group()
            env:
            - name: NCCL_DEBUG
              value: INFO
            - name: NCCL_SOCKET_IFNAME
              value: eth0
            - name: NCCL_IB_DISABLE
              value: "1"
            - name: TORCH_NCCL_ASYNC_ERROR_HANDLING
              value: "1"
            resources:
              requests:
                cpu: "4"
                memory: 16Gi
                nvidia.com/gpu: 1
              limits:
                cpu: "4"
                memory: 16Gi
                nvidia.com/gpu: 1
    Worker:
      replicas: 1
      restartPolicy: OnFailure
      template:
        metadata:
          labels:
            training-job: pytorch-nccl-example
        spec:
          nodeSelector:
            accelerator: nvidia
          tolerations:
          - key: sku
            operator: Equal
            value: gpu
            effect: NoSchedule
          containers:
          - name: pytorch
            image: pytorch/pytorch:2.4.1-cuda12.4-cudnn9-runtime
            command:
            - python
            - -c
            - |
              import os
              import torch
              import torch.distributed as dist

              torch.cuda.set_device(0)
              dist.init_process_group(backend="nccl")
              value = torch.tensor(
                  [float(dist.get_rank() + 1)],
                  device="cuda"
              )
              dist.all_reduce(value)
              print(
                  f"rank={dist.get_rank()} "
                  f"world_size={dist.get_world_size()} "
                  f"all_reduce_sum={value.item()}"
              )
              dist.destroy_process_group()
            env:
            - name: NCCL_DEBUG
              value: INFO
            - name: NCCL_SOCKET_IFNAME
              value: eth0
            - name: NCCL_IB_DISABLE
              value: "1"
            - name: TORCH_NCCL_ASYNC_ERROR_HANDLING
              value: "1"
            resources:
              requests:
                cpu: "4"
                memory: 16Gi
                nvidia.com/gpu: 1
              limits:
                cpu: "4"
                memory: 16Gi
                nvidia.com/gpu: 1

Para treinamento em produção, substitua o teste embutido pela sua imagem e pelo seu script de treinamento com controle de versão. Alinhe as contagens de réplicas com o número de GPUs e processos exigidos pela estrutura de treinamento.

Executar um TFJob

O operador de treinamento também fornece o TFJob CRD. O exemplo a seguir executa o treinamento síncrono do TensorFlow em dois trabalhadores de GPU usando MultiWorkerMirroredStrategy:

apiVersion: v1
kind: Namespace
metadata:
  name: ml-training
  labels:
    purpose: ml-training
---
apiVersion: kubeflow.org/v1
kind: TFJob
metadata:
  name: tensorflow-multiworker-example
  namespace: ml-training
spec:
  runPolicy:
    cleanPodPolicy: None
  tfReplicaSpecs:
    Worker:
      replicas: 2
      restartPolicy: OnFailure
      template:
        metadata:
          labels:
            training-job: tensorflow-multiworker-example
        spec:
          nodeSelector:
            accelerator: nvidia
          tolerations:
          - key: sku
            operator: Equal
            value: gpu
            effect: NoSchedule
          containers:
          - name: tensorflow
            image: tensorflow/tensorflow:2.16.1-gpu
            command:
            - python
            - -c
            - |
              import tensorflow as tf

              strategy = tf.distribute.MultiWorkerMirroredStrategy()
              print("workers:", strategy.num_replicas_in_sync)

              with strategy.scope():
                  model = tf.keras.Sequential([
                      tf.keras.layers.Input(shape=(32,)),
                      tf.keras.layers.Dense(64, activation="relu"),
                      tf.keras.layers.Dense(1)
                  ])
                  model.compile(
                      optimizer="adam",
                      loss="mean_squared_error"
                  )

              features = tf.random.normal([4096, 32])
              labels = tf.random.normal([4096, 1])
              dataset = (
                  tf.data.Dataset.from_tensor_slices((features, labels))
                  .shuffle(4096)
                  .repeat()
                  .batch(64)
              )
              model.fit(dataset, epochs=2, steps_per_epoch=32)
            resources:
              requests:
                cpu: "4"
                memory: 16Gi
                nvidia.com/gpu: 1
              limits:
                cpu: "4"
                memory: 16Gi
                nvidia.com/gpu: 1

Para o treinamento com servidor de parâmetros, defina os tipos de réplica Chief, Worker e PS de acordo com a estratégia de distribuição do TensorFlow. Meça os gargalos de rede e de servidor de parâmetro resultantes antes de aumentar a contagem de réplicas.

Faça o ajuste fino de um modelo com KAITO

O KAITO fornece uma abstração de workspace voltada ao AKS para implantação de modelos e ajuste fino. Habilite o complemento KAITO ou instale uma versão compatível do KAITO antes de aplicar um Workspace. Verifique se a predefinição selecionada, a SKU da VM e o método de ajuste fino têm suporte na versão do KAITO instalada.

O workspace a seguir solicita uma VM de GPU A100 e inicia o ajuste fino do QLoRA para uma predefinição Phi-3 com suporte usando um conjunto de dados acessível publicamente:

apiVersion: kaito.sh/v1alpha1
kind: Workspace
metadata:
  name: workspace-tuning-phi-3-mini
resource:
  instanceType: Standard_NC24ads_A100_v4
  labelSelector:
    matchLabels:
      apps: phi-3-mini-tuning
tuning:
  preset:
    name: phi-3-mini-4k-instruct
  method: qlora
  input:
    urls:
    - https://huggingface.co/datasets/yahma/alpaca-cleaned/resolve/main/alpaca_data_cleaned.json

O KAITO pode coordenar a configuração predefinida do modelo e a infraestrutura de GPU necessária para o workspace. Para produção, armazene o conjunto de dados de treinamento em uma conta de armazenamento do Azure aprovada, use acesso privado e identidade de carga de trabalho onde houver suporte e persista o modelo resultante em um registro de modelos governado ou no Registro de Contêiner do Azure. Trate o manifesto do workspace, a versão do conjunto de dados de entrada, a versão da predefinição, a configuração do adaptador e o digest da imagem de saída como um único registro de treinamento com controle de versão

Configurar a comunicação NCCL com o CNI do AKS

A NCCL (Biblioteca de Comunicações Coletivas) da NVIDIA manipula operações coletivas, como redução total. Para uma linha de base TCP portátil no CNI do AKS, use a interface de rede do pod, em geral eth0, e inicie com NCCL_IB_DISABLE=1. Em tamanhos de VM com suporte a RDMA, use a configuração de RDMA documentada da NVIDIA e do Azure e valide-a antes de definir NCCL_IB_DISABLE=0.

Use estas variáveis de ambiente de linha de base:

  • NCCL_SOCKET_IFNAME=eth0 seleciona a interface de rede do pod.
  • NCCL_DEBUG=INFO fornece diagnóstico durante a validação. Reduza o nível de log após o ajuste.
  • NCCL_IB_DISABLE=1 seleciona soquetes TCP quando RDMA não está configurado.
  • TORCH_NCCL_ASYNC_ERROR_HANDLING=1 ajuda o PyTorch a terminar em vez de ficar suspenso indefinidamente após falhas de comunicação assíncronas.

Se a política de rede estiver habilitada, permita todo o tráfego de reunião e NCCL necessários entre réplicas. O NCCL pode negociar portas dinâmicas; portanto, uma política restritiva de portas fixas pode levar ao travamento de trabalhos de treinamento. A seguinte política permite comunicação irrestrita de pod para pod somente entre réplicas do trabalho PyTorch e permite a resolução DNS:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-pytorch-nccl
  namespace: ml-training
spec:
  podSelector:
    matchLabels:
      training-job: pytorch-nccl-example
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          training-job: pytorch-nccl-example
  egress:
  - to:
    - podSelector:
        matchLabels:
          training-job: pytorch-nccl-example
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53

Avalie o desempenho do NCCL separadamente do treinamento do modelo antes de expandir. Compare a largura de banda de all-reduce, a latência, a utilização da GPU e a taxa de processamento do treinamento em cada contagem de trabalhadores. Mais trabalhadores podem reduzir o desempenho quando o carregamento de dados ou comunicação se torna o gargalo.

Use agendamento de gangue e filas de cargas de trabalho

Principal conclusão: admita os trabalhadores distribuídos como um grupo e aplique cotas de GPU por locatário para que os trabalhos parcialmente agendados não reservem GPUs enquanto esperam pelos trabalhadores restantes.

O agendador padrão do Kubernetes agenda os pods individualmente. Para um trabalho distribuído que não pode progredir até que cada trabalhador esteja em execução, o posicionamento parcial pode desperdiçar a capacidade de GPU. O Kueue oferece controle de admissão e gerenciamento de cotas antes do início da execução de pods. O Volcano oferece um agendador e um modelo de agendamento do tipo tudo ou nada baseado em PodGroup.

O AKS Automatic fornece o agendador padrão do Kubernetes e o provisionamento automático de nós como ponto de partida. Envie trabalhos comuns ou trabalhos de treinamento gerenciados por operador com solicitações de recursos precisas e permita que a plataforma provisione a capacidade qualificada. Esse comportamento não garante a admissão atômica para cada trabalhador. Instale o Kueue ou o Volcano quando uma carga de trabalho exigir agendamento de gangue, enfileiramento, cotas de equipe, compartilhamento equitativo ou comportamento personalizado de preempção.

Aloque a cota de GPU multilocatário com Kueue

Instale uma versão do Kueue compatível com a versão do Kubernetes e habilite integrações para os tipos de carga de trabalho que você usa. O manifesto a seguir cria:

  • Uma GPU ResourceFlavor associada a nós rotulados accelerator=nvidia.
  • Um ClusterQueue de quatro GPUs para a equipe A.
  • Um ClusterQueue de duas GPUs para a equipe B.
  • Um namespace com escopo LocalQueue para cada equipe.
  • Um trabalho de dois trabalhadores que o Kueue admite somente quando os recursos solicitados estão disponíveis.
apiVersion: v1
kind: Namespace
metadata:
  name: team-a
---
apiVersion: v1
kind: Namespace
metadata:
  name: team-b
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: nvidia-gpu
spec:
  nodeLabels:
    accelerator: nvidia
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: team-a-gpu
spec:
  namespaceSelector:
    matchLabels:
      kubernetes.io/metadata.name: team-a
  queueingStrategy: BestEffortFIFO
  resourceGroups:
  - flavors:
    - name: nvidia-gpu
      resources:
      - name: cpu
        nominalQuota: "64"
      - name: memory
        nominalQuota: 256Gi
      - name: nvidia.com/gpu
        nominalQuota: "4"
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: team-b-gpu
spec:
  namespaceSelector:
    matchLabels:
      kubernetes.io/metadata.name: team-b
  queueingStrategy: BestEffortFIFO
  resourceGroups:
  - flavors:
    - name: nvidia-gpu
      resources:
      - name: cpu
        nominalQuota: "32"
      - name: memory
        nominalQuota: 128Gi
      - name: nvidia.com/gpu
        nominalQuota: "2"
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  name: gpu-queue
  namespace: team-a
spec:
  clusterQueue: team-a-gpu
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  name: gpu-queue
  namespace: team-b
spec:
  clusterQueue: team-b-gpu
---
apiVersion: batch/v1
kind: Job
metadata:
  name: team-a-two-gpu-training
  namespace: team-a
  labels:
    kueue.x-k8s.io/queue-name: gpu-queue
spec:
  suspend: true
  completions: 2
  parallelism: 2
  backoffLimit: 2
  template:
    metadata:
      labels:
        app: team-a-two-gpu-training
    spec:
      restartPolicy: Never
      tolerations:
      - key: sku
        operator: Equal
        value: gpu
        effect: NoSchedule
      containers:
      - name: worker
        image: nvcr.io/nvidia/cuda:12.4.1-runtime-ubuntu22.04
        command:
        - /bin/bash
        - -c
        - |
          set -e
          nvidia-smi
          echo "Kueue admitted this worker."
          sleep 120
        resources:
          requests:
            cpu: "4"
            memory: 16Gi
            nvidia.com/gpu: 1
          limits:
            cpu: "4"
            memory: 16Gi
            nvidia.com/gpu: 1

Uma ClusterQueue cota é uma cota de agendamento, não uma cota de assinatura Azure. Verifique se o cluster do AKS pode provisionar a capacidade subjacente de VMs e se a cota de GPUs do Azure é suficiente para a carga de trabalho total admitida. Use coortes e recursos de compartilhamento justo do Kueue quando as equipes puderem emprestar cota não utilizada entre si.

Usar o Vulcão como alternativa

O Volcano é uma alternativa quando você deseja um agendador em lotes dedicado com agendamento de gangues, filas e políticas de ciclo de vida do trabalho. Instale uma versão do Volcano compatível com o cluster antes de aplicar seus CRDs. O seguinte trabalho do Volcano exige que os dois trabalhadores de GPU estejam disponíveis antes da execução do trabalho:

apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
  name: volcano-gpu-training
  namespace: default
spec:
  minAvailable: 2
  schedulerName: volcano
  policies:
  - event: PodEvicted
    action: RestartJob
  - event: PodFailed
    action: RestartJob
  tasks:
  - replicas: 2
    name: worker
    template:
      metadata:
        labels:
          app: volcano-gpu-training
      spec:
        restartPolicy: OnFailure
        nodeSelector:
          accelerator: nvidia
        tolerations:
        - key: sku
          operator: Equal
          value: gpu
          effect: NoSchedule
        containers:
        - name: worker
          image: nvcr.io/nvidia/cuda:12.4.1-runtime-ubuntu22.04
          command:
          - /bin/bash
          - -c
          - |
            set -e
            nvidia-smi
            echo "All Volcano workers were admitted."
            sleep 120
          resources:
            requests:
              cpu: "4"
              memory: 16Gi
              nvidia.com/gpu: 1
            limits:
              cpu: "4"
              memory: 16Gi
              nvidia.com/gpu: 1

Padronize em um sistema primário de filas e agendamento de gangues, a menos que você tenha um design de interoperabilidade testado. Vários controladores de admissão ou agendadores podem dificultar o comportamento pendente e de preempção.

Configurar prioridades de treinamento e inferência

A inferência sensível à latência geralmente requer uma prioridade maior do que o treinamento em lote interruptível. Os seguintes recursos PriorityClass permitem que os pods de inferência prevaleçam sobre pods de menor prioridade, impedindo que pods de treinamento em lote prevaleçam sobre outras cargas de trabalho:

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: training-batch
value: 10000
globalDefault: false
preemptionPolicy: Never
description: Batch training can wait and can be preempted by higher-priority workloads.
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: inference-critical
value: 100000
globalDefault: false
preemptionPolicy: PreemptLowerPriority
description: Latency-sensitive production inference can preempt lower-priority workloads.

Atribua a classe por meio de spec.priorityClassName no modelo de pod. A prioridade do pod do Kubernetes e a prioridade de carga de trabalho do Kueue afetam diferentes estágios: o Kueue controla a admissão de fila, enquanto a prioridade do Kubernetes influencia o agendamento e a preempção de pod após a admissão. Coordene ambas as políticas para que expressem a mesma prioridade de negócios.

A preempção encerra pods de prioridade inferior. As cargas de trabalho de treinamento que podem ser interrompidas devem gravar pontos de verificação em armazenamento persistente, tolerar a duplicação de trabalho e se recuperar sem depender de arquivos locais do nó.

Configurar a memória compartilhada e o gerenciamento de recursos

Principal conclusão: substitua o padrão pequeno de runtime de contêineres /dev/shm, defina as solicitações de recursos com base na utilização observada e configure o escalonamento dos nós com base em requisitos completos das cargas de trabalho de GPU.

Aumente /dev/shm para carregadores de dados do ML

Carregadores de dados do PyTorch, pipelines de entrada do TensorFlow, NCCL e multiprocessamento do Python podem usar memória compartilhada POSIX. O padrão do contêiner para /dev/shm costuma ser pequeno demais para treinamento com vários processos, o que pode causar falhas dos trabalhadores, erros de barramento ou travamentos aparentes no treinamento.

Monte um emptyDir com suporte de memória em /dev/shm e defina um sizeLimit explícito:

apiVersion: batch/v1
kind: Job
metadata:
  name: shared-memory-training
spec:
  backoffLimit: 2
  template:
    metadata:
      labels:
        app: shared-memory-training
    spec:
      restartPolicy: Never
      nodeSelector:
        accelerator: nvidia
      tolerations:
      - key: sku
        operator: Equal
        value: gpu
        effect: NoSchedule
      containers:
      - name: trainer
        image: pytorch/pytorch:2.4.1-cuda12.4-cudnn9-runtime
        command:
        - /bin/bash
        - -c
        - |
          set -e
          df -h /dev/shm
          python -c '
          import multiprocessing as mp
          import torch

          def worker(index):
              value = torch.ones(1024, 1024)
              print(f"worker={index}, sum={value.sum().item()}")

          if __name__ == "__main__":
              processes = [mp.Process(target=worker, args=(i,)) for i in range(4)]
              for process in processes:
                  process.start()
              for process in processes:
                  process.join()
                  if process.exitcode != 0:
                      raise SystemExit(process.exitcode)
          '
        volumeMounts:
        - name: shared-memory
          mountPath: /dev/shm
        resources:
          requests:
            cpu: "8"
            memory: 16Gi
            nvidia.com/gpu: 1
          limits:
            cpu: "8"
            memory: 16Gi
            nvidia.com/gpu: 1
      volumes:
      - name: shared-memory
        emptyDir:
          medium: Memory
          sizeLimit: 8Gi

O uso com suporte de memória emptyDir é contabilizado no consumo de memória do pod. Inclua o uso máximo de memória compartilhada esperado ao definir o limite de memória do contêiner. Se /dev/shm crescer além do orçamento de memória efetivo, será possível remover ou encerrar o pod por exceder o limite de memória.

Dimensione CPU e memória com base na utilização observada

Comece com um parâmetro de comparação com base no modelo representativo, tamanho do lote, comprimento da sequência, simultaneidade do carregador de dados, aumento e comportamento de ponto de verificação. Em seguida, use os dados de monitoramento para ajustar:

  • Defina requisições de CPU altas o suficiente para manter os pipelines de entrada da GPU abastecidos. Períodos ociosos persistentes de GPU podem indicar falta de CPU ou armazenamento.
  • Defina solicitações de memória perto do uso estável do conjunto de trabalho mais uma margem de segurança. Inclua /dev/shm, comportamento de cache de página, sobrecarga do alocador de estrutura e picos de serialização de ponto de verificação.
  • Defina limites de memória acima do pico observado. Investigue os eventos OOMKilled em vez de aumentar repetidamente os limites sem encontrar a causa.
  • Avalie a limitação da CPU antes de usar limites restritivos de uso de CPU. Os limites de CPU muito baixos podem reduzir a utilização da GPU mesmo quando o uso médio da CPU parece aceitável.
  • Defina solicitações e limites iguais para trabalhos de treinamento previsíveis e de alto valor quando a Qualidade de serviço garantida for mais importante do que o empacotamento de nós.
  • Crie o perfil de cada alteração significativa no tamanho do modelo, precisão, tamanho do lote, contagem de trabalho e pipeline de dados.

Use percentuais em execuções de treinamento completas, e não uma média curta. As fases de inicialização, validação, ponto de verificação e ordem aleatória de dados costumam ter picos de recursos distintos.

Configurar o dimensionamento automático de cluster para um pool de nós de GPU

Para o AKS Standard, habilite o dimensionador automático de cluster no pool de nós de usuário da GPU e defina a capacidade mínima e máxima:

RESOURCE_GROUP=myResourceGroup
AKS_CLUSTER=myAKSCluster
GPU_NODE_POOL=gpunp

az aks nodepool update \
  --resource-group "$RESOURCE_GROUP" \
  --cluster-name "$AKS_CLUSTER" \
  --name "$GPU_NODE_POOL" \
  --enable-cluster-autoscaler \
  --min-count 0 \
  --max-count 8

Você pode ajustar as configurações de perfil com suporte do dimensionador automático de cluster em nível de cluster. Testar alterações de perfil em relação a cargas de trabalho de treinamento e de serviço:

az aks update \
  --resource-group "$RESOURCE_GROUP" \
  --name "$AKS_CLUSTER" \
  --cluster-autoscaler-profile \
    scan-interval=20s \
    scale-down-unneeded-time=10m \
    scale-down-delay-after-add=15m \
    max-graceful-termination-sec=120

Um pod de GPU pendente dispara a escala vertical apenas quando todos os requisitos de agendamento correspondem a um modelo de pool de nós. Verifique a solicitação de recursos de GPU, a capacidade da VM, os rótulos, os taints, as tolerâncias, a afinidade, as restrições de topologia, a topologia do volume persistente, o número máximo de nós e a cota do Azure quando a expansão não ocorrer.

O escalonamento para zero é apropriado para pools de treinamento interruptíveis, mas acrescenta latência no provisionamento da VM, no download da imagem, na montagem do conjunto de dados e na inicialização do modelo. Mantenha um mínimo diferente de zero quando a latência de inicialização tiver um impacto significativo sobre as metas de serviço. Orçamentos de interrupção de pods, pods que não despejáveis, armazenamento local e longos períodos de tolerância para encerramento podem atrasar a redução vertical.

O AKS Automatic gerencia o provisionamento e o escalonamento de nós como parte da configuração básica do serviço. Foque solicitações precisas de pods e restrições compatíveis, em vez de configurar manualmente um dimensionador automático para o pool de nós de GPU.

Armazenar conjuntos de dados e pontos de verificação

Principal conclusão: selecione o armazenamento com base em padrão de acesso, taxa de transferência, compartilhamento e requisitos de recuperação, e mantenha os pontos de verificação fora do nó para que o treinamento possa se recuperar de remoção ou preempção.

Mantenha grandes conjuntos de dados, artefatos de modelo e pontos de verificação fora das imagens de contêiner. Escolha uma interface de armazenamento com base na carga de trabalho:

  • Use Armazenamento de Blobs do Azure para conjuntos de dados de objetos grandes, artefatos de modelo e repositórios de dados de alta capacidade.
  • Use Arquivos do Azure quando vários nós de trabalho exigirem um sistema de arquivos compartilhado no estilo POSIX.
  • Use o Disco do Azure para cargas de trabalho de ponto de verificação ou de cache de leitura/gravação em nó único e alto desempenho, quando ReadWriteOnce for suficiente.
  • Use o armazenamento efêmero local do nó somente para caches reconstruíveis e arquivos temporários.

Acesse grandes volumes de dados com o driver CSI do Azure Blob

Habilite o driver CSI do blob Azure no AKS Standard se ele ainda não estiver habilitado:

RESOURCE_GROUP=myResourceGroup
AKS_CLUSTER=myAKSCluster

az aks update \
  --resource-group "$RESOURCE_GROUP" \
  --name "$AKS_CLUSTER" \
  --enable-blob-driver

O exemplo a seguir cria dinamicamente um contêiner premium do Azure Blob exposto via NFS e o monta em um pod de treinamento. Para um conjunto de dados controlado existente, use um volume definido estaticamente ou uma configuração do BlobFuse com a identidade apropriada e controles de rede privados em vez de criar um contêiner dinâmico vazio:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ml-azureblob-nfs
provisioner: blob.csi.azure.com
parameters:
  protocol: nfs
  skuName: Premium_LRS
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: Immediate
mountOptions:
- -o attr_timeout=120
- -o entry_timeout=120
- -o negative_timeout=120
- -o actimeo=120
- -o noresvport
- -o nconnect=4
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: blob-training-dataset
  namespace: default
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: ml-azureblob-nfs
  resources:
    requests:
      storage: 1Ti
---
apiVersion: batch/v1
kind: Job
metadata:
  name: inspect-blob-dataset
  namespace: default
spec:
  backoffLimit: 2
  template:
    metadata:
      labels:
        app: inspect-blob-dataset
    spec:
      restartPolicy: Never
      containers:
      - name: dataset-reader
        image: ubuntu:24.04
        command:
        - /bin/bash
        - -c
        - |
          set -e
          echo "Mounted dataset path:"
          df -h /mnt/dataset
          find /mnt/dataset -maxdepth 2 -type f | head -100
        volumeMounts:
        - name: dataset
          mountPath: /mnt/dataset
      volumes:
      - name: dataset
        persistentVolumeClaim:
          claimName: blob-training-dataset

Faça o benchmark das opções de montagem e protocolo em relação aos tamanhos de fragmentos representativos e aos padrões de acesso. Muitos trabalhadores que leem muitos arquivos pequenos podem produzir resultados diferentes do streaming sequencial de fragmentos grandes. Considere pré-processar os conjuntos de dados em partições de tamanho adequado e usar espaço de cache local do nó quando épocas repetidas baixem os mesmos objetos.

Compartilhar dados de treinamento com Arquivos do Azure

Os Arquivos do Azure dão suporte a ReadWriteMany, permitindo que trabalhadores distribuídos em nós distintos montem o mesmo compartilhamento. O Arquivos do Azure Premium é apropriado quando a carga de trabalho requer um desempenho previsível do sistema de arquivos e a região selecionada dá suporte à opção de redundância necessária.

O manifesto a seguir cria um compartilhamento de Arquivos do Azure premium e o monta em dois pods de trabalho paralelos:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ml-azurefile-premium
provisioner: file.csi.azure.com
parameters:
  skuName: Premium_LRS
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: Immediate
mountOptions:
- dir_mode=0770
- file_mode=0660
- uid=1000
- gid=1000
- mfsymlinks
- cache=strict
- actimeo=30
- nosharesock
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: shared-training-data
  namespace: default
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: ml-azurefile-premium
  resources:
    requests:
      storage: 100Gi
---
apiVersion: batch/v1
kind: Job
metadata:
  name: shared-data-workers
  namespace: default
spec:
  completions: 2
  parallelism: 2
  completionMode: Indexed
  backoffLimitPerIndex: 2
  template:
    metadata:
      labels:
        app: shared-data-workers
    spec:
      restartPolicy: Never
      securityContext:
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
      containers:
      - name: worker
        image: ubuntu:24.04
        command:
        - /bin/bash
        - -c
        - |
          set -e
          WORKER_INDEX="${JOB_COMPLETION_INDEX:-0}"
          echo "worker=${WORKER_INDEX}" \
            > "/mnt/shared/worker-${WORKER_INDEX}.txt"
          ls -la /mnt/shared
        volumeMounts:
        - name: shared-data
          mountPath: /mnt/shared
      volumes:
      - name: shared-data
        persistentVolumeClaim:
          claimName: shared-training-data

Evite fazer com que todos os trabalhadores enumerem repetidamente um diretório que contenha milhões de arquivos. Use um manifesto ou uma atribuição de fragmento determinística para que cada trabalhador saiba os objetos a serem lidos.

Ponto de verificação para armazenamento persistente

Um ponto de verificação deve incluir informações de estado suficientes para permitir a retomada correta, como pesos do modelo, estado do otimizador, estado do agendador, estado do escalador, número da época ou da etapa, estado do gerador de números aleatórios e posição do carregador de dados, quando houver suporte. Escreva pontos de verificação periodicamente e antes do término normal.

O seguinte trabalho grava pontos de verificação atômicos do PyTorch para Arquivos do Azure, restaura o ponto de verificação mais recente após uma reinicialização de contêiner ou substituição de pod e trata SIGTERM para que a preempção possa preservar o progresso recente:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ml-checkpoints-azurefile
provisioner: file.csi.azure.com
parameters:
  skuName: Premium_LRS
reclaimPolicy: Retain
allowVolumeExpansion: true
volumeBindingMode: Immediate
mountOptions:
- dir_mode=0770
- file_mode=0660
- uid=1000
- gid=1000
- mfsymlinks
- cache=strict
- actimeo=30
- nosharesock
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: model-checkpoints
  namespace: default
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: ml-checkpoints-azurefile
  resources:
    requests:
      storage: 100Gi
---
apiVersion: batch/v1
kind: Job
metadata:
  name: checkpointed-pytorch-training
  namespace: default
spec:
  backoffLimit: 6
  template:
    metadata:
      labels:
        app: checkpointed-pytorch-training
    spec:
      restartPolicy: OnFailure
      terminationGracePeriodSeconds: 120
      securityContext:
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
      containers:
      - name: trainer
        image: pytorch/pytorch:2.4.1-cuda12.4-cudnn9-runtime
        command:
        - python
        - -c
        - |
          import os
          import signal
          import sys
          import time
          import torch

          checkpoint_path = "/checkpoints/latest.pt"
          temporary_path = "/checkpoints/latest.pt.tmp"
          state = {"epoch": 0, "value": torch.tensor([0.0])}

          if os.path.exists(checkpoint_path):
              state = torch.load(checkpoint_path, map_location="cpu")
              print(f"Restored epoch {state['epoch']}", flush=True)

          def save_checkpoint():
              torch.save(state, temporary_path)
              os.replace(temporary_path, checkpoint_path)
              print(f"Saved epoch {state['epoch']}", flush=True)

          def terminate(signum, frame):
              print(f"Received signal {signum}", flush=True)
              save_checkpoint()
              sys.exit(143)

          signal.signal(signal.SIGTERM, terminate)
          signal.signal(signal.SIGINT, terminate)

          for epoch in range(state["epoch"] + 1, 21):
              state["epoch"] = epoch
              state["value"] += 1
              time.sleep(10)
              if epoch % 2 == 0:
                  save_checkpoint()

          save_checkpoint()
          print("Training completed.", flush=True)
        volumeMounts:
        - name: checkpoints
          mountPath: /checkpoints
        resources:
          requests:
            cpu: "1"
            memory: 2Gi
          limits:
            cpu: "1"
            memory: 2Gi
      volumes:
      - name: checkpoints
        persistentVolumeClaim:
          claimName: model-checkpoints

Use uma política de recuperação Retain ou uma conta de armazenamento gerenciada independentemente para pontos de verificação de produção para que a exclusão de um PVC não exclua acidentalmente o único ponto de recuperação. Para treinamento paralelo de dados distribuído, em geral faz-se com que a classificação zero grave o ponto de verificação global, ou usa-se um formato de ponto de verificação particionado com suporte da estrutura. Impedir que várias classificações gravem o mesmo arquivo ao mesmo tempo.

Teste a recuperação excluindo intencionalmente um pod de trabalho durante uma execução de não produção. Verifique se o controlador recria a carga de trabalho, se o novo pod monta o mesmo volume persistente e se o treinamento retoma a partir da etapa esperada.

Monitorar a utilização da GPU e a taxa de processamento do treinamento

Principal conclusão: monitore em conjunto o processamento da GPU, a memória da GPU, a taxa de transferência do pipeline de dados, a duração de cada etapa e a atividade de ponto de verificação para distinguir a saturação do processamento dos gargalos de CPU, rede ou armazenamento.

Habilite o serviço gerenciado do Azure Monitor para Prometheus e Container Insights para correlacionar o estado do Kubernetes, os logs de contêineres, a integridade dos nós e as métricas do Prometheus. Use as práticas recomendadas de monitoramento para o AKS ao criar alertas, retenção e painéis operacionais.

Coletar métricas de GPU NVIDIA

O Exportador do DCGM (Gerenciador de GPUs) do NVIDIA Data Center expõe as métricas do Prometheus para GPUs NVIDIA. Se o Operador de GPU NVIDIA já implantar o Exportador de DCGM, não implante um exportador duplicado. Configure o Prometheus gerenciado do Azure Monitor para extrair do exportador existente.

O PodMonitor a seguir usa o CRD do Prometheus gerenciado do Azure Monitor e tem como destino os pods do exportador DCGM no namespace gpu-operator. Ajuste o seletor de rótulo para corresponder aos rótulos usados pelo exportador instalado:

apiVersion: azmonitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: nvidia-dcgm-exporter
  namespace: gpu-operator
spec:
  selector:
    matchLabels:
      app: nvidia-dcgm-exporter
  podMetricsEndpoints:
  - port: metrics
    interval: 30s
    scrapeTimeout: 10s

Verifique o nome da porta do pod ou serviço do exportador antes de aplicar o monitor. As métricas comuns do DCGM incluem:

Métrica Purpose
DCGM_FI_DEV_GPU_UTIL Porcentagem de tempo em que a GPU está ativa
DCGM_FI_DEV_FB_USED Memória de buffer de quadro usada
DCGM_FI_DEV_FB_FREE Memória de buffer de quadros livre
DCGM_FI_DEV_MEM_COPY_UTIL Utilização do mecanismo de cópia de memória
DCGM_FI_DEV_POWER_USAGE Consumo de energia de GPU
DCGM_FI_DEV_GPU_TEMP Temperatura da GPU
DCGM_FI_DEV_XID_ERRORS Eventos de erro XID da NVIDIA

A disponibilidade da métrica depende das versões gpu, driver, DCGM e exportador.

Exportar métricas de vazão do treinamento

Instrumente o aplicativo de treinamento com métricas de nível de carga de trabalho. As métricas úteis incluem:

  • ml_training_samples_total para exemplos cumulativos ou tokens processados.
  • ml_training_steps_total para etapas de otimizador concluídas.
  • ml_training_step_duration_seconds para latência de etapa.
  • ml_training_checkpoint_duration_seconds para latência de ponto de verificação.
  • ml_training_data_wait_seconds para o tempo de espera por dados de entrada.
  • Perda específica do modelo, taxa de aprendizado, norma de gradiente e métricas de validação.

O exemplo executável a seguir expõe métricas de treinamento sintético na porta 8000. Substitua a simulação por métricas emitidas pelo loop de treinamento real:

apiVersion: v1
kind: Namespace
metadata:
  name: ml-observability
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: training-metrics-server
  namespace: ml-observability
data:
  server.py: |
    import http.server
    import threading
    import time

    state = {
        "samples": 0,
        "steps": 0,
        "last_step_duration": 0.0,
    }

    def train():
        while True:
            started = time.time()
            time.sleep(1)
            state["samples"] += 256
            state["steps"] += 1
            state["last_step_duration"] = time.time() - started

    class MetricsHandler(http.server.BaseHTTPRequestHandler):
        def do_GET(self):
            if self.path != "/metrics":
                self.send_response(404)
                self.end_headers()
                return

            body = (
                "# HELP ml_training_samples_total Samples processed.\n"
                "# TYPE ml_training_samples_total counter\n"
                f"ml_training_samples_total{{job_name=\"metrics-demo\"}} "
                f"{state['samples']}\n"
                "# HELP ml_training_steps_total Training steps completed.\n"
                "# TYPE ml_training_steps_total counter\n"
                f"ml_training_steps_total{{job_name=\"metrics-demo\"}} "
                f"{state['steps']}\n"
                "# HELP ml_training_step_duration_seconds "
                "Duration of the most recent training step.\n"
                "# TYPE ml_training_step_duration_seconds gauge\n"
                f"ml_training_step_duration_seconds"
                f"{{job_name=\"metrics-demo\"}} "
                f"{state['last_step_duration']}\n"
            ).encode("utf-8")

            self.send_response(200)
            self.send_header("Content-Type", "text/plain; version=0.0.4")
            self.send_header("Content-Length", str(len(body)))
            self.end_headers()
            self.wfile.write(body)

        def log_message(self, format, *args):
            return

    threading.Thread(target=train, daemon=True).start()
    http.server.ThreadingHTTPServer(("0.0.0.0", 8000), MetricsHandler).serve_forever()
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: training-metrics-demo
  namespace: ml-observability
spec:
  replicas: 1
  selector:
    matchLabels:
      app: training-metrics-demo
  template:
    metadata:
      labels:
        app: training-metrics-demo
    spec:
      containers:
      - name: metrics
        image: python:3.12-slim
        command:
        - python
        - /app/server.py
        ports:
        - name: metrics
          containerPort: 8000
        readinessProbe:
          httpGet:
            path: /metrics
            port: metrics
          initialDelaySeconds: 2
          periodSeconds: 10
        resources:
          requests:
            cpu: 50m
            memory: 64Mi
          limits:
            cpu: 200m
            memory: 128Mi
        volumeMounts:
        - name: application
          mountPath: /app
          readOnly: true
      volumes:
      - name: application
        configMap:
          name: training-metrics-server
---
apiVersion: azmonitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: training-metrics-demo
  namespace: ml-observability
spec:
  selector:
    matchLabels:
      app: training-metrics-demo
  podMetricsEndpoints:
  - port: metrics
    path: /metrics
    interval: 30s
    scrapeTimeout: 10s

Para um trabalho distribuído real, inclua rótulos estáveis, como nome do modelo, versão do modelo, nome do trabalho, ID de execução, função de réplica e equipe. Não use rótulos não associados, como IDs de exemplo, IDs de solicitação ou caminhos de conjunto de dados brutos porque rótulos de alta cardinalidade aumentam o custo de monitoramento e a latência da consulta.

Criar dashboards de GPU e taxa de transferência

Use consultas PromQL, como as seguintes, como ponto de partida e valide rótulos de métricas com base no seu exportador:

avg by (namespace, pod, gpu) (
  avg_over_time(DCGM_FI_DEV_GPU_UTIL[5m])
)

A consulta a seguir calcula o uso de memória do buffer de quadro em porcentagem:

100 *
DCGM_FI_DEV_FB_USED
/
(DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE)

A consulta a seguir calcula os exemplos de treinamento processados por segundo:

sum by (job_name) (
  rate(ml_training_samples_total[5m])
)

A consulta a seguir exibe a duração média da etapa:

avg by (job_name) (
  ml_training_step_duration_seconds
)

Interprete estes sinais juntos:

  • A baixa utilização de GPU e o alto tempo de espera de dados geralmente indicam armazenamento, rede, pré-processamento ou falta de CPU.
  • A alta utilização de GPU com a taxa de transferência esperada indica que o acelerador está sendo usado efetivamente.
  • O alto uso de memória da GPU e erros recorrentes de falta de memória indicam que o tamanho do lote, o comprimento da sequência, a memória de ativação, o estado do otimizador ou a fragmentação precisam ser ajustados.
  • A queda da taxa de transferência com a utilização estável de GPU pode indicar sequências mais longas, sobrecarga de comunicação, comportamento térmico ou alteração na computação do modelo.
  • A baixa utilização de GPU em GPUs alocadas indica uma capacidade ociosa que pode ser reduzida, enfileirada de forma distinta ou particionada com MIG.
  • A longa duração do ponto de verificação pode tornar a preempção cara e aumentar a quantidade de trabalho repetido após a recuperação.

Crie alertas para subutilização sustentada de GPU, memória de GPU de quase capacidade, erros XID, taxa de transferência de treinamento paralisada, trabalhos com falha, cargas de trabalho agendadas por gangues pendentes e pontos de verificação que não foram concluídos dentro do objetivo de ponto de recuperação esperado.

Perguntas frequentes

Qual é a diferença entre o AKS Automatic e o AKS Standard para MLOps?

O AKS Automatic fornece padrões pré-configurados para pools de nós, rede, segurança e atualizações, permitindo que as equipes do MLOps se concentrem em definições de carga de trabalho e governança. O AKS Standard requer a configuração manual desses componentes de plataforma, mas oferece um controle mais profundo para arquiteturas personalizadas e requisitos de dimensionamento.

Como versionar modelos de ML no AKS?

Versão de seus modelos empacotando pesos, metadados e configurações de modelo em imagens de contêiner com marcas de versão semânticas. Armazene essas imagens em Registro de Contêiner do Azure e referencie versões específicas nos manifestos de implantação do Kubernetes para garantir a consistência entre ambientes.

Como faço para agendar cargas de trabalho de GPU no AKS?

Verifique se o plug-in do dispositivo NVIDIA está disponível para que os nós de GPU anunciem nvidia.com/gpu. No AKS Padrão, crie um pool dedicado de nós de usuário com GPU usando um tamanho de VM compatível, como uma SKU da série Standard_NC, e configure rótulos de nós, um taint NoSchedule e limites do dimensionador automático de cluster. Em cada pod de treinamento, solicite nvidia.com/gpu, selecione um nó de GPU qualificado e adicione a tolerância correspondente. O AKS Automático pode provisionar a capacidade de GPU qualificada com base em solicitações de pod, sujeito a SKUs compatíveis, disponibilidade regional, restrições de agendamento e cota da assinatura do Azure.

Como executar trabalhos de treinamento distribuídos no AKS?

Instale o Operador de Treinamento do Kubeflow e envie um PyTorchJob ou TFJob para gerenciar réplicas distribuídas, configuração de reunião, comportamento de reinicialização e status do trabalho. O KAITO fornece uma abstração de workspace voltada para o AKS para fluxos de trabalho compatíveis de ajuste fino de modelos e pode coordenar a infraestrutura de GPU com predefinições de modelos. Para o treinamento do PyTorch em vários nós, configure e compare o desempenho do NCCL na rede de pods AKS CNI, permita o tráfego entre nós de trabalho usando a política de rede e use a configuração de RDMA compatível quando a SKU de VM selecionada e a carga de trabalho o exigirem.

Como gerenciar a cota de GPU em várias equipes?

Instale o Kueue e defina recursos ClusterQueue com cotas de CPU, memória e nvidia.com/gpu e, em seguida, mapeie o namespace de cada equipe para a cota atribuída por meio de um LocalQueue. Use coortes do Kueue ou compartilhamento justo quando as equipes puderem pegar emprestada a capacidade não utilizada. O Volcano é uma alternativa quando você precisa de um agendador em lotes com admissão de trabalho tudo ou nada. Coordene a prioridade da fila com os recursos do Kubernetes PriorityClass para que a inferência sensível à latência possa prevalecer no treinamento interrompível e garanta que as tarefas de treinamento preemptíveis sejam recuperadas a partir de pontos de verificação no armazenamento persistente.

Quais são as práticas recomendadas para executar trabalhos em lotes de execução longa no AKS?

Use um Kubernetes Job para tarefas finitas e um CronJob para tarefas agendadas. Persista pontos de verificação e saída confirmada fora do pod, torne o processamento idempotente, trate SIGTERMe defina solicitações de recursos explícitas, limites de repetição, prazos e políticas de limpeza. Suponha que a manutenção ou falha do nó possa substituir o pod e testar se a substituição restaura o ponto de verificação corretamente. Monitore o status do trabalho, os eventos de pod, os logs, o tempo desde o último ponto de verificação, a taxa de transferência e o comportamento de repetições. Trabalhos reinicializáveis comuns não exigem agendamento de grupos ou um PDB. Use esses controles somente quando os trabalhadores paralelos precisarem iniciar juntos ou manter uma simultaneidade mínima testada.

Saiba mais sobre as melhores práticas em outras áreas da implantação e das operações do aplicativo no AKS: