Cerrando esta serie de posts de ejecución de LLMs en local, llegamos para conocer a uno de los últimos actores que se ha unido a la moda de la ejecución de LLMs-IA: ¡Docker!

Que haya sido uno de los últimos en llegar no quiere decir que sea una plataforma que no se deba tener en cuenta debido a su historial de game-changer, sobre todo en lo que se refiere a ejecución de aplicaciones de forma transparente, revolucionando por completo el mundo del desarrollo.

Model Runner es la nueva herramienta que Docker ha liberado para la ejecución de modelos de IA de forma local, cuyas características principales veremos en este artículo.

A modo de recordatorio, por si te has perdido alguno de los artículos anteriores, puedes echarle un vistazo al resto de contenidos de la serie de ejecución de LLMs en local en los siguientes enlaces:

Funcionamiento y características

Docker Model Runner permite la ejecución de modelos de IA al incluir un motor de inferencia (construido sobre la librería llama.cpp) como parte del entorno de ejecución Docker. A alto nivel, la arquitectura está formada por 3 componentes:

Arquitectura Docker Model Runner
Arquitectura Docker Model Runner
  • Model distribution (almacenamiento de modelos y cliente): el model store es el componente principal de la arquitectura, en donde se guardan los ficheros de tensors. El client realiza operaciones (como por ejemplo la descarga) contra los OCI registries.
  • Model Runner: mapea las peticiones API a los procesos que tienen los motores de inferencia (/engines) y modelos (/models). Formado por componentes como el scheduler, loader y runner que coordinan la carga y descarga de los modelos de la memoria (los motores de inferencia y los modelos operan como procesos efímeros). Por cada combinación de motor de inferencia (por ejemplo, llama.cpp) y modelo (por ejemplo, ai/llama3.2:3B-Q4_0) se ejecuta un proceso diferente según las peticiones que se reciban en el API.
  • Model CLI: el componente principal de interacción del usuario. Esta pieza es un plugin CLI de Docker que proporciona una interfaz similar a la CLI de ejecución de imágenes Docker. Por debajo, la CLI se comunica con la API de Model Runner para la ejecución de casi todas sus operaciones.

Como nota importante a mencionar es que, a pesar de que la arquitectura general es la misma, en función de la plataforma en donde se despliegan, estos 3 componentes anteriores se empaquetan, guardan y ejecutan de distintas maneras (a veces se ejecutan en el host, otras en una máquina virtual y otras en un contenedor).

Algunas de las características principales de Docker Model Runner son:

  • Posibilidad de descargar y subir modelos de/a Docker Hub.
  • Ejecución de modelos exponiendo endpoints compatibles con el API de OpenAI.
  • Empaquetado de ficheros GGUF como artefactos OCI para publicarlos en cualquier Registry de contenedores.
  • Ejecución e interacción con los modelos directamente desde línea de comandos.
  • Gestión de los modelos locales.
  • Indicar los detalles de los prompts de entrada, así como las respuestas de los modelos.
  • Compatibilidad para la ejecución de interacciones multiturno con los modelos (chat).

Instalación

Model Runner está disponible para los principales sistemas operativos (Windows, MacOS y Linux), bien a través de Docker Desktop o Docker Engine. En este artículo se procederá a la ejecución de Docker Model Runner sobre el sistema operativo Ubuntu y Docker Engine.

Previamente instalado Docker Engine si fuera necesario, se procederá a instalar Model Runner ejecutando el siguiente comando:

sudo apt-get install docker-model-plugin
Instalación Model Runner
Instalación Model Runner

Comprobando dicha instalación con el comando:

docker model version
Comprobación de la instalación Model Runner
Comprobación de la instalación Model Runner

Comandos CLI

Una vez instalado Docker Model Runner, se puede interactuar con los modelos gracias a los siguientes comandos:

1 INSPECT

Comando que muestra la información detallada de un modelo.

