--- title: "Cómo desplegar TensorFlow Serving con Docker en Ubuntu 24.04" description: "Sirve modelos TensorFlow en producción con TensorFlow Serving y Docker en Ubuntu 24.04: exporta un SavedModel, consulta la API REST y gestiona varias versiones." category: "Infraestructura de IA y ML" language: es source: https://cubepath.com/es/docs/infraestructura-de-ia-y-ml/instalacion-y-configuracion-de-tensorflow-serving updated: 2026-09-25 --- # Cómo desplegar TensorFlow Serving con Docker en Ubuntu 24.04 TensorFlow Serving es el servidor de inferencia de TensorFlow para producción: carga modelos en formato SavedModel, los expone mediante APIs REST y gRPC y detecta nuevas versiones en disco sin reiniciar. En este tutorial lo desplegarás con la imagen oficial de Docker en Ubuntu 24.04, exportarás un modelo de ejemplo, lo consultarás por REST y configurarás el servidor para mantener dos versiones del modelo activas a la vez. Al final verás cómo habilitar el agrupamiento de peticiones y la variante con GPU. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS de 64 bits en arquitectura x86_64, por ejemplo un VPS de CubePath, con al menos 2 GB de RAM. La imagen oficial `tensorflow/serving` solo se publica para x86_64. - Un usuario no root con privilegios `sudo` y que pertenezca al grupo `docker`. - Docker Engine instalado desde el repositorio oficial de Docker. - Unos 5 GB libres en disco para las imágenes de TensorFlow y TensorFlow Serving. ## Paso 1: Descargar la imagen de TensorFlow Serving La forma recomendada de ejecutar TensorFlow Serving es la imagen oficial de Docker Hub, que incluye el binario `tensorflow_model_server` ya compilado. Descárgala: ```bash docker pull tensorflow/serving ``` Comprueba que la imagen está disponible y qué versión del servidor contiene: ```bash docker run --rm --entrypoint tensorflow_model_server tensorflow/serving --version ``` ```output TensorFlow ModelServer: 2.19.0 TensorFlow Library: 2.19.0 ``` ## Paso 2: Exportar un modelo en formato SavedModel TensorFlow Serving solo carga modelos en formato SavedModel y espera una estructura de directorios concreta: una carpeta por modelo y, dentro, una subcarpeta numérica por versión. Para un modelo llamado `half_plus_two`, la versión 1 debe quedar así: - `~/models/half_plus_two/1/saved_model.pb`: el grafo y las firmas del modelo. - `~/models/half_plus_two/1/variables/`: los pesos. El servidor ignora los directorios cuyo nombre no es un número, y trata el número como versión del modelo. Para no depender de un modelo entrenado, exportarás un modelo trivial que calcula `y = x * 0.5 + 2`. Así podrás verificar que las predicciones son exactas. Crea el directorio de trabajo y el script de exportación: ```bash mkdir -p ~/models ~/tfs nano ~/tfs/exportar.py ``` ```python import sys import tensorflow as tf class HalfPlusTwo(tf.Module): def __init__(self, offset): super().__init__() self.offset = tf.Variable(offset, dtype=tf.float32) @tf.function(input_signature=[tf.TensorSpec(shape=[None], dtype=tf.float32, name="x")]) def __call__(self, x): return {"y": x * 0.5 + self.offset} destino = sys.argv[1] offset = float(sys.argv[2]) modelo = HalfPlusTwo(offset) tf.saved_model.save( modelo, destino, signatures={"serving_default": modelo.__call__}, ) print(f"Modelo exportado en {destino} con offset {offset}") ``` La firma `serving_default` es la que TensorFlow Serving usa por defecto; define una entrada `x` (vector de floats) y una salida `y`. Con un modelo de Keras 3 obtendrías lo mismo con `model.export("ruta/1")`. El script recibe dos argumentos: el directorio de destino y el offset. En lugar de instalar TensorFlow en el servidor, ejecútalo con la imagen oficial `tensorflow/tensorflow`. La opción `-u` hace que los archivos creados pertenezcan a tu usuario: ```bash docker run --rm -u "$(id -u):$(id -g)" \ -v "$HOME/models:/models" \ -v "$HOME/tfs:/tfs:ro" \ tensorflow/tensorflow python /tfs/exportar.py /models/half_plus_two/1 2.0 ``` ```output Modelo exportado en /models/half_plus_two/1 con offset 2.0 ``` Inspecciona la firma del modelo con `saved_model_cli`, incluido en la misma imagen. Es la forma de saber qué nombres, tipos y formas espera el servidor: ```bash docker run --rm -v "$HOME/models:/models:ro" tensorflow/tensorflow \ saved_model_cli show --dir /models/half_plus_two/1 --tag_set serve --signature_def serving_default ``` ```output The given SavedModel SignatureDef contains the following input(s): inputs['x'] tensor_info: dtype: DT_FLOAT shape: (-1) name: serving_default_x:0 The given SavedModel SignatureDef contains the following output(s): outputs['y'] tensor_info: dtype: DT_FLOAT shape: (-1) name: StatefulPartitionedCall:0 Method name is: tensorflow/serving/predict ``` ## Paso 3: Arrancar TensorFlow Serving La imagen arranca el servidor con el modelo indicado en la variable `MODEL_NAME`, que busca en `/models/`. El puerto 8501 es la API REST y el 8500 la API gRPC. Publícalos solo en `127.0.0.1`, porque los puertos publicados por Docker se saltan las reglas de UFW: ```bash docker run -d --name tfserving --restart unless-stopped \ -p 127.0.0.1:8500:8500 \ -p 127.0.0.1:8501:8501 \ -v "$HOME/models/half_plus_two:/models/half_plus_two:ro" \ -e MODEL_NAME=half_plus_two \ tensorflow/serving ``` Revisa el log de arranque: ```bash docker logs tfserving 2>&1 | tail -n 5 ``` ```output ... Successfully loaded servable version {name: half_plus_two version: 1} ... Running gRPC ModelServer at 0.0.0.0:8500 ... ... Exporting HTTP/REST API at:localhost:8501 ... ``` Consulta el estado del modelo a través de la API: ```bash curl http://127.0.0.1:8501/v1/models/half_plus_two ``` ```output { "model_version_status": [ { "version": "1", "state": "AVAILABLE", "status": { "error_code": "OK", "error_message": "" } } ] } ``` El estado `AVAILABLE` indica que la versión 1 está cargada y lista. ## Paso 4: Hacer predicciones con la API REST El endpoint de predicción es `/v1/models/:predict`. El formato `instances` envía una lista de ejemplos; como el modelo tiene una sola entrada, cada elemento es un valor de `x`: ```bash curl -X POST http://127.0.0.1:8501/v1/models/half_plus_two:predict \ -d '{"instances": [1.0, 2.0, 5.0]}' ``` ```output { "predictions": [2.5, 3.0, 4.5] } ``` Los resultados coinciden con `x * 0.5 + 2`. Si el modelo tuviera varias entradas, cada instancia sería un objeto con una clave por nombre de entrada, por ejemplo `{"x": 1.0, "z": 3.0}`. Desde Python basta con cualquier cliente HTTP. Por ejemplo, con `requests`: ```python import requests respuesta = requests.post( "http://127.0.0.1:8501/v1/models/half_plus_two:predict", json={"instances": [1.0, 2.0, 5.0]}, timeout=10, ) respuesta.raise_for_status() print(respuesta.json()["predictions"]) ``` La API gRPC del puerto 8500 ofrece menor latencia para cargas altas. Sus clientes usan el paquete de Python `tensorflow-serving-api` y los mismos nombres de entrada y salida que muestra `saved_model_cli`. ## Paso 5: Publicar una nueva versión del modelo TensorFlow Serving vigila el directorio del modelo y, por defecto, sirve siempre la versión con el número más alto. Si el servidor detecta el directorio de una versión mientras aún se está escribiendo, puede intentar cargar un modelo incompleto. Por eso conviene exportar primero a un directorio de preparación y moverlo después a su número definitivo: `mv` dentro del mismo sistema de archivos es atómico. Exporta una versión con un offset distinto en `~/models/staging`: ```bash mkdir -p ~/models/staging docker run --rm -u "$(id -u):$(id -g)" \ -v "$HOME/models:/models" \ -v "$HOME/tfs:/tfs:ro" \ tensorflow/tensorflow python /tfs/exportar.py /models/staging/half_plus_two 3.0 ``` Publícala como versión 2: ```bash mv ~/models/staging/half_plus_two ~/models/half_plus_two/2 ``` En unos segundos el servidor la detecta, la carga y retira la versión 1. Compruébalo: ```bash curl -s -X POST http://127.0.0.1:8501/v1/models/half_plus_two:predict \ -d '{"instances": [1.0]}' ``` ```output { "predictions": [3.5] } ``` El resultado es ahora `1.0 * 0.5 + 3`. Con `docker logs tfserving` verás las líneas de carga de la versión 2 y de descarga de la versión 1. ## Paso 6: Servir varias versiones con un archivo de configuración Para comparar versiones o hacer un despliegue gradual necesitas que ambas estén cargadas a la vez. Eso se configura con un archivo de configuración de modelos en formato texto de protobuf. Créalo dentro de `~/models`: ```bash nano ~/models/models.config ``` ```text model_config_list { config { name: "half_plus_two" base_path: "/models/half_plus_two" model_platform: "tensorflow" model_version_policy { specific { versions: 1 versions: 2 } } } } ``` La política `specific` indica exactamente qué versiones cargar. Puedes añadir más bloques `config` para servir varios modelos distintos desde el mismo servidor. Recrea el contenedor montando todo `~/models` y pasando el archivo al servidor. `--model_config_file_poll_wait_seconds=60` hace que relea el archivo cada minuto, de modo que los cambios se aplican sin reiniciar: ```bash docker rm -f tfserving docker run -d --name tfserving --restart unless-stopped \ -p 127.0.0.1:8500:8500 \ -p 127.0.0.1:8501:8501 \ -v "$HOME/models:/models:ro" \ tensorflow/serving \ --model_config_file=/models/models.config \ --model_config_file_poll_wait_seconds=60 ``` Comprueba que ambas versiones están disponibles: ```bash curl -s http://127.0.0.1:8501/v1/models/half_plus_two | grep -E '"version"|"state"' ``` ```output "version": "2", "state": "AVAILABLE", "version": "1", "state": "AVAILABLE", ``` Ahora puedes dirigir cada petición a una versión concreta con la ruta `/versions/`: ```bash curl -s -X POST http://127.0.0.1:8501/v1/models/half_plus_two/versions/1:predict -d '{"instances": [1.0]}' curl -s -X POST http://127.0.0.1:8501/v1/models/half_plus_two/versions/2:predict -d '{"instances": [1.0]}' ``` La primera devuelve `2.5` y la segunda `3.5`. Las peticiones sin versión van a la más alta de las cargadas. ## Paso 7: Habilitar el agrupamiento de peticiones Con muchas peticiones pequeñas y simultáneas, agrupar varias en un único lote aprovecha mucho mejor la CPU y, sobre todo, la GPU. Crea el archivo de parámetros: ```bash nano ~/models/batching.config ``` ```text max_batch_size { value: 32 } batch_timeout_micros { value: 2000 } num_batch_threads { value: 4 } max_enqueued_batches { value: 100 } ``` - `max_batch_size`: número máximo de ejemplos por lote. - `batch_timeout_micros`: tiempo máximo que una petición espera a que se llene el lote. Es la latencia extra que aceptas a cambio de rendimiento. - `num_batch_threads`: lotes procesados en paralelo; un valor cercano al número de núcleos es un buen punto de partida. Recrea el contenedor con `--enable_batching`: ```bash docker rm -f tfserving docker run -d --name tfserving --restart unless-stopped \ -p 127.0.0.1:8500:8500 \ -p 127.0.0.1:8501:8501 \ -v "$HOME/models:/models:ro" \ tensorflow/serving \ --model_config_file=/models/models.config \ --model_config_file_poll_wait_seconds=60 \ --enable_batching=true \ --batching_parameters_file=/models/batching.config ``` Repite la petición del paso 6 para confirmar que el servidor sigue respondiendo. Ajusta los valores midiendo latencia y rendimiento con tu tráfico real. ## Paso 8: Usar una GPU NVIDIA (opcional) Si el servidor tiene una GPU NVIDIA, usa la imagen `tensorflow/serving:latest-gpu`. Necesitas el driver de NVIDIA (`nvidia-smi` debe funcionar en el host) y el NVIDIA Container Toolkit configurado para Docker. Con ambos instalados, añade `--gpus all` al comando: ```bash docker rm -f tfserving docker run -d --name tfserving --restart unless-stopped --gpus all \ -p 127.0.0.1:8500:8500 \ -p 127.0.0.1:8501:8501 \ -v "$HOME/models:/models:ro" \ tensorflow/serving:latest-gpu \ --model_config_file=/models/models.config ``` En el log de arranque (`docker logs tfserving`) debe aparecer una línea `Created device /job:localhost/replica:0/task:0/device:GPU:0` con el nombre de la tarjeta. ## Solución de problemas **El log muestra `No versions of servable half_plus_two found under base path`.** El directorio montado no tiene la estructura `//saved_model.pb`. Revisa la ruta con `ls -R ~/models/half_plus_two` y que el volumen de `docker run` apunta al directorio del modelo, no al de la versión. **La predicción devuelve un error de forma o de tipo.** Los datos no coinciden con la firma. Compara la petición con la salida de `saved_model_cli show` del paso 2: nombres de entrada, `dtype` y número de dimensiones. **`curl: (7) Failed to connect to 127.0.0.1 port 8501`.** El contenedor no está en marcha o ha fallado al cargar la configuración. Revisa `docker ps -a` y `docker logs tfserving`; un error de sintaxis en `models.config` impide arrancar el servidor. ## Conclusión Tienes TensorFlow Serving funcionando en Docker sobre Ubuntu 24.04, con un modelo servido por REST y gRPC, despliegue de nuevas versiones sin reinicio, varias versiones activas mediante un archivo de configuración y agrupamiento de peticiones. Como siguientes pasos, coloca un proxy inverso con TLS y autenticación delante del puerto 8501 si otros servicios deben consultarlo desde fuera, recoge métricas del servidor con `--monitoring_config_file` y automatiza la exportación de modelos desde tu pipeline de entrenamiento.