Publique LLMs escaláveis no GKE com o TorchServe

Este tutorial mostra como implementar e publicar um modelo de aprendizagem automática (ML) escalável num cluster do Google Kubernetes Engine (GKE) através da estrutura TorchServe. Publica um modelo do PyTorch pré-preparado que gera previsões com base nas solicitações dos utilizadores. Depois de implementar o modelo, recebe um URL de previsão que a sua aplicação usa para enviar pedidos de previsão. Este método permite-lhe dimensionar o modelo e a aplicação Web de forma independente. Quando implementa a carga de trabalho e a aplicação de ML no Autopilot, o GKE escolhe o tipo e o tamanho de máquina subjacentes mais eficientes para executar as cargas de trabalho.

Este tutorial destina-se a engenheiros de aprendizagem automática (AA), administradores e operadores de plataformas, e a especialistas em dados e IA interessados em usar o GKE Autopilot para reduzir os custos administrativos da configuração, do dimensionamento e das atualizações dos nós. Para saber mais sobre as funções comuns e as tarefas de exemplo a que fazemos referência no Google Cloud conteúdo, consulte o artigo Funções e tarefas comuns de utilizadores do GKE.

Antes de ler esta página, certifique-se de que conhece o modo Autopilot do GKE.

Acerca da aplicação do tutorial

A aplicação é uma pequena aplicação Web Python criada com o framework Fast Dash. Usa a aplicação para enviar pedidos de previsão ao modelo T5. Esta aplicação captura as entradas de texto e os pares de idiomas do utilizador e envia as informações para o modelo. O modelo traduz o texto e devolve o resultado à aplicação, que apresenta o resultado ao utilizador. Para mais informações acerca do Fast Dash, consulte a documentação do Fast Dash.

Prepare o ambiente

Clone o repositório de exemplo e abra o diretório do tutorial:

git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git
cd kubernetes-engine-samples/ai-ml/t5-model-serving

Crie o cluster

Execute o seguinte comando:

gcloud container clusters create-auto ml-cluster \
    --release-channel=RELEASE_CHANNEL \
    --cluster-version=CLUSTER_VERSION \
    --location=us-central1

Substitua o seguinte:

  • RELEASE_CHANNEL: o canal de lançamento do seu cluster. Tem de ser um de rapid, regular ou stable. Escolha um canal com a versão 1.28.3-gke.1203000 ou posterior do GKE para usar GPUs L4. Para ver as versões disponíveis num canal específico, consulte o artigo Veja as versões predefinidas e disponíveis para canais de lançamento.
  • CLUSTER_VERSION: a versão do GKE a usar. Tem de ser 1.28.3-gke.1203000 ou posterior.

Esta operação demora vários minutos a ser concluída.

Crie um repositório do Artifact Registry

  1. Crie um novo repositório padrão do Artifact Registry com o formato Docker na mesma região que o seu cluster:

    gcloud artifacts repositories create models \
        --repository-format=docker \
        --location=us-central1 \
        --description="Repo for T5 serving image"
    
  2. Valide o nome do repositório:

    gcloud artifacts repositories describe models \
        --location=us-central1
    

    O resultado é semelhante ao seguinte:

    Encryption: Google-managed key
    Repository Size: 0.000MB
    createTime: '2023-06-14T15:48:35.267196Z'
    description: Repo for T5 serving image
    format: DOCKER
    mode: STANDARD_REPOSITORY
    name: projects/PROJECT_ID/locations/us-central1/repositories/models
    updateTime: '2023-06-14T15:48:35.267196Z'
    

Empacote o modelo

