Implantar um aplicativo Ray Serve com um modelo de difusão estável no Google Kubernetes Engine (GKE)

Implante e disponibilize um modelo de difusão estável no Google Kubernetes Engine (GKE) usando o Ray Serve e o complemento Ray Operator. Essa solução ajuda você a criar aplicativos de inferência de IA generativa escalonáveis e altamente disponíveis. O GKE oferece uma plataforma flexível e econômica para executar suas cargas de trabalho de IA/ML em contêineres. O Ray Serve oferece recursos como atualizações no local e upgrades sem tempo de inatividade. Esses recursos são essenciais para gerenciar modelos de IA distribuídos e manter uma disponibilização eficiente e confiável em ambientes de produção no GKE.

Sobre o Ray e o Ray Serve

O Ray é um framework de computação escalonável e de código aberto para aplicativos de IA/ML. O Ray Serve é uma biblioteca de disponibilização de modelos para Ray usado para dimensionar e exibir modelos em um ambiente distribuído. Para mais informações, consulte Ray Serve na documetação do Ray.

Use um recurso do RayCluster ou do RayService para implantar seu aplicativos do Ray Serve. Use um recurso do RayService na produção pelos seguintes motivos:

  • Atualizações no local para aplicativos do RayService
  • Upgrade sem inatividade para recursos do RayCluster
  • Aplicativos do Ray Serve altamente disponíveis

Objetivos

Este guia é destinado a clientes de IA generativa, usuários novos ou atuais do GKE, engenheiros de ML, engenheiros de MLOps (DevOps) ou administradores de plataformas interessados em usar os recursos de orquestração de contêineres do Kubernetes para disponibilizar modelos usando o Ray.

  • Criar um cluster do GKE com um pool de nós de GPU
  • Crie um cluster do Ray usando o recurso personalizado do RayCluster.
  • Execute um aplicativo do Ray Serve.
  • Implantar um recurso personalizado do RayService.

Custos

Neste documento, você vai usar os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços.

Novos usuários do Google Cloud podem estar qualificados para um teste sem custo financeiro.

Ao concluir as tarefas descritas neste documento, é possível evitar o faturamento contínuo excluindo os recursos criados. Para mais informações, consulte Limpeza.

Antes de começar

O Cloud Shell vem pré-instalado com o software necessário para este tutorial, incluindo kubectl e a CLI gcloud. Se você não usa o Cloud Shell, é necessário instalar a CLI gcloud.

  1. Faça login na sua conta do Google Cloud . Se você começou a usar o Google Cloud, crie uma conta para avaliar o desempenho de nossos produtos em situações reais. Clientes novos também recebem US$ 300 em créditos para executar, testar e implantar cargas de trabalho.
  2. Instale a CLI do Google Cloud.

  3. Ao usar um provedor de identidade (IdP) externo, primeiro faça login na CLI gcloud com sua identidade federada.

  4. Para inicializar a CLI gcloud, execute o seguinte comando:

    gcloud init
  5. Crie ou selecione um Google Cloud projeto.

    Funções necessárias para selecionar ou criar um projeto

    • Selecionar um projeto: não é necessário um papel específico do IAM para selecionar um projeto. Você pode escolher qualquer projeto em que tenha recebido um papel.
    • Criar um projeto: para criar um projeto, é necessário ter o papel de Criador de projetos (roles/resourcemanager.projectCreator), que contém a permissão resourcemanager.projects.create. Saiba como conceder papéis.
    • Crie um projeto do Google Cloud :

      gcloud projects create PROJECT_ID

      Substitua PROJECT_ID por um nome para o projeto Google Cloud que você está criando.

    • Selecione o projeto Google Cloud que você criou:

      gcloud config set project PROJECT_ID

      Substitua PROJECT_ID pelo nome do projeto do Google Cloud .

  6. Verifique se o faturamento está ativado para o projeto do Google Cloud .

  7. Ative a API GKE:

    Funções necessárias para ativar APIs

    Para ativar as APIs, é necessário ter o papel do IAM de administrador de uso do serviço (roles/serviceusage.serviceUsageAdmin), que contém a permissão serviceusage.services.enable. Saiba como conceder papéis.

    gcloud services enable container.googleapis.com
  8. Instale a CLI do Google Cloud.

  9. Ao usar um provedor de identidade (IdP) externo, primeiro faça login na CLI gcloud com sua identidade federada.

  10. Para inicializar a CLI gcloud, execute o seguinte comando:

    gcloud init
  11. Crie ou selecione um Google Cloud projeto.

    Funções necessárias para selecionar ou criar um projeto

    • Selecionar um projeto: não é necessário um papel específico do IAM para selecionar um projeto. Você pode escolher qualquer projeto em que tenha recebido um papel.
    • Criar um projeto: para criar um projeto, é necessário ter o papel de Criador de projetos (roles/resourcemanager.projectCreator), que contém a permissão resourcemanager.projects.create. Saiba como conceder papéis.
    • Crie um projeto do Google Cloud :

      gcloud projects create PROJECT_ID

      Substitua PROJECT_ID por um nome para o projeto Google Cloud que você está criando.

    • Selecione o projeto Google Cloud que você criou:

      gcloud config set project PROJECT_ID

      Substitua PROJECT_ID pelo nome do projeto do Google Cloud .

  12. Verifique se o faturamento está ativado para o projeto do Google Cloud .

  13. Ative a API GKE:

    Funções necessárias para ativar APIs

    Para ativar as APIs, é necessário ter o papel do IAM de administrador de uso do serviço (roles/serviceusage.serviceUsageAdmin), que contém a permissão serviceusage.services.enable. Saiba como conceder papéis.

    gcloud services enable container.googleapis.com
  14. Atribua papéis à sua conta de usuário. Execute uma vez o seguinte comando para cada um dos seguintes papéis do IAM: roles/container.clusterAdmin, roles/container.admin

    gcloud projects add-iam-policy-binding PROJECT_ID --member="user:USER_IDENTIFIER" --role=ROLE

    Substitua:

    • PROJECT_ID: o ID do projeto.
    • USER_IDENTIFIER: o identificador da sua conta de usuário . Por exemplo, myemail@example.com.
    • ROLE: o papel do IAM concedido à sua conta de usuário.

