Este tutorial mostra como partilhar eficientemente recursos de acelerador entre cargas de trabalho de serviço de inferência e de preparação num único cluster do Google Kubernetes Engine (GKE). Ao distribuir as suas cargas de trabalho mistas por um único cluster, melhora a utilização de recursos, simplifica a gestão de clusters, reduz os problemas decorrentes de limitações da quantidade de aceleradores e melhora a rentabilidade geral.
Neste tutorial, cria uma implementação de serviço de alta prioridade usando o modelo de linguagem grande (LLM) Gemma 2 para inferência e a framework de serviço Hugging Face TGI (interface de geração de texto), juntamente com uma tarefa de ajuste fino de LLM de baixa prioridade. Ambas as cargas de trabalho são executadas num único cluster que usa GPUs NVIDIA L4. Usa o Kueue, um sistema de filas de tarefas nativo do Kubernetes de código aberto, para gerir e agendar as suas cargas de trabalho. O Kueue permite-lhe dar prioridade a tarefas de publicação e antecipar tarefas de preparação com prioridade inferior para otimizar a utilização de recursos. À medida que as exigências de publicação diminuem, reatribui os aceleradores libertados para retomar as tarefas de preparação. Usa o Kueue e as classes de prioridade para gerir as quotas de recursos ao longo do processo.
Este tutorial destina-se a engenheiros de aprendizagem automática (ML), administradores e operadores de plataformas, e especialistas em dados e IA que pretendam preparar e alojar um modelo de aprendizagem automática (ML) num cluster do GKE, e que também pretendam reduzir os custos e a sobrecarga de gestão, especialmente quando lidam com um número limitado de aceleradores. Para saber mais sobre as funções comuns e exemplos de tarefas que referimos no conteúdo, consulte o artigo Funções e tarefas comuns do utilizador do GKE. Google Cloud
Antes de ler esta página, certifique-se de que conhece o seguinte:
Prepare o ambiente
Nesta secção, aprovisiona os recursos de que precisa para implementar o TGI e o modelo para as suas cargas de trabalho de inferência e preparação.
Aceda ao modelo
Para aceder aos modelos Gemma para implementação no GKE, primeiro tem de assinar o contrato de consentimento de licença e, em seguida, gerar um token de acesso do Hugging Face.
- Assine o contrato de consentimento de licença. Aceda à página de consentimento do modelo, valide o consentimento através da sua conta do Hugging Face e aceite os termos do modelo.
Gere uma chave de acesso. Para aceder ao modelo através do Hugging Face, precisa de um token do Hugging Face. Siga estes passos para gerar um novo token se ainda não tiver um:
- Clique em O seu perfil > Definições > Tokens de acesso.
- Selecione Novo token.
- Especifique um nome à sua escolha e uma função de, pelo menos,
Read
. - Selecione Gerar um token.
- Copie o token gerado para a área de transferência.
Inicie o Cloud Shell
Neste tutorial, vai usar o Cloud Shell para gerir recursos alojados no
Google Cloud. O Cloud Shell vem pré-instalado com o software de que precisa para este tutorial, incluindo o kubectl
, a
CLI gcloud e o Terraform.
Para configurar o seu ambiente com o Cloud Shell, siga estes passos:
Na Google Cloud consola, inicie uma sessão do Cloud Shell clicando em
Ativar Cloud Shell na Google Cloud consola. Esta ação inicia uma sessão no painel inferior da consola. Google Cloud
Defina as variáveis de ambiente predefinidas:
gcloud config set project PROJECT_ID export PROJECT_ID=$(gcloud config get project)
Substitua PROJECT_ID pelo seu Google Cloud ID do projeto.
Clone o exemplo de código do GitHub. No Cloud Shell, execute os seguintes comandos:
git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/ cd kubernetes-engine-samples/ai-ml/mix-train-and-inference export EXAMPLE_HOME=$(pwd)
Crie um cluster do GKE
Pode usar um cluster do Autopilot ou Standard para as suas cargas de trabalho mistas. Recomendamos que use um cluster do Autopilot para uma experiência do Kubernetes totalmente gerida. Para escolher o modo de funcionamento do GKE mais adequado às suas cargas de trabalho, consulte o artigo Escolha um modo de funcionamento do GKE.
Piloto automático
Defina as variáveis de ambiente predefinidas no Cloud Shell:
export HF_TOKEN=HF_TOKEN export REGION=REGION export CLUSTER_NAME="llm-cluster" export PROJECT_NUMBER=$(gcloud projects list \ --filter="$(gcloud config get-value project)" \ --format="value(PROJECT_NUMBER)") export MODEL_BUCKET="model-bucket-$PROJECT_ID"
Substitua os seguintes valores:
- HF_TOKEN: o token do Hugging Face que gerou anteriormente.
- REGION: uma região que suporta o tipo de acelerador que quer usar, por exemplo,
us-central1
para a GPU L4.
Pode ajustar a variável MODEL_BUCKET, que representa o contentor do Cloud Storage onde armazena as ponderações do modelo com aprendizagem.
Crie um cluster do Autopilot:
gcloud container clusters create-auto ${CLUSTER_NAME} \ --project=${PROJECT_ID} \ --location=${REGION} \ --release-channel=rapid
Crie o contentor do Cloud Storage para a tarefa de ajuste preciso:
gcloud storage buckets create gs://${MODEL_BUCKET} \ --location ${REGION} \ --uniform-bucket-level-access
Para conceder acesso ao contentor do Cloud Storage, execute este comando:
gcloud storage buckets add-iam-policy-binding "gs://$MODEL_BUCKET" \ --role=roles/storage.objectAdmin \ --member=principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/$PROJECT_ID.svc.id.goog/subject/ns/llm/sa/default \ --condition=None
Para obter as credenciais de autenticação do cluster, execute este comando:
gcloud container clusters get-credentials llm-cluster \ --location=$REGION \ --project=$PROJECT_ID
Crie um espaço de nomes para as suas implementações. No Cloud Shell, execute o seguinte comando:
kubectl create ns llm
Standard
Defina as variáveis de ambiente predefinidas no Cloud Shell:
export HF_TOKEN=HF_TOKEN export REGION=REGION export CLUSTER_NAME="llm-cluster" export GPU_POOL_MACHINE_TYPE="g2-standard-24" export GPU_POOL_ACCELERATOR_TYPE="nvidia-l4" export PROJECT_NUMBER=$(gcloud projects list \ --filter="$(gcloud config get-value project)" \ --format="value(PROJECT_NUMBER)") export MODEL_BUCKET="model-bucket-$PROJECT_ID"
Substitua os seguintes valores:
- HF_TOKEN: o token do Hugging Face que gerou anteriormente.
- REGION: a região que suporta o tipo de acelerador que quer usar, por exemplo,
us-central1
para a GPU L4.
Pode ajustar estas variáveis:
- GPU_POOL_MACHINE_TYPE: a série de máquinas do node pool que quer usar na região selecionada. Este valor depende do tipo de acelerador
que selecionou. Para saber mais, consulte o artigo Limitações da utilização de GPUs no GKE. Por exemplo, este tutorial usa o
g2-standard-24
com duas GPUs anexadas por nó. Para ver a lista mais atualizada de GPUs disponíveis, consulte o artigo GPUs para cargas de trabalho de computação. - GPU_POOL_ACCELERATOR_TYPE: o tipo de acelerador
suportado na região selecionada. Por exemplo, este tutorial usa
nvidia-l4
. Para ver a lista mais recente de GPUs disponíveis, consulte o artigo GPUs para cargas de trabalho de computação. - MODEL_BUCKET: o contentor do Cloud Storage onde armazena os pesos do modelo preparado.
Criar um cluster padrão:
gcloud container clusters create ${CLUSTER_NAME} \ --project=${PROJECT_ID} \ --location=${REGION} \ --workload-pool=${PROJECT_ID}.svc.id.goog \ --release-channel=rapid \ --machine-type=e2-standard-4 \ --addons GcsFuseCsiDriver \ --num-nodes=1
Crie o node pool de GPU para cargas de trabalho de inferência e ajuste preciso:
gcloud container node-pools create gpupool \ --accelerator type=${GPU_POOL_ACCELERATOR_TYPE},count=2,gpu-driver-version=latest \ --project=${PROJECT_ID} \ --location=${REGION} \ --node-locations=${REGION}-a \ --cluster=${CLUSTER_NAME} \ --machine-type=${GPU_POOL_MACHINE_TYPE} \ --num-nodes=3
Crie o contentor do Cloud Storage para a tarefa de ajuste preciso:
gcloud storage buckets create gs://${MODEL_BUCKET} \ --location ${REGION} \ --uniform-bucket-level-access
Para conceder acesso ao contentor do Cloud Storage, execute este comando:
gcloud storage buckets add-iam-policy-binding "gs://$MODEL_BUCKET" \ --role=roles/storage.objectAdmin \ --member=principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/$PROJECT_ID.svc.id.goog/subject/ns/llm/sa/default \ --condition=None
Para obter as credenciais de autenticação do cluster, execute este comando:
gcloud container clusters get-credentials llm-cluster \ --location=$REGION \ --project=$PROJECT_ID
Crie um espaço de nomes para as suas implementações. No Cloud Shell, execute o seguinte comando:
kubectl create ns llm
Crie um segredo do Kubernetes para as credenciais do Hugging Face
Para criar um segredo do Kubernetes que contenha o token do Hugging Face, execute o seguinte comando:
kubectl create secret generic hf-secret \
--from-literal=hf_api_token=$HF_TOKEN \
--dry-run=client -o yaml | kubectl apply --namespace=llm --filename=-
Configure o Kueue
Neste tutorial, o Kueue é o gestor de recursos central, o que permite a partilha eficiente de GPUs entre as cargas de trabalho de preparação e publicação. O Kueue consegue isto definindo requisitos de recursos ("sabores"), dando prioridade às cargas de trabalho através de filas (com tarefas de publicação com prioridade sobre o treino) e atribuindo dinamicamente recursos com base na procura e na prioridade. Este tutorial usa o tipo de recurso Workload para agrupar as cargas de trabalho de inferência e ajuste fino, respetivamente.
A funcionalidade de preemptção do Kueue garante que as cargas de trabalho de apresentação de alta prioridade têm sempre os recursos necessários, pausando ou desalojando tarefas de preparação de prioridade inferior quando os recursos são escassos.
Para controlar a implementação do servidor de inferência com o Kueue, ative a integração do pod
e configure o managedJobsNamespaceSelector
para excluir os espaços de nomes kube-system
e kueue-system
.
No diretório
/kueue
, veja o código emkustomization.yaml
. Este manifesto instala o gestor de recursos Kueue com configurações personalizadas.No diretório
/kueue
, veja o código empatch.yaml
. Este ConfigMap personaliza o Kueue para excluir a gestão de pods nos espaços de nomeskube-system
ekueue-system
.No Cloud Shell, execute o seguinte comando para instalar o Kueue:
cd ${EXAMPLE_HOME} kubectl kustomize kueue |kubectl apply --server-side --filename=-
Aguarde até que os pods do Kueue estejam prontos:
watch kubectl --namespace=kueue-system get pods
O resultado deve ser semelhante ao seguinte:
NAME READY STATUS RESTARTS AGE kueue-controller-manager-bdc956fc4-vhcmx 1/1 Running 0 3m15s
No diretório
/workloads
, veja os ficheirosflavors.yaml
,cluster-queue.yaml
elocal-queue.yaml
. Estes manifestos especificam como o Kueue gere as quotas de recursos:ResourceFlavor
Este manifesto define um ResourceFlavor predefinido no Kueue para a gestão de recursos.
ClusterQueue
Este manifesto configura uma Kueue ClusterQueue com limites de recursos para CPU, memória e GPU.
Este tutorial usa nós com duas GPUs Nvidia L4 anexadas, com o tipo de nó correspondente
g2-standard-24
, que oferece 24 vCPUs e 96 GB de RAM. O código de exemplo mostra como limitar a utilização de recursos da sua carga de trabalho a um máximo de seis GPUs.O campo
preemption
na configuração ClusterQueue faz referência às PriorityClasses para determinar que pods podem ser antecipados quando os recursos são escassos.LocalQueue
Este manifesto cria uma LocalQueue do Kueue denominada
lq
no espaço de nomesllm
.Veja os ficheiros
default-priorityclass.yaml
,low-priorityclass.yaml
ehigh-priorityclass.yaml
. Estes manifestos definem os objetos PriorityClass para o agendamento do Kubernetes.Prioridade predefinida
Prioridade baixa
Prioridade alta
Crie os objetos Kueue e Kubernetes executando estes comandos para aplicar os manifestos correspondentes.
cd ${EXAMPLE_HOME}/workloads kubectl apply --filename=flavors.yaml kubectl apply --filename=default-priorityclass.yaml kubectl apply --filename=high-priorityclass.yaml kubectl apply --filename=low-priorityclass.yaml kubectl apply --filename=cluster-queue.yaml kubectl apply --filename=local-queue.yaml --namespace=llm
Implemente o servidor de inferência TGI
Nesta secção, implementa o contentor TGI para publicar o modelo Gemma 2.
No diretório
/workloads
, veja o ficheirotgi-gemma-2-9b-it-hp.yaml
. Este manifesto define uma implementação do Kubernetes para implementar o tempo de execução de publicação do TGI e o modelogemma-2-9B-it
. Uma implementação é um objeto da API Kubernetes que lhe permite executar várias réplicas de pods distribuídas entre os nós num cluster.A implementação dá prioridade às tarefas de inferência e usa duas GPUs para o modelo. Usa o paralelismo de tensores, definindo a variável de ambiente
NUM_SHARD
, para ajustar o modelo à memória da GPU.Aplique o manifesto executando o seguinte comando:
kubectl apply --filename=tgi-gemma-2-9b-it-hp.yaml --namespace=llm
A operação de implementação demora alguns minutos a concluir.
Para verificar se o GKE criou a implementação com êxito, execute o seguinte comando:
kubectl --namespace=llm get deployment
O resultado deve ser semelhante ao seguinte:
NAME READY UP-TO-DATE AVAILABLE AGE tgi-gemma-deployment 1/1 1 1 5m13s
Valide a gestão de quotas do Kueue
Nesta secção, confirma que o Kueue está a aplicar corretamente a quota de GPU para a sua implementação.
Para verificar se o Kueue tem conhecimento da sua implementação, execute este comando para obter o estado dos objetos de carga de trabalho:
kubectl --namespace=llm get workloads
O resultado deve ser semelhante ao seguinte:
NAME QUEUE RESERVED IN ADMITTED FINISHED AGE pod-tgi-gemma-deployment-6bf9ffdc9b-zcfrh-84f19 lq cluster-queue True 8m23s
Para testar a substituição dos limites de quota, dimensione a implementação para quatro réplicas:
kubectl scale --replicas=4 deployment/tgi-gemma-deployment --namespace=llm
Execute o seguinte comando para ver o número de réplicas que o GKE implementa:
kubectl get workloads --namespace=llm
O resultado deve ser semelhante ao seguinte:
NAME QUEUE RESERVED IN ADMITTED FINISHED AGE pod-tgi-gemma-deployment-6cb95cc7f5-5thgr-3f7d4 lq cluster-queue True 14s pod-tgi-gemma-deployment-6cb95cc7f5-cbxg2-d9fe7 lq cluster-queue True 5m41s pod-tgi-gemma-deployment-6cb95cc7f5-tznkl-80f6b lq 13s pod-tgi-gemma-deployment-6cb95cc7f5-wd4q9-e4302 lq cluster-queue True 13s
A saída mostra que apenas três pods são admitidos devido à quota de recursos que o Kueue aplica.
Execute o seguinte comando para apresentar os pods no espaço de nomes
llm
:kubectl get pod --namespace=llm
O resultado deve ser semelhante ao seguinte:
NAME READY STATUS RESTARTS AGE tgi-gemma-deployment-7649884d64-6j256 1/1 Running 0 4m45s tgi-gemma-deployment-7649884d64-drpvc 0/1 SchedulingGated 0 7s tgi-gemma-deployment-7649884d64-thdkq 0/1 Pending 0 7s tgi-gemma-deployment-7649884d64-znvpb 0/1 Pending 0 7s
Agora, reduza a implementação para 1. Este passo é necessário antes de implementar a tarefa de ajuste fino. Caso contrário, não é admitida porque a tarefa de inferência tem prioridade.
kubectl scale --replicas=1 deployment/tgi-gemma-deployment --namespace=llm
Explicação do comportamento
O exemplo de escalabilidade resulta em apenas três réplicas (apesar da escalabilidade para quatro) devido ao limite de quota da GPU que definiu na configuração ClusterQueue. A secção spec.resourceGroups
de ClusterQueue define uma nominalQuota de "6" para nvidia.com/gpu
. A implementação especifica que cada pod requer "2" GPUs.
Por conseguinte, a ClusterQueue só pode acomodar um máximo de três réplicas da implementação de cada vez (uma vez que 3 réplicas * 2 GPUs por réplica = 6 GPUs, que é a quota total).
Quando tenta dimensionar para quatro réplicas, o Kueue reconhece que esta ação excederia a quota de GPUs e impede o agendamento da quarta réplica. Isto
é indicado pelo estado SchedulingGated
do quarto agrupamento. Este comportamento demonstra a aplicação da quota de recursos do Kueue.
Implemente a tarefa de preparação
Nesta secção, implementa uma tarefa de ajuste fino de prioridade inferior para um modelo Gemma 2 que requer quatro GPUs em dois pods. Um controlador de tarefas no Kubernetes cria um ou mais pods e garante que executam com êxito uma tarefa específica.
Esta tarefa vai usar a quota de GPU restante na ClusterQueue. A tarefa usa uma imagem pré-criada e guarda pontos de verificação para permitir o reinício a partir de resultados intermédios.
A tarefa de ajuste fino usa o conjunto de dados b-mc2/sql-create-context
. Pode encontrar a origem da tarefa de ajuste na repositório.
Veja o ficheiro
fine-tune-l4.yaml
. Este manifesto define a tarefa de ajuste preciso.Aplique o manifesto para criar a tarefa de ajuste fino:
cd ${EXAMPLE_HOME}/workloads sed -e "s/<MODEL_BUCKET>/$MODEL_BUCKET/g" \ -e "s/<PROJECT_ID>/$PROJECT_ID/g" \ -e "s/<REGION>/$REGION/g" \ fine-tune-l4.yaml |kubectl apply --filename=- --namespace=llm
Verifique se as implementações estão em execução. Para verificar o estado dos objetos Workload, execute o seguinte comando:
kubectl get workloads --namespace=llm
O resultado deve ser semelhante ao seguinte:
NAME QUEUE RESERVED IN ADMITTED FINISHED AGE job-finetune-gemma-l4-3316f lq cluster-queue True 29m pod-tgi-gemma-deployment-6cb95cc7f5-cbxg2-d9fe7 lq cluster-queue True 68m
Em seguida, veja os pods no espaço de nomes
llm
executando este comando:kubectl get pod --namespace=llm
O resultado deve ser semelhante ao seguinte:
NAME READY STATUS RESTARTS AGE finetune-gemma-l4-0-vcxpz 2/2 Running 0 31m finetune-gemma-l4-1-9ppt9 2/2 Running 0 31m tgi-gemma-deployment-6cb95cc7f5-cbxg2 1/1 Running 0 70m
O resultado mostra que o Kueue admite a execução do seu trabalho de ajuste preciso e dos pods do servidor de inferência, reservando os recursos corretos com base nos limites de quota especificados.
Veja os registos de saída para verificar se a tarefa de ajuste fino guarda pontos de verificação no contentor do Cloud Storage. A tarefa de ajuste preciso demora cerca de 10 minutos antes de começar a guardar o primeiro ponto de verificação.
kubectl logs --namespace=llm --follow --selector=app=finetune-job
O resultado do primeiro ponto de verificação guardado tem um aspeto semelhante ao seguinte:
{"name": "finetune", "thread": 133763559483200, "threadName": "MainThread", "processName": "MainProcess", "process": 33, "message": "Fine tuning started", "timestamp": 1731002351.0016131, "level": "INFO", "runtime": 451579.89835739136} … {"name": "accelerate.utils.fsdp_utils", "thread": 136658669348672, "threadName": "MainThread", "processName": "MainProcess", "process": 32, "message": "Saving model to /model-data/model-gemma2/experiment/checkpoint-10/pytorch_model_fsdp_0", "timestamp": 1731002386.1763802, "level": "INFO", "runtime": 486753.8924217224}
Teste a preemptividade e a atribuição dinâmica do Kueue na sua carga de trabalho mista
Nesta secção, simula um cenário em que a carga do servidor de inferência aumenta, o que requer o aumento da escala. Este cenário demonstra como o Kueue dá prioridade ao servidor de inferência de alta prioridade suspendendo e antecipando a tarefa de ajuste preciso de baixa prioridade quando os recursos estão limitados.
Execute o seguinte comando para dimensionar as réplicas do servidor de inferência para duas:
kubectl scale --replicas=2 deployment/tgi-gemma-deployment --namespace=llm
Verifique o estado dos objetos Workload:
kubectl get workloads --namespace=llm
O resultado tem um aspeto semelhante ao seguinte:
NAME QUEUE RESERVED IN ADMITTED FINISHED AGE job-finetune-gemma-l4-3316f lq False 32m pod-tgi-gemma-deployment-6cb95cc7f5-cbxg2-d9fe7 lq cluster-queue True 70m pod-tgi-gemma-deployment-6cb95cc7f5-p49sh-167de lq cluster-queue True 14s
O resultado mostra que a tarefa de ajuste fino já não é admitida porque as réplicas do servidor de inferência aumentadas estão a usar a quota de GPU disponível.
Verifique o estado da tarefa de ajuste:
kubectl get job --namespace=llm
O resultado tem um aspeto semelhante ao seguinte, o que indica que o estado da tarefa de ajuste preciso está agora suspenso:
NAME STATUS COMPLETIONS DURATION AGE finetune-gemma-l4 Suspended 0/2 33m
Execute o seguinte comando para inspecionar os seus pods:
kubectl get pod --namespace=llm
O resultado é semelhante ao seguinte, o que indica que o Kueue terminou os pods de tarefas de ajuste fino para libertar recursos para a implementação do servidor de inferência de prioridade mais elevada.
NAME READY STATUS RESTARTS AGE tgi-gemma-deployment-6cb95cc7f5-cbxg2 1/1 Running 0 72m tgi-gemma-deployment-6cb95cc7f5-p49sh 0/1 ContainerCreating 0 91s
Em seguida, teste o cenário em que a carga do servidor de inferência diminui e os respetivos pods são reduzidos. Execute o seguinte comando:
kubectl scale --replicas=1 deployment/tgi-gemma-deployment --namespace=llm
Execute o seguinte comando para apresentar os objetos Workload:
kubectl get workloads --namespace=llm
O resultado é semelhante ao seguinte, o que indica que uma das implementações do servidor de inferência foi terminada e que a tarefa de ajuste fino foi novamente admitida.
NAME QUEUE RESERVED IN ADMITTED FINISHED AGE job-finetune-gemma-l4-3316f lq cluster-queue True 37m pod-tgi-gemma-deployment-6cb95cc7f5-cbxg2-d9fe7 lq cluster-queue True 75m
Execute este comando para apresentar as tarefas:
kubectl get job --namespace=llm
O resultado tem um aspeto semelhante ao seguinte, o que indica que o trabalho de ajuste fino está a ser executado novamente, retomando a partir do ponto de verificação mais recente disponível.
NAME STATUS COMPLETIONS DURATION AGE finetune-gemma-l4 Running 0/2 2m11s 38m