Nesta secção, vai criar um pacote do modelo e da framework de publicação numa única imagem de contentor com o Cloud Build e enviar a imagem resultante para o repositório do Artifact Registry.

  1. Reveja o Dockerfile da imagem de contentor:

    # Copyright 2023 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     https://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    ARG BASE_IMAGE=pytorch/torchserve:0.12.0-cpu
    
    FROM alpine/git
    
    ARG MODEL_NAME=t5-small
    ARG MODEL_REPO=https://huggingface.co/${MODEL_NAME}
    ENV MODEL_NAME=${MODEL_NAME}
    ENV MODEL_VERSION=${MODEL_VERSION}
    
    RUN git clone "${MODEL_REPO}" /model
    
    FROM ${BASE_IMAGE}
    
    ARG MODEL_NAME=t5-small
    ARG MODEL_VERSION=1.0
    ENV MODEL_NAME=${MODEL_NAME}
    ENV MODEL_VERSION=${MODEL_VERSION}
    
    COPY --from=0 /model/. /home/model-server/
    COPY handler.py \
         model.py \
         requirements.txt \
         setup_config.json /home/model-server/
    
    RUN  torch-model-archiver \
         --model-name="${MODEL_NAME}" \
         --version="${MODEL_VERSION}" \
         --model-file="model.py" \
         --serialized-file="pytorch_model.bin" \
         --handler="handler.py" \
         --extra-files="config.json,spiece.model,tokenizer.json,setup_config.json" \
         --runtime="python" \
         --export-path="model-store" \
         --requirements-file="requirements.txt"
    
    FROM ${BASE_IMAGE}
    
    ENV PATH /home/model-server/.local/bin:$PATH
    ENV TS_CONFIG_FILE /home/model-server/config.properties
    # CPU inference will throw a warning cuda warning (not error)
    # Could not load dynamic library 'libnvinfer_plugin.so.7'
    # This is expected behaviour. see: https://stackoverflow.com/a/61137388
    ENV TF_CPP_MIN_LOG_LEVEL 2
    
    COPY --from=1 /home/model-server/model-store/ /home/model-server/model-store
    COPY config.properties /home/model-server/
    

    Este Dockerfile define o seguinte processo de compilação de várias fases:

    1. Transfira os artefactos do modelo do repositório do Hugging Face.
    2. Empacote o modelo com a ferramenta PyTorch Serving Archive. Isto cria um ficheiro de arquivo do modelo (.mar) que o servidor de inferência usa para carregar o modelo.
    3. Crie a imagem final com o PyTorch Serve.
  2. Compile e envie a imagem através do Cloud Build:

    gcloud builds submit model/ \
        --region=us-central1 \
        --config=model/cloudbuild.yaml \
        --substitutions=_LOCATION=us-central1,_MACHINE=gpu,_MODEL_NAME=t5-small,_MODEL_VERSION=1.0
    

    O processo de compilação demora vários minutos a ser concluído. Se usar um tamanho do modelo superior a t5-small, o processo de compilação pode demorar significativamente mais tempo.

  3. Verifique se a imagem está no repositório:

    gcloud artifacts docker images list us-central1-docker.pkg.dev/PROJECT_ID/models
    

    Substitua PROJECT_ID pelo seu Google Cloud ID do projeto.

    O resultado é semelhante ao seguinte:

    IMAGE                                                     DIGEST         CREATE_TIME          UPDATE_TIME
    us-central1-docker.pkg.dev/PROJECT_ID/models/t5-small     sha256:0cd...  2023-06-14T12:06:38  2023-06-14T12:06:38
    

Implemente o modelo em pacote no GKE

Para implementar a imagem, este tutorial usa implementações do Kubernetes. 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.