Preparar o ambiente

Para configurar o ambiente, siga estas etapas:

  1. Inicie uma sessão do Cloud Shell no console Google Cloud clicando em Ícone de ativação do Cloud Shell Ativar o Cloud Shell no consoleGoogle Cloud . Isso inicia uma sessão no painel inferior do console Google Cloud .

  2. Defina as variáveis de ambiente:

    export PROJECT_ID=PROJECT_ID
    export CLUSTER_NAME=rayserve-cluster
    export COMPUTE_REGION=us-central1
    export COMPUTE_ZONE=us-central1-c
    export CLUSTER_VERSION=CLUSTER_VERSION
    export TUTORIAL_HOME=`pwd`
    

    Substitua:

    • PROJECT_ID: o Google Cloud ID do projeto.
    • CLUSTER_VERSION: a versão do GKE a ser usada. Precisa ser 1.30.1 ou mais recente.
  3. Clone o repositório do GitHub:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  4. Mude para o diretório de trabalho:

    cd kubernetes-engine-samples/ai-ml/gke-ray/rayserve/stable-diffusion
    
  5. Crie um ambiente virtual em Python:

    venv

    python -m venv myenv && \
    source myenv/bin/activate
    

    Conda

    1. Instale o Conda.

    2. Execute os comandos a seguir:

      conda create -c conda-forge python=3.9.19 -n myenv && \
      conda activate myenv
      

    Ao implantar um aplicativo do Serve com serve run, o Ray espera que a versão do Python do cliente local corresponda à versão usada no cluster do Ray. A imagem rayproject/ray:2.37.0 usa o Python 3.9. Se você estiver executando uma versão diferente do cliente, selecione a imagem do Ray adequada.

  6. Instale as dependências necessárias para executar o aplicativo Serve:

    pip install ray[serve]==2.37.0
    pip install torch
    pip install requests
    

Criar um cluster com um pool de nós de GPU

Crie um cluster do GKE Autopilot ou Standard com um pool de nós de GPU:

Piloto automático

Criar um cluster do Autopilot:

gcloud container clusters create-auto ${CLUSTER_NAME}  \
    --enable-ray-operator \
    --cluster-version=${CLUSTER_VERSION} \
    --location=${COMPUTE_REGION}

Padrão

  1. Criar um cluster padrão

    gcloud container clusters create ${CLUSTER_NAME} \
        --addons=RayOperator \
        --cluster-version=${CLUSTER_VERSION}  \
        --machine-type=c3d-standard-8 \
        --location=${COMPUTE_ZONE} \
        --num-nodes=1
    
  2. Crie um pool de nós de GPU:

    gcloud container node-pools create gpu-pool \
        --cluster=${CLUSTER_NAME} \
        --machine-type=g2-standard-8 \
        --location=${COMPUTE_ZONE} \
        --num-nodes=1 \
        --accelerator type=nvidia-l4,count=1,gpu-driver-version=latest
    

Implantar um recurso do RayCluster

Para implantar um recurso do RayCluster:

  1. Analise o seguinte manifesto:

    apiVersion: ray.io/v1
    kind: RayCluster
    metadata:
      name: stable-diffusion-cluster
    spec:
      rayVersion: '2.37.0'
      headGroupSpec:
        rayStartParams:
          dashboard-host: '0.0.0.0'
        template:
          metadata:
          spec:
            containers:
            - name: ray-head
              image: rayproject/ray:2.37.0
              ports:
              - containerPort: 6379
                name: gcs
              - containerPort: 8265
                name: dashboard
              - containerPort: 10001
                name: client
              - containerPort: 8000
                name: serve
              resources:
                limits:
                  cpu: "2"
                  ephemeral-storage: "15Gi"
                  memory: "8Gi"
                requests:
                  cpu: "2"
                  ephemeral-storage: "15Gi"
                  memory: "8Gi"
            nodeSelector:
              cloud.google.com/machine-family: c3d
      workerGroupSpecs:
      - replicas: 1
        minReplicas: 1
        maxReplicas: 4
        groupName: gpu-group
        rayStartParams: {}
        template:
          spec:
            containers:
            - name: ray-worker
              image: rayproject/ray:2.37.0-gpu
              resources:
                limits:
                  cpu: 4
                  memory: "16Gi"
                  nvidia.com/gpu: 1
                requests:
                  cpu: 3
                  memory: "16Gi"
                  nvidia.com/gpu: 1
            nodeSelector:
              cloud.google.com/gke-accelerator: nvidia-l4

    Esse manifesto descreve um recurso do RayCluster.

  2. Aplique o manifesto ao cluster:

    kubectl apply -f ray-cluster.yaml
    
  3. Verifique se o recurso do RayCluster está pronto:

    kubectl get raycluster
    

    O resultado será assim:

    NAME                       DESIRED WORKERS   AVAILABLE WORKERS   CPUS   MEMORY   GPUS   STATUS   AGE
    stable-diffusion-cluster   2                 2                   6      20Gi     0      ready    33s
    

    Nesta saída, ready na coluna STATUS indica que o recurso do RayCluster está pronto.

Conectar-se ao recurso do RayCluster

Para se conectar ao recurso do RayCluster:

  1. Verifique se o GKE criou o serviço RayCluster:

    kubectl get svc stable-diffusion-cluster-head-svc
    

    O resultado será assim:

    NAME                             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                                AGE
    pytorch-mnist-cluster-head-svc   ClusterIP   34.118.238.247   <none>        10001/TCP,8265/TCP,6379/TCP,8080/TCP   109s
    
  2. Estabeleça sessões de encaminhamento de portas para o head do Ray:

    kubectl port-forward svc/stable-diffusion-cluster-head-svc 8265:8265 2>&1 >/dev/null &
    kubectl port-forward svc/stable-diffusion-cluster-head-svc 10001:10001 2>&1 >/dev/null &
    
  3. Verifique se o cliente Ray pode se conectar ao cluster usando localhost:

    ray list nodes --address http://localhost:8265
    

    O resultado será assim:

    ======== List: 2024-06-19 15:15:15.707336 ========
    Stats:
    ------------------------------
    Total: 3
    
    Table:
    ------------------------------
        NODE_ID                                                   NODE_IP     IS_HEAD_NODE    STATE    NODE_NAME    RESOURCES_TOTAL                 LABELS
    0  1d07447d7d124db641052a3443ed882f913510dbe866719ac36667d2  10.28.1.21  False           ALIVE    10.28.1.21   CPU: 2.0                        ray.io/node_id: 1d07447d7d124db641052a3443ed882f913510dbe866719ac36667d2
    # Several lines of output omitted
    

Executar um aplicativo do Ray Serve

Para executar um aplicativo do Ray Serve:

  1. Execute o aplicativo Stable Diffusion do Ray Serve:

    serve run stable_diffusion:entrypoint --working-dir=. --runtime-env-json='{"pip": ["torch", "torchvision", "diffusers==0.12.1", "huggingface_hub==0.25.2", "transformers", "fastapi==0.113.0"], "excludes": ["myenv"]}' --address ray://localhost:10001
    
    

    O resultado será assim:

    2024-06-19 18:20:58,444 INFO scripts.py:499 -- Running import path: 'stable_diffusion:entrypoint'.
    2024-06-19 18:20:59,730 INFO packaging.py:530 -- Creating a file package for local directory '.'.
    2024-06-19 18:21:04,833 INFO handle.py:126 -- Created DeploymentHandle 'hyil6u9f' for Deployment(name='StableDiffusionV2', app='default').
    2024-06-19 18:21:04,834 INFO handle.py:126 -- Created DeploymentHandle 'xo25rl4k' for Deployment(name='StableDiffusionV2', app='default').
    2024-06-19 18:21:04,836 INFO handle.py:126 -- Created DeploymentHandle '57x9u4fp' for Deployment(name='APIIngress', app='default').
    2024-06-19 18:21:04,836 INFO handle.py:126 -- Created DeploymentHandle 'xr6kt85t' for Deployment(name='StableDiffusionV2', app='default').
    2024-06-19 18:21:04,836 INFO handle.py:126 -- Created DeploymentHandle 'g54qagbz' for Deployment(name='APIIngress', app='default').
    2024-06-19 18:21:19,139 INFO handle.py:126 -- Created DeploymentHandle 'iwuz00mv' for Deployment(name='APIIngress', app='default').
    2024-06-19 18:21:19,139 INFO api.py:583 -- Deployed app 'default' successfully.
    
  2. Estabeleça uma sessão de encaminhamento de portas para a porta do Ray Serve (8000):

    kubectl port-forward svc/stable-diffusion-cluster-head-svc 8000:8000 2>&1 >/dev/null &
    
  3. Execute o script Python:

    python generate_image.py
    

    O script gera uma imagem para um arquivo chamado output.png. O resultado será assim:

    Uma praia ao pôr do sol. Imagem gerada pelo Stable Diffusion.

Implantar um RayService

O recurso personalizado do RayService gerencia o ciclo de vida de um recurso do RayCluster e do aplicativo do Ray Serve.

Para mais informações sobre o RayService, consulte Implantar aplicativos do Ray Serve e Guia de produção na documentação do Ray.

Para implantar um recurso do RayService, siga estas etapas:

  1. Analise o seguinte manifesto:

    apiVersion: ray.io/v1
    kind: RayService
    metadata:
      name: stable-diffusion
    spec:
      serveConfigV2: |
        applications:
          - name: stable_diffusion
            import_path: ai-ml.gke-ray.rayserve.stable-diffusion.stable_diffusion:entrypoint
            runtime_env:
              working_dir: "https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/archive/main.zip"
              pip: ["diffusers==0.12.1", "torch", "torchvision", "huggingface_hub==0.25.2", "transformers"]
      rayClusterConfig:
        rayVersion: '2.37.0'
        headGroupSpec:
          rayStartParams:
            dashboard-host: '0.0.0.0'
          template:
            spec:
              containers:
              - name: ray-head
                image:  rayproject/ray:2.37.0
                ports:
                - containerPort: 6379
                  name: gcs
                - containerPort: 8265
                  name: dashboard
                - containerPort: 10001
                  name: client
                - containerPort: 8000
                  name: serve
                resources:
                  limits:
                    cpu: "2"
                    ephemeral-storage: "15Gi"
                    memory: "8Gi"
                  requests:
                    cpu: "2"
                    ephemeral-storage: "15Gi"
                    memory: "8Gi"
              nodeSelector:
                cloud.google.com/machine-family: c3d
        workerGroupSpecs:
        - replicas: 1
          minReplicas: 1
          maxReplicas: 4
          groupName: gpu-group
          rayStartParams: {}
          template:
            spec:
              containers:
              - name: ray-worker
                image: rayproject/ray:2.37.0-gpu
                resources:
                  limits:
                    cpu: 4
                    memory: "16Gi"
                    nvidia.com/gpu: 1
                  requests:
                    cpu: 3
                    memory: "16Gi"
                    nvidia.com/gpu: 1
              nodeSelector:
                cloud.google.com/gke-accelerator: nvidia-l4

    Este manifesto descreve um recurso personalizado do RayService.

  2. Aplique o manifesto ao cluster:

    kubectl apply -f ray-service.yaml
    
  3. Verifique se o serviço está pronto:

    kubectl get svc stable-diffusion-serve-svc
    

    O resultado será assim:

    NAME                         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
    
    stable-diffusion-serve-svc   ClusterIP   34.118.236.0   <none>        8000/TCP   31m
    
  4. Configure o encaminhamento de portas para o serviço Ray Serve:

    kubectl port-forward svc/stable-diffusion-serve-svc 8000:8000 2>&1 >/dev/null &
    
  5. Execute o script Python da seção anterior:

    python generate_image.py
    

    O script gera uma imagem semelhante à imagem gerada na seção anterior.

Observar suas cargas de trabalho do Ray

Para conferir detalhes dos RayJobs, acesse a seção Kubernetes Engine > IA/ML > Jobs em Google Cloud console.

Ver RayJobs no Google Cloud console

Limpar

Excluir o projeto

    Excluir um projeto do Google Cloud :

    gcloud projects delete PROJECT_ID

Excluir recursos individuais

Para excluir o cluster, digite:

gcloud container clusters delete ${CLUSTER_NAME}

A seguir