Executar inferência de LLM em GPUs do Cloud Run com Gemma 3 e Ollama

Configurar a gcloud

Para configurar a CLI do Google Cloud para seu serviço do Cloud Run:

  1. Defina seu projeto padrão:

    gcloud config set project PROJECT_ID

    Clique no ícone . para substituir a variável PROJECT_ID pelo nome do projeto que você criou para este tutorial. Isso garante que todas as listagens nesta página com referência a PROJECT_ID tenham o valor correto preenchido.

  2. Configure a CLI do Google Cloud para usar a região europe-west1 nos comandos do Cloud Run.

    gcloud config set run/region europe-west1

Use o Docker para criar uma imagem de contêiner com o Ollama e o Gemma

  1. Crie um diretório para o serviço Ollama e mude o diretório existente para esse novo diretório:

    mkdir ollama-backend
    cd ollama-backend
  2. Crie um arquivo Dockerfile com o seguinte conteúdo:

    FROM ollama/ollama:latest
    
    # Listen on all interfaces, port 8080
    ENV OLLAMA_HOST 0.0.0.0:8080
    
    # Store model weight files in /models
    ENV OLLAMA_MODELS /models
    
    # Reduce logging verbosity
    ENV OLLAMA_DEBUG false
    
    # Never unload model weights from the GPU
    ENV OLLAMA_KEEP_ALIVE -1
    
    # Store the model weights in the container image
    ENV MODEL gemma3:4b
    RUN ollama serve & sleep 5 && ollama pull $MODEL
    
    # Start Ollama
    ENTRYPOINT ["ollama", "serve"]
    

Armazene pesos do modelo na imagem do contêiner para inicializações de instância mais rápidas

O Google recomenda armazenar os pesos do modelo para Gemma 3 (4B) e modelos de tamanho semelhante diretamente na imagem do contêiner.

Os pesos do modelo são os parâmetros numéricos que definem o comportamento de um LLM. O Ollama precisa ler esses arquivos completamente e carregar os pesos na memória da GPU (VRAM) durante a inicialização da instância do contêiner, antes que ela possa começar a atender a solicitações de inferência.

No Cloud Run, uma inicialização rápida de instância de contêiner é importante para minimizar latência de solicitação. Se a instância do contêiner tiver um tempo de inicialização lento, o serviço leva mais tempo para escalonar de zero a uma instância e precisa de mais tempo para escalonar durante um pico de tráfego.

Para garantir uma inicialização rápida, armazene os arquivos do modelo na própria imagem do contêiner. Isso é mais rápido e confiável do que fazer o download dos arquivos de um local remoto durante a inicialização. O armazenamento interno de imagens de contêineres do Cloud Run é otimizado para lidar com picos de tráfego, permitindo que ele configure rapidamente o sistema de arquivos do contêiner quando uma instância é iniciada.

Os pesos do modelo do Gemma 3 (4B) ocupam 8 GB de armazenamento. Modelos maiores têm arquivos de peso de modelo maiores, e eles podem ser impraticáveis para armazenar na imagem do contêiner. Consulte Práticas recomendadas: inferência de IA no Cloud Run com GPUs para ter uma visão geral das compensações.

Criar e implantar o serviço do Cloud Run

Crie e implante o serviço no Cloud Run:

gcloud run deploy ollama-gemma \
  --source . \
  --concurrency 4 \
  --cpu 8 \
  --set-env-vars OLLAMA_NUM_PARALLEL=4 \
  --gpu 1 \
  --gpu-type nvidia-l4 \
  --max-instances 1 \
  --memory 32Gi \
  --no-allow-unauthenticated \
  --no-cpu-throttling \
  --no-gpu-zonal-redundancy \
  --timeout=600

Observe as seguintes flags importantes neste comando:

  • --concurrency 4 está definido para corresponder ao valor da variável de ambiente OLLAMA_NUM_PARALLEL.
  • --gpu 1 com --gpu-type nvidia-l4 atribui uma GPU NVIDIA L4 a cada instância do Cloud Run no serviço.
  • --max-instances 1 especifica o número máximo de instâncias para escalonar. Ela precisa ser igual ou menor que a cota de GPU NVIDIA L4 (Total Nvidia L4 GPU allocation, per project per region) do seu projeto.
  • --no-allow-unauthenticated restringe o acesso não autenticado ao serviço. Ao manter o serviço privado, você pode confiar na API integrada do Cloud Run Autenticação do Identity and Access Management (IAM) para comunicação de serviço a serviço. Consulte Como gerenciar o acesso usando o IAM.
  • --no-cpu-throttling é necessário para ativar a GPU.
  • --no-gpu-zonal-redundancy defina opções de redundância zonal dependendo dos requisitos de failover zonal e da cota disponível. Consulte Opções de redundância zonal de GPU para mais detalhes.

Como definir a simultaneidade para performance ideal

Nesta seção, fornecemos contexto sobre as configurações de simultaneidade recomendadas. Para uma latência de solicitação ideal, verifique se a configuração --concurrency é igual à variável de ambiente OLLAMA_NUM_PARALLEL do Ollama.

  • OLLAMA_NUM_PARALLEL determina quantos slots de solicitação estão disponíveis para cada modelo para processar solicitações de inferência simultaneamente.
  • O --concurrency determina quantas solicitações o Cloud Run envia para uma instância do Ollama ao mesmo tempo.

Se --concurrency exceder OLLAMA_NUM_PARALLEL, o Cloud Run poderá enviar mais solicitações a um modelo em Ollama do que os slots disponíveis para ele. Isso leva à formação de filas de solicitações no Ollama, aumentando a latência das solicitações na fila. Isso também leva a um escalonamento automático menos responsivo, já que as solicitações em fila não acionam o Cloud Run para escalonar horizontalmente e iniciar novas instâncias.

O Ollama também oferece suporte à exibição de vários modelos de uma GPU. Para evitar completamente a fila de solicitações na instância do Ollama, defina --concurrency para corresponder a OLLAMA_NUM_PARALLEL.

É importante observar que aumentar OLLAMA_NUM_PARALLEL também faz com que as solicitações paralelas demorem mais.

Otimização da utilização

Para uma utilização ideal da GPU, aumente --concurrency, mantendo-o dentro do dobro do valor de OLLAMA_NUM_PARALLEL. Embora isso leve à fila de solicitações no Ollama, ele pode ajudar a melhorar a utilização: as instâncias do Ollama podem processar imediatamente as solicitações da fila, e as filas ajudam a absorver picos de tráfego.

Testar o serviço Ollama implantado com curl

Agora que você implantou o serviço Ollama, é possível enviar solicitações para ele. No entanto, se você enviar uma solicitação diretamente, o Cloud Run responderá com HTTP 401 Unauthorized. Isso é intencional, porque uma API de inferência de LLM é destinada a outros serviços para chamada, como um aplicativo de front-end. Para mais informações sobre serviços no Cloud Run, consulte Como autenticar serviço a serviço.

Para enviar solicitações ao serviço do Ollama, adicione um cabeçalho com um token OIDC válido às solicitações, por exemplo, usando o proxy de desenvolvedor do Cloud Run:

  1. Inicie o proxy e, quando solicitado a instalar o componente cloud-run-proxy, escolha Y:

    gcloud run services proxy ollama-gemma --port=9090
  2. Envie uma solicitação para ele em uma guia de terminal separada, deixando o proxy em execução. Observe que o proxy é executado em localhost:9090:

    curl http://localhost:9090/api/generate -d '{
      "model": "gemma3:4b",
      "prompt": "Why is the sky blue?"
    }'

    O comando vai fornecer uma saída de streaming semelhante a esta:

    {"model":"gemma3:4b","created_at":"2025-03-10T03:02:18.641492408Z","response":"That","done":false}
    {"model":"gemma3:4b","created_at":"2025-03-10T03:02:18.687529153Z","response":"'","done":false}
    {"model":"gemma3:4b","created_at":"2025-03-10T03:02:18.753284927Z","response":"s","done":false}
    {"model":"gemma3:4b","created_at":"2025-03-10T03:02:18.812957381Z","response":" a","done":false}
    {"model":"gemma3:4b","created_at":"2025-03-10T03:02:18.889102649Z","response":" fantastic","done":false}
    {"model":"gemma3:4b","created_at":"2025-03-10T03:02:18.925748116Z","response":",","done":false}
    {"model":"gemma3:4b","created_at":"2025-03-10T03:02:18.958391572Z","response":" decept","done":false}
    {"model":"gemma3:4b","created_at":"2025-03-10T03:02:18.971035028Z","response":"ively","done":false}
    {"model":"gemma3:4b","created_at":"2025-03-10T03:02:18.989678484Z","response":" tricky","done":false}
    {"model":"gemma3:4b","created_at":"2025-03-10T03:02:18.999321940Z","response":" question","done":false}
    ...