Modifique o manifesto do Kubernetes no repositório de exemplo para corresponder ao seu ambiente.

  1. Reveja o manifesto da carga de trabalho de inferência:

    # Copyright 2023 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     https://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: t5-inference
      labels:
        model: t5
        version: v1.0
        machine: gpu
    spec:
      replicas: 1
      selector:
        matchLabels:
          model: t5
          version: v1.0
          machine: gpu
      template:
        metadata:
          labels:
            model: t5
            version: v1.0
            machine: gpu
        spec:
          nodeSelector:
            cloud.google.com/gke-accelerator: nvidia-l4
          securityContext:
            fsGroup: 1000
            runAsUser: 1000
            runAsGroup: 1000
          containers:
            - name: inference
              image: us-central1-docker.pkg.dev/PROJECT_ID/models/t5-small:1.0-gpu
              imagePullPolicy: IfNotPresent
              args: ["torchserve", "--start", "--foreground"]
              resources:
                limits:
                  nvidia.com/gpu: "1"
                  cpu: "3000m"
                  memory: 16Gi
                  ephemeral-storage: 10Gi
                requests:
                  nvidia.com/gpu: "1"
                  cpu: "3000m"
                  memory: 16Gi
                  ephemeral-storage: 10Gi
              ports:
                - containerPort: 8080
                  name: http
                - containerPort: 8081
                  name: management
                - containerPort: 8082
                  name: metrics
              readinessProbe:
                httpGet:
                  path: /ping
                  port: http
                initialDelaySeconds: 120
                failureThreshold: 10
              livenessProbe:
                httpGet:
                  path: /models/t5-small
                  port: management
                initialDelaySeconds: 150
                periodSeconds: 5
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: t5-inference
      labels:
        model: t5
        version: v1.0
        machine: gpu
    spec:
      type: ClusterIP
      selector:
        model: t5
        version: v1.0
        machine: gpu
      ports:
        - port: 8080
          name: http
          targetPort: http
        - port: 8081
          name: management
          targetPort: management
        - port: 8082
          name: metrics
          targetPort: metrics
    

  2. Substitua PROJECT_ID pelo seu Google Cloud ID do projeto:

    sed -i "s/PROJECT_ID/PROJECT_ID/g" "kubernetes/serving-gpu.yaml"
    

    Isto garante que o caminho da imagem do contentor na especificação de implementação corresponde ao caminho da imagem do modelo T5 no Artifact Registry.

  3. Crie os recursos do Kubernetes:

    kubectl create -f kubernetes/serving-gpu.yaml
    

Para verificar se o modelo foi implementado com êxito, faça o seguinte:

  1. Obtenha o estado da implementação e do serviço:

    kubectl get -f kubernetes/serving-gpu.yaml
    

    Aguarde até que o resultado mostre pods prontos, semelhantes ao seguinte. Consoante o tamanho da imagem, a primeira obtenção de imagens pode demorar vários minutos.

    NAME                            READY   UP-TO-DATE    AVAILABLE   AGE
    deployment.apps/t5-inference    1/1     1             0           66s
    
    NAME                    TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)                       AGE
    service/t5-inference    ClusterIP   10.48.131.86    <none>        8080/TCP,8081/TCP,8082/TCP    66s
    
  2. Abra uma porta local para o t5-inference serviço:

    kubectl port-forward svc/t5-inference 8080
    
  3. Abra uma nova janela de terminal e envie um pedido de teste ao serviço:

    curl -v -X POST -H 'Content-Type: application/json' -d '{"text": "this is a test sentence", "from": "en", "to": "fr"}' "http://localhost:8080/predictions/t5-small/1.0"
    

    Se o pedido de teste falhar e a ligação do Pod for fechada, verifique os registos:

    kubectl logs deployments/t5-inference
    

    Se o resultado for semelhante ao seguinte, o TorchServe não conseguiu instalar algumas dependências do modelo:

    org.pytorch.serve.archive.model.ModelException: Custom pip package installation failed for t5-small
    

    Para resolver este problema, reinicie a implementação:

    kubectl rollout restart deployment t5-inference
    

    O controlador de implementação cria um novo pod. Repita os passos anteriores para abrir uma porta no novo Pod.

Aceda ao modelo implementado através da aplicação Web

Para aceder ao modelo implementado com a aplicação Web Fast Dash, conclua os seguintes passos:

  1. Crie e envie a app Web Fast Dash como uma imagem de contentor no Artifact Registry:

    gcloud builds submit client-app/ \
        --region=us-central1 \
        --config=client-app/cloudbuild.yaml
    
  2. Abra kubernetes/application.yaml num editor de texto e substitua PROJECT_ID no campo image: pelo ID do seu projeto. Em alternativa, execute o seguinte comando:

    sed -i "s/PROJECT_ID/PROJECT_ID/g" "kubernetes/application.yaml"
    
  3. Crie os recursos do Kubernetes:

    kubectl create -f kubernetes/application.yaml
    

    A implementação e o serviço podem demorar algum tempo a serem totalmente aprovisionados.

  4. Para verificar o estado, execute o seguinte comando:

    kubectl get -f kubernetes/application.yaml
    

    Aguarde até que o resultado mostre Pods prontos, semelhante ao seguinte:

    NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
    deployment.apps/fastdash   1/1     1            0           1m
    
    NAME               TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
    service/fastdash   NodePort   203.0.113.12    <none>        8050/TCP         1m
    
  5. A aplicação Web está agora em execução, embora não esteja exposta num endereço IP externo. Para aceder à aplicação Web, abra uma porta local:

    kubectl port-forward service/fastdash 8050
    
  6. Num navegador, abra a interface Web:

    • Se estiver a usar uma shell local, abra um navegador e aceda a http://127.0.0.1:8050.
    • Se estiver a usar o Cloud Shell, clique em Pré-visualização Web e, de seguida, em Alterar porta. Especifique a porta 8050.
  7. Para enviar um pedido ao modelo T5, especifique valores nos campos TEXT, FROM LANG e TO LANG na interface Web e clique em Enviar. Para ver uma lista dos idiomas disponíveis, consulte a documentação do T5.

Ative o dimensionamento automático para o modelo

Esta secção mostra-lhe como ativar o dimensionamento automático para o modelo com base nas métricas do Google Cloud Managed Service for Prometheus fazendo o seguinte:

  1. Instale o adaptador do Stackdriver de métricas personalizadas
  2. Aplique configurações de PodMonitoring e HorizontalPodAutoscaling

O Google Cloud Managed Service for Prometheus está ativado por predefinição em clusters do Autopilot com a versão 1.25 e posteriores.

Instale o adaptador do Stackdriver de métricas personalizadas

Este adaptador permite que o cluster use métricas do Prometheus para tomar decisões de ajuste de escala automático do Kubernetes.

  1. Implemente o adaptador:

    kubectl create -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter_new_resource_model.yaml
    
  2. Crie uma conta de serviço do IAM para o adaptador usar:

    gcloud iam service-accounts create monitoring-viewer
    
  3. Conceda à conta de serviço de IAM a função monitoring.viewer no projeto e a função iam.workloadIdentityUser:

    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member "serviceAccount:monitoring-viewer@PROJECT_ID.iam.gserviceaccount.com" \
        --role roles/monitoring.viewer
    gcloud iam service-accounts add-iam-policy-binding monitoring-viewer@PROJECT_ID.iam.gserviceaccount.com \
        --role roles/iam.workloadIdentityUser \
        --member "serviceAccount:PROJECT_ID.svc.id.goog[custom-metrics/custom-metrics-stackdriver-adapter]"
    

    Substitua PROJECT_ID pelo seu Google Cloud ID do projeto.

  4. Anote a conta de serviço do Kubernetes do adaptador para lhe permitir roubar a identidade da conta de serviço do IAM:

    kubectl annotate serviceaccount custom-metrics-stackdriver-adapter \
        --namespace custom-metrics \
        iam.gke.io/gcp-service-account=monitoring-viewer@PROJECT_ID.iam.gserviceaccount.com
    
  5. Reinicie o adaptador para propagar as alterações:

    kubectl rollout restart deployment custom-metrics-stackdriver-adapter \
        --namespace=custom-metrics
    

Aplique configurações de PodMonitoring e HorizontalPodAutoscaling

O PodMonitoring é um recurso personalizado do Google Cloud Managed Service for Prometheus que permite a ingestão de métricas e a recolha de alvos num espaço de nomes específico.

  1. Implemente o recurso PodMonitoring no mesmo espaço de nomes que a implementação do TorchServe:

    kubectl apply -f kubernetes/pod-monitoring.yaml
    
  2. Reveja o manifesto HorizontalPodAutoscaler:

    # Copyright 2023 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     https://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    metadata:
      name: t5-inference
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: t5-inference
      minReplicas: 1
      maxReplicas: 5
      metrics:
      - type: Pods
        pods:
          metric:
            name: prometheus.googleapis.com|ts_queue_latency_microseconds|counter
          target:
            type: AverageValue
            averageValue: "30000"
    

    O HorizontalPodAutoscaler dimensiona a quantidade de pods do modelo T5 com base na duração cumulativa da fila de pedidos. A escala automática baseia-se na métrica ts_queue_latency_microseconds, que mostra a duração cumulativa da fila em microsegundos.

  3. Crie o HorizontalPodAutoscaler:

    kubectl apply -f kubernetes/hpa.yaml
    

Valide o dimensionamento automático com um gerador de carga

Para testar a configuração do ajuste de escala automático, gere carga para a aplicação de publicação. Este tutorial usa um gerador de carga do Locust para enviar pedidos para o ponto final de previsão do modelo.

  1. Crie o gerador de carga:

    kubectl apply -f kubernetes/loadgenerator.yaml
    

    Aguarde até que os pods do gerador de carga fiquem prontos.

  2. Exponha a interface Web do gerador de carga localmente:

    kubectl port-forward svc/loadgenerator 8080
    

    Se vir uma mensagem de erro, tente novamente quando o Pod estiver em funcionamento.

  3. Num navegador, abra a interface Web do gerador de carga:

    • Se estiver a usar uma shell local, abra um navegador e aceda a http://127.0.0.1:8080.
    • Se estiver a usar o Cloud Shell, clique em Pré-visualização Web e, de seguida, clique em Alterar porta. Introduza a porta 8080.
  4. Clique no separador Gráficos para observar o desempenho ao longo do tempo.

  5. Abra uma nova janela de terminal e monitorize a contagem de réplicas dos seus redimensionadores automáticos de pods horizontais:

    kubectl get hpa -w
    

    O número de réplicas aumenta à medida que a carga aumenta. O aumento da escala pode demorar cerca de dez minutos. À medida que são iniciadas novas réplicas, o número de pedidos bem-sucedidos no gráfico do Locust aumenta.

    NAME           REFERENCE                 TARGETS           MINPODS   MAXPODS   REPLICAS   AGE
    t5-inference   Deployment/t5-inference   71352001470m/7M   1         5        1           2m11s
    

Recomendações

  • Crie o modelo com a mesma versão da imagem Docker base que vai usar para a publicação.
  • Se o seu modelo tiver dependências de pacotes especiais ou se o tamanho das dependências for grande, crie uma versão personalizada da sua imagem Docker base.
  • Monitorize a versão de árvore dos pacotes de dependências do modelo. Certifique-se de que as dependências dos pacotes suportam as versões umas das outras. Por exemplo, a versão 2.0.3 do Panda suporta a versão 1.20.3 e posteriores do NumPy.
  • Execute modelos com utilização intensiva da GPU em nós de GPU e modelos com utilização intensiva da CPU na CPU. Isto pode melhorar a estabilidade da publicação de modelos e garante que está a consumir recursos de nós de forma eficiente.

Observe o desempenho do modelo

Para observar o desempenho do modelo, pode usar a integração do painel de controlo do TorchServe no Cloud Monitoring. Com este painel de controlo, pode ver métricas de desempenho críticas, como o débito de tokens, a latência de pedidos e as taxas de erro.

Para usar o painel de controlo do TorchServe, tem de ativar o Google Cloud Managed Service for Prometheus, que recolhe as métricas do TorchServe, no seu cluster do GKE. O TorchServe expõe métricas no formato Prometheus por predefinição; não precisa de instalar um exportador adicional.

Em seguida, pode ver as métricas através do painel de controlo do TorchServe. Para obter informações sobre a utilização do serviço gerido do Google Cloud para Prometheus para recolher métricas do seu modelo, consulte as orientações de observabilidade do TorchServe na documentação do Cloud Monitoring.