docker model inspect ai/llama3.2:3B-Q4_0

docker model inspect ai/llama3.2:3B-Q4_0 --openai #Presentar la información en formato OpenAI
Comandos CLI: inspect docker model

2 LIST

Comando para listar los modelos descargados en el entorno local.

docker model list

docker model list --json #Listar los modelos en formato JSON

docker model list --openai #Listar los modelos en formato OpenAI

docker model list --quiet #Solo mostrar los ids de los modelos
Comandos cli: list

3 LOGS

Comando para exponer los logs.

docker model logs

docker model logs --follow #Ver los logs en tiempo real
Comandos cli: logs

4 PACKAGE

Comando para empaquetar un fichero en formato GGUF a un artefacto OCI de Docker Model.

docker model package --gguf <path> [--license <path>...] [--context-size <tokens>] [--push] MODEL

docker model package --gguf /home/simonrodriguez/dockerModelRunner/model.gguf my_new_llama_model

Las opciones disponibles para este comando son:

  • --chat-template: ruta absoluta al fichero de plantilla de chat (la plantilla debe estar en formato Jinja).
  • --context-size: tamaño de la context-window.
  • --gguf (obligatorio): ruta absoluta al fichero en formato gguf.
  • --license: ruta absoluta al fichero de licencia.
  • --push: subir al registry.
Comando cli: package

5 PULL

Comando para descargar un modelo de Docker Hub o Hugging Face.

docker model pull ai/llama3.2

docker model pull ai/llama3.2:3B-Q4_0 #Descarga del modelo indicando la cuantización

docker model pull ai/llama3.2 --ignore-runtime-memory-check #Opción para no bloquear la descarga si se estima que el modelo vaya a superar la memoria del sistema
comandos cli: pull

En la descarga desde Hugging Face, si no se especifica el tag se intentará descargar la versión Q4_K_M del modelo. Si esta versión no existe, se descargará el primer GGUF que se encuentre en la sección Files del modelo en Hugging Face. Para indicar la cuantización del modelo, simplemente se tiene que añadir el tag correspondiente.

6 PUSH

Comando para subir un modelo a Docker Hub.

docker model push ai/llama3.3

7 RM

Comando para eliminar los modelos en local.

docker model rm ai/llama3.2:3B-Q4_0

docker model rm ai/llama3.2:3B-Q4_0 --force #Forzar la eliminación del modelo
comandos cli: RM

8 RUN

Comando para ejecutar un modelo e interactuar con él enviando un prompt o a través del modo chat.

docker model run ai/llama3.2:3B-Q4_0 #Se abre un prompt para un chat interactivo del que se sale con el comando /bye

docker model run ai/llama3.2:3B-Q4_0 "Hola, ¿que me puedes decir de Docker Model Runner?"

docker model run ai/llama3.2:3B-Q4_0 --debug #Habilita el modo debug

docker model run ai/llama3.2:3B-Q4_0 --ignore-runtime-memory-check #Opción para no bloquear la descarga si se estima que el modelo vaya a superar la memoria del sistema
Comando cli: run
comando cli: run

Cuando se ejecuta un modelo Docker, llama al endpoint API del servidor de inferencia alojado por Model Runner. El modelo se quedará en memoria hasta que se cargue otro modelo o se alcance el timeout de inactividad.

9 PS

Comando que muestra los modelos que se están ejecutando.

docker model ps
comando cli: ps

10 UNLOAD

Comando para descargar un modelo que se está ejecutando.

docker model unload ai/llama3.2:3B-Q4_0
comandos cli: unload

11 DF

Comando que muestra el espacio en disco que ocupan los modelos.

docker model df
comando cli: DF

12 STATUS

Comando para revisar si se está ejecutando Docker Model Runner.

docker model status

docker model status --json #Exponer la información en formato JSON
comando cli: status

13 TAG

Comando para crear un tag concreto de un modelo.

docker model tag ai/llama3.2:3B-Q4_0 modelo-cuantizado
comando cli: tag

Si no se indica el tag, el valor por defecto es latest.

14 VERSION

Comando para revisar qué versión de Docker Model Runner se tiene en el sistema.

docker model version
Comando cli: version

API

Una vez habilitado Model Runner, se exponen automáticamente endpoints API (endpoints propios de Docker Model Runner y endpoints compatibles con OpenAI) que se pueden emplear para interactuar con los modelos de forma programática.

Para realizar las llamadas al API expuesta, se debe tener en cuenta cuál es el origen de la llamada:

  • Desde otros contenedores: peticiones a la dirección http://172.17.0.1:12434/. Es posible que esta interfaz no esté disponible para poder ser llamada desde los contenedores. Si este es el caso, se deberá incluir la instrucción extra_hosts en el fichero de configuración del Docker Compose:
extra_hosts:
  - "model-runner.docker.internal:host-gateway"

Con esta instrucción anterior, se podrá acceder al API a través de la dirección http://model-runner.docker.internal:12434/

  • Desde el host: peticiones a la dirección http://localhost:12434/

Endpoints propios

Los endpoints disponibles son los siguientes:

  • /models/create (POST): endpoint para descargar un modelo.
endpoints propios: /models/create
endpoints propios: /models/create
  • /models (GET): endpoint para listar los modelos existentes en el sistema con su información.
endpoints propios: /models (get)
  • /models/{namespace}/{name} (GET): endpoint para mostrar la información de un modelo.
endpoints propios: /models/{namespace}/{name} (get)
  • /models/{namespace}/{name} (DELETE): endpoint para borrar un modelo local.
/models/{namespace}/{name} (DELETE)

Endpoints compatibles con OpenAI

Los endpoints expuestos son:

  • /engines/llama.cpp/v1/models (GET): endpoint para listar los modelos disponibles en el sistema.
/engines/llama.cpp/v1/models (GET)
  • /engines/llama.cpp/v1/models/{namespace}/{name} (GET): endpoint para exponer información de un modelo.
/engines/llama.cpp/v1/models/{namespace}/{name} (GET)
  • /engines/llama.cpp/v1/chat/completions (POST): endpoint para enviar una interacción de chat en la que se recibe la respuesta del asistente. Se pueden indicar múltiples parámetros como temperature, stream, seed, etc
/engines/llama.cpp/v1/chat/completions (POST)
  • /engines/llama.cpp/v1/completions (POST): respuesta del modelo al input del usuario. Este endpoint ya está deprecado por OpenAI.
/engines/llama.cpp/v1/completions (POST)
  • /engines/llama.cpp/v1/embeddings (POST): endpoint para obtener los embeddings de un texto.
/engines/llama.cpp/v1/embeddings (POST)

Como en estos momentos solo un motor de inferencia (engine) está soportado (llama.cpp), se puede omitir esta parte en las urls anteriores (por ejemplo, de /engines/llama.cpp/v1/models se pasaría a /engines/v1/models).

Omisión en urls anteriores

Docker Compose

Docker Compose permite definir los modelos como piezas básicas de tu aplicación de forma que se puedan declarar junto a los servicios y, así, ejecutar la aplicación en cualquier plataforma compatible con la especificación Compose. Para poder ejecutar los modelos en Docker Compose es necesario disponer como mínimo de la versión 2.38.0 de la utilidad, además de una plataforma que soporte los modelos en Compose como Docker Model Runner.

Para el uso de modelos en Docker Compose se ha creado el elemento models, con el que se puede:

  • Declarar los modelos de IA que la aplicación necesita.
  • Indicar las configuraciones y requisitos que necesita el modelo.
  • Hacer la aplicación portable a diferentes plataformas.
  • Permitir que la plataforma sea la que maneje el ciclo de vida del modelo.

Las opciones de configuración del elemento models son:

  • model (obligatorio): el identificador del artefacto OCI para el modelo. Es lo que se va a descargar y ejecutar en el Model Runner.
  • context_size: indica el valor máximo de la context-window para el modelo.
  • runtime_flags: listado de parámetros que se le pasan al motor de inferencia cuando se inicia el modelo. Por ejemplo, para el motor de inferencia llama.cpp, los parámetros a informar se pueden encontrar en este enlace.
  • x-*: propiedades extensibles para opciones concretas de cada plataforma.

Un ejemplo sencillo de definición de models podría ser:

models:
  llm:
    model: ai/llama3.2:3B-Q4_0
    context_size: 4096
    runtime_flags:
      - "--temp"                # Temperature
      - "0.1"
      - "--top-p"               # Top-p sampling
      - "0.9"

Los servicios pueden referenciar a los modelos de dos formas:

  • Forma corta: la forma más sencilla. Con esta forma, la plataforma genera automáticamente variables de entorno basadas en el nombre del modelo:
    • LLM_URL: url para acceder al modelo LLM.
    • LLM_MODEL: identificador para el modelo LLM.
    • EMBEDDING_MODEL_URL: url para acceder al embedding-model.
    • EMBEDDING_MODEL_MODEL: identificador del modelo para el embedding-model.
services:
  app:
    image: my-app
    models:
      - llm
      - embedding-model

models:
  llm:
    model: ai/llama3.2:3B-Q4_0
  embedding-model:
    model: ai/embeddinggemma
  • Forma larga: con esta configuración se le indica al servicio:
    • AI_MODEL_URL y AI_MODEL_NAME para el modelo LLM.
    • EMBEDDING_URL y EMBEDDING_NAME para el embedding-model.
services:
  app:
    image: my-app
    models:
      llm:
        endpoint_var: AI_MODEL_URL
        model_var: AI_MODEL_NAME
      embedding-model:
        endpoint_var: EMBEDDING_URL
        model_var: EMBEDDING_NAME

models:
  llm:
    model: ai/llama3.2:3B-Q4_0
  embedding-model:
    model: ai/embeddinggemma

Aquí tienes algunas configuraciones para casos de uso concretos del elemento models de Docker Compose.

Demo

Para ver este nuevo elemento models de Docker Compose en acción, creamos una aplicación sencilla para interactuar con un LLM. Para la aplicación se usarán los siguientes componentes:

  • Java 21
  • Spring Boot 3.4.4 (Con el soporte por defecto de Buildpacks para la creación de imágenes Docker de aplicaciones)
  • Spring AI
  • Maven 3.8.5
  • Docker version 28.4.0
  • Docker Model Runner 0.1.40
  • Docker Compose 2.39.4

La aplicación únicamente expone un endpoint /chat que recibe el input del usuario y lo envía al LLM correspondiente.

Demo Models
Demo Models

Aquí se puede descargar el código de la aplicación de ejemplo y el fichero README con los pasos a seguir para ejecutarla.

Conclusiones

En este último post de la serie se ha visto cómo poder ejecutar LLMs con Docker y la facilidad que nos proporciona para poder integrarlo con nuestras aplicaciones gracias a la integración con Docker Compose.

A lo largo de esta serie de posts centrados en la ejecución de LLMs en local hemos visto las distintas herramientas más extendidas con sus particularidades, pero en donde todas ellas ofrecen las funcionalidades más básicas como los comandos y endpoints API de interacción con los modelos. Actualmente, tal vez se pueda destacar Ollama sobre las demás en cuanto a número de opciones que ofrece, así como de una personalización de modelos más avanzada.

Con lo visto principalmente con Ollama y Docker, ¿podremos ver en algún momento del futuro próximo la ejecución de modelos personalizados de IA (contenerizados o no) en el cloud al lado de nuestros microservicios? Solo el tiempo lo dirá.

Referencias

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.