Ejecutar inferencias de LLMs en GPUs de Cloud Run con Gemma 3 y Ollama

Configurar gcloud

Para configurar Google Cloud CLI en tu servicio de Cloud Run, sigue estos pasos:

  1. Configura tu proyecto predeterminado:

    gcloud config set project PROJECT_ID

    Haz clic en el icono para sustituir la variable PROJECT_ID por el nombre del proyecto que has creado en este tutorial. De esta forma, todos los fichas de esta página que hagan referencia a PROJECT_ID tendrán el valor correcto ya rellenado.

  2. Configura la CLI de Google Cloud para que use la región europe-west1 en los comandos de Cloud Run.

    gcloud config set run/region europe-west1

Usar Docker para crear una imagen de contenedor con Ollama y Gemma

  1. Crea un directorio para el servicio Ollama y cambia tu directorio de trabajo a este nuevo directorio:

    mkdir ollama-backend
    cd ollama-backend
  2. Crea un archivo Dockerfile con el siguiente contenido:

    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"]
    

Almacenar los pesos del modelo en la imagen de contenedor para que las instancias se inicien más rápido

Google recomienda almacenar los pesos del modelo de Gemma 3 (4B) y de modelos de tamaño similar directamente en la imagen del contenedor.

Los pesos del modelo son los parámetros numéricos que definen el comportamiento de un LLM. Ollama debe leer estos archivos por completo y cargar los pesos en la memoria de la GPU (VRAM) durante el inicio de la instancia del contenedor para poder empezar a atender solicitudes de inferencia.

En Cloud Run, es importante que las instancias de contenedor se inicien rápidamente para minimizar la latencia de las solicitudes. Si la instancia de contenedor tarda en iniciarse, el servicio tardará más en pasar de cero a una instancia y necesitará más tiempo para escalar horizontalmente durante un pico de tráfico.

Para que el inicio sea rápido, almacena los archivos del modelo en la propia imagen de contenedor. Este método es más rápido y fiable que descargar los archivos desde una ubicación remota durante el inicio. El almacenamiento interno de imágenes de contenedor de Cloud Run está optimizado para gestionar picos de tráfico, lo que le permite configurar rápidamente el sistema de archivos del contenedor cuando se inicia una instancia.

Ten en cuenta que los pesos del modelo de Gemma 3 (4B) ocupan 8 GB de almacenamiento. Los modelos más grandes tienen archivos de peso de modelo más grandes, por lo que puede que no sea práctico almacenarlos en la imagen del contenedor. Consulta el artículo Prácticas recomendadas: inferencia de IA en Cloud Run con GPUs para ver un resumen de las ventajas y desventajas.

Compilar y desplegar el servicio de Cloud Run

Crea y despliega el servicio en 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

Ten en cuenta las siguientes marcas importantes de este comando:

  • --concurrency 4 se define para que coincida con el valor de la variable de entorno OLLAMA_NUM_PARALLEL.
  • --gpu 1 con --gpu-type nvidia-l4 asigna 1 GPU NVIDIA L4 a cada instancia de Cloud Run del servicio.
  • --max-instances 1 especifica el número máximo de instancias al que se puede escalar. Debe ser igual o inferior a la cuota de GPU NVIDIA L4 (Total Nvidia L4 GPU allocation, per project per region) de tu proyecto.
  • --no-allow-unauthenticated restringe el acceso no autenticado al servicio. Si mantienes el servicio privado, puedes usar la autenticación Gestión de Identidades y Accesos (IAM) integrada de Cloud Run para la comunicación entre servicios. Consulta el artículo Gestionar el acceso con la gestión de identidades y accesos.
  • --no-cpu-throttling es necesario para habilitar la GPU.
  • --no-gpu-zonal-redundancy Define las opciones de redundancia de zona en función de tus requisitos de conmutación por error de zona y de la cuota disponible. Para obtener más información, consulta las opciones de redundancia zonal de GPU.

Configurar la simultaneidad para obtener un rendimiento óptimo

En esta sección se explica el contexto de los ajustes de simultaneidad recomendados. Para que la latencia de las solicitudes sea óptima, asegúrate de que el ajuste --concurrency sea igual a la variable de entorno OLLAMA_NUM_PARALLEL de Ollama.

  • OLLAMA_NUM_PARALLEL determina cuántas ranuras de solicitud están disponibles por modelo para gestionar las solicitudes de inferencia simultáneamente.
  • --concurrency determina cuántas solicitudes envía Cloud Run a una instancia de Ollama al mismo tiempo.

Si --concurrency supera a OLLAMA_NUM_PARALLEL, Cloud Run puede enviar más solicitudes a un modelo de Ollama de las que tiene disponibles. Esto provoca que las solicitudes se pongan en cola en Ollama, lo que aumenta la latencia de las solicitudes en cola. También provoca que el autoescalado sea menos eficaz, ya que las solicitudes en cola no activan el escalado horizontal de Cloud Run ni el inicio de nuevas instancias.

Ollama también permite servir varios modelos desde una GPU. Para evitar por completo que se pongan en cola las solicitudes en la instancia de Ollama, debes definir --concurrency para que coincida con OLLAMA_NUM_PARALLEL.

Es importante tener en cuenta que, al aumentar OLLAMA_NUM_PARALLEL, las solicitudes paralelas también tardan más.

Optimizar el uso

Para conseguir una utilización de la GPU óptima, aumenta --concurrency y mantenlo dentro del doble del valor de OLLAMA_NUM_PARALLEL. Aunque esto provoca que las solicitudes se pongan en cola en Ollama, puede ayudar a mejorar la utilización: las instancias de Ollama pueden procesar inmediatamente las solicitudes de su cola y las colas ayudan a absorber los picos de tráfico.

Probar el servicio Ollama desplegado con curl

Ahora que has desplegado el servicio Ollama, puedes enviarle solicitudes. Sin embargo, si envías una solicitud directamente, Cloud Run responde con HTTP 401 Unauthorized. Esto es intencional, ya que una API de inferencia de LLM está diseñada para que la llamen otros servicios, como una aplicación frontend. Para obtener más información sobre la autenticación de servicio a servicio en Cloud Run, consulta el artículo Autenticación de servicio a servicio.

Para enviar solicitudes al servicio Ollama, añade un encabezado con un token OIDC válido a las solicitudes. Por ejemplo, puedes usar el proxy de desarrollador de Cloud Run:

  1. Inicia el proxy y, cuando se te pida que instales el componente cloud-run-proxy, elige Y:

    gcloud run services proxy ollama-gemma --port=9090
  2. Envía una solicitud en otra pestaña de la terminal y deja el proxy en ejecución. Ten en cuenta que el proxy se ejecuta en localhost:9090:

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

    Este comando debería proporcionar una salida de streaming similar a la siguiente:

    {"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}
    ...