Cómo Testcontainers agiliza el desarrollo de aplicaciones impulsadas por IA
En el panorama empresarial actual, acelerado y cada vez más competitivo, las organizaciones deben encontrar formas de agilizar la finalización de sus proyectos de transformación digital. Minimiza los retrasos en el despliegue de nuevas aplicaciones o funciones, y podrás aprovechar sus beneficios de productividad y eficiencia en menos tiempo, obteniendo una ventaja sobre tus competidores. Sin embargo, prolongar el desarrollo de nuevo software o el lanzamiento de actualizaciones puede erosionar tu cuota de mercado, ya que otras empresas de tu sector innovan más rápido.
Los contenedores han surgido como la tecnología de referencia para las empresas que buscan optimizar sus prácticas de desarrollo de aplicaciones, permitiendo a los desarrolladores crear aplicaciones más seguras, robustas y escalables, en menos tiempo que nunca.
En este artículo, inspirado en esta charla de Oleg Šelajev en un reciente Unstructured Data Meetup organizado por Zilliz, exploramos el concepto de contenerización y una de sus herramientas esenciales, Docker, y cómo reducen la complejidad del proceso de desarrollo de aplicaciones. Luego centraremos nuestra atención en un framework de código abierto, Testcontainers, que amplía considerablemente las capacidades de Docker, permitiendo a los desarrolladores optimizar el desarrollo de aplicaciones impulsadas por IA.
¿Qué son los contenedores?
Los contenedores son paquetes estandarizados de código de aplicación que contienen todos los componentes requeridos, incluidos binarios, dependencias, bibliotecas y archivos de configuración, para ejecutarse en cualquier entorno. Tradicionalmente, las aplicaciones eran monolíticas, con diferentes componentes dentro de una sola base de código. Hoy en día, los desarrolladores dividen el software en componentes según su función o servicio, cada uno con su base de código y dependencias: contenerización o arquitectura de microservicios.
Los beneficios de los contenedores incluyen:
Mayor agilidad empresarial: dividir las aplicaciones en servicios permite a los desarrolladores trabajar en diferentes partes del proyecto de forma simultánea. De manera similar, la modularidad de los contenedores permite actualizaciones y nuevas funciones sin modificar toda la aplicación.
Escalabilidad mejorada: si una aplicación puede empaquetarse dentro de un contenedor, solo necesitas desplegar un contenedor adicional para crear otra instancia. Mientras tanto, en aplicaciones modulares, los equipos pueden simplemente poner en marcha nuevos contenedores para el servicio que se acerca a su capacidad máxima.
Mayor eficiencia: debido a que los contenedores son altamente portátiles e independientes de la plataforma, los desarrolladores no tienen que dedicar tiempo a configurar y mantener diferentes entornos.
Menores costos: liberar a los desarrolladores de la responsabilidad de gestionar servidores y entornos los hace más productivos en otras áreas, es decir, ¡desarrollando aplicaciones de verdad! Esto reduce las horas-hombre necesarias para completar proyectos, disminuyendo su costo.
Los casos de uso de los contenedores incluyen:
Desarrollo de aplicaciones nativas de la nube: las arquitecturas contenerizadas aprovechan plenamente las capacidades de la nube, incluida una escalabilidad mejorada, mayor acceso a los datos y una ciberseguridad más sólida.
Modernización de aplicaciones: los contenedores pueden utilizarse para refactorizar aplicaciones heredadas con el fin de aumentar su compatibilidad con un entorno de nube, haciéndolas más eficientes, con mejor rendimiento y más fáciles de actualizar.
Creación de prototipos: debido a que son independientes de la plataforma, los contenedores evitan la necesidad de configurar diferentes entornos para desarrollo, pruebas, staging, etc., lo que facilita a los desarrolladores experimentar con nuevas funciones.
Dispositivos de Internet de las cosas (IoT): al ser ligeros y eficientes en recursos, los contenedores proporcionan un mecanismo ideal de despliegue de software para hardware IoT con recursos limitados.
- DevOps: los contenedores se utilizan para crear entornos consistentes y aislados, garantizando que una aplicación se ejecute igual sin importar dónde se despliegue, lo que resulta en un despliegue más rápido y una colaboración mejorada entre los equipos de desarrollo y operaciones.
¿Qué es Docker?
Docker es una plataforma que permite a los desarrolladores crear, desplegar, configurar y gestionar contenedores. Docker es tan sinónimo de contenedores, de hecho, que con frecuencia se los denomina “contenedores Docker. En realidad, sin embargo, cuando las personas mencionan Docker, en realidad se refieren al motor Docker: el entorno de ejecución sobre el cual se construyen y ejecutan los contenedores. Este motor se sitúa debajo de los contenedores para virtualizar el sistema operativo (SO) del dispositivo host y asignarles la cantidad óptima de recursos, como CPU y memoria.
Esto contrasta notablemente con una solución similar en máquinas virtuales (VMs), que requieren su propio SO y recursos computacionales. Además de hacer que los contenedores sean agnósticos al entorno, el uso de Docker los hace intensivos en recursos, más rápidos de iniciar y apagar, y con mayor portabilidad que las VMs.
Herramientas de Docker ampliamente utilizadas
Tan potente como es Docker, su popularidad se debe en gran parte a su rico ecosistema de herramientas y bibliotecas asociadas. Estas son algunas de las herramientas de Docker más destacadas.
Docker Enterprise: una versión de Docker diseñada para despliegues críticos, que ofrece funciones avanzadas de seguridad, gestión y orquestación para lanzamientos certificados y de nivel de producción.
Docker Swarm: una herramienta de agrupación y orquestación para Docker que permite a los usuarios gestionar múltiples nodos Docker como un único sistema virtual, permite un escalado y balanceo de carga más sencillos.
Docker Compose: una herramienta para optimizar el desarrollo y la gestión de aplicaciones Docker complejas de múltiples contenedores.
Kubernetes: Una plataforma de orquestación de contenedores de código abierto que automatiza el despliegue, escalado y gestión de grandes aplicaciones contenerizadas en clústeres de servidores.
Puedes obtener más información sobre el ecosistema Docker en este artículo https://middleware.io/blog/understanding-the-docker-ecosystem/
Testcontainers: Gestión simplificada de dependencias
Testcontainers es un framework de código abierto que proporciona instancias ligeras y modulares de bases de datos, navegadores, brokers de mensajes y otras dependencias preconfiguradas que pueden ejecutarse en un contenedor. Al poner a disposición del usuario una biblioteca de APIs programáticas para manipular contenedores, Testcontainers abstrae los detalles complicados de cada dependencia, facilitando el trabajo con ellas. Esto reduce el costo operativo de los proyectos, a la vez que fomenta la experimentación y facilita un desarrollo optimizado.
Una selección de las más de 50 integraciones modulares de Testcontainers
Como su nombre sugiere, Testcontainers ofrece los mayores aumentos de productividad cuando se trata de pruebas de aplicaciones, incluidas las pruebas de capa de acceso a datos, integración y aceptación.
Los beneficios de realizar pruebas de aplicaciones con Testcontainers incluyen:
Aislamiento y consistencia: contenerizar componentes garantiza que cada prueba se ejecute en un estado nuevo y conocido. Por ejemplo, probar bases de datos de forma aislada elimina las inconsistencias que surgen de las diferentes configuraciones de bases de datos locales, mientras que los navegadores contenerizados permiten pruebas de interfaz de usuario más confiables al remediar las variaciones causadas por las configuraciones de navegadores locales.
Paridad de entornos: los desarrolladores pueden llevar a cabo pruebas de extremo a extremo en un entorno que se asemeja estrechamente a producción, reduciendo las discrepancias entre entornos y aumentando la precisión de los resultados de las pruebas.
Gestión de recursos: como los contenedores tienen tiempos de ejecución cortos, puedes gestionar los recursos de forma más eficiente, a la vez que habilitas una ejecución y eliminación rápidas de las pruebas.
Escalabilidad y fiabilidad: en consecuencia, las pruebas pueden escalarse fácilmente sin tener que considerar los recursos disponibles.
Además de hacer que las pruebas de aplicaciones sean más fáciles y efectivas, Testcontainers puede ayudarte a desarrollar aplicaciones de IA independientes del entorno. Dirijamos ahora nuestra atención a algunas de las otras formas clave en que Testcontainers aumenta la productividad.
Ejecutar una instancia de Milvus
Como primer ejemplo de cómo usar Testcontainers para aumentar la productividad, creemos una instancia de Milvus, como se muestra en el ejemplo de código a continuación (todos los ejemplos de código de este artículo están escritos en Java, pero la biblioteca Testcontainers también está disponible en Python, Node.js Go, Rust y varios otros lenguajes):
import io.milvus.client.MilvusServiceClient;
import io.milvus.param.ConnectParam;
import org.testcontainers.milvus.MilvusContainer;
public class MilvusExample {
public static void main(String[] args) {
MilvusContainer milvus = new MilvusContainer("milvusdb/milvus:v2.3.9");
milvus.start();
MilvusServiceClient milvusClient = new MilvusServiceClient(
ConnectParam.newBuilder().withUri(milvus.getEndpoint()).build());
}
}
En lugar de tener que instalar y configurar una instancia separada de Milvus e integrarla con tu aplicación, Testcontainers proporciona una abstracción fácil de usar que simplemente invoca la instancia desde dentro del código.
Primero, creamos una nueva instancia de un contenedor de Milvus, especificando la versión al pasar la imagen de contenedor adecuada como parámetro. Además de agilizar el desarrollo de aplicaciones, esto también facilita la actualización de las aplicaciones en el futuro, ya que solo tienes que especificar la imagen actualizada. A partir de ahí, ejecutamos el contenedor usando el método .start() y luego pasamos el contenedor como parámetro a una nueva instancia de un Milvus Service Client que te permite interactuar con el servicio dentro del contenedor.
Además de crear una instancia de Milvus en apenas unas pocas líneas de código, este enfoque garantiza que el componente tampoco quede nunca desincronizado con los requisitos de la aplicación, porque lo invoca la propia aplicación.
Integrar Ollama y HuggingFace para la integración de LLM
Una segunda forma en que Testcontainers mejora la productividad es agilizando la integración de componentes de IA, como los LLM, dentro de las aplicaciones. Esto te permite realizar fácilmente llamadas a LLM desde dentro del código para añadir funcionalidad de LLM a las aplicaciones y, de igual importancia, convierte los componentes de IA en una parte normal del proceso DevOps.
En el fragmento de código a continuación, creamos una clase OllamaHuggingFaceContainer, que extiende el OllamaContainer existente para lanzar una instancia que ejecuta un contenedor de Ollama que ejecuta un modelo de aprendizaje profundo descargado de HuggingFace.
Ollama es una plataforma que te permite servir modelos para inferencia localmente en tu dispositivo, mientras que HuggingFace es un centro indispensable para descargar modelos de IA de código abierto. Al combinar los dos recursos, obtienes acceso a las docenas de modelos compatibles con Ollama, así como a cualquier modelo en GGUF (GPT-Generated Unified Format), de los cuales hay 23.000 en HuggingFace.
package org.testcontainers.huggingface;
import com.github.dockerjava.api.command.InspectContainerResponse;
import org.testcontainers.containers.ContainerLaunchException;
import org.testcontainers.images.builder.ImageFromDockerfile;
import org.testcontainers.ollama.OllamaContainer;
import org.testcontainers.utility.DockerImageName;
import java.io.IOException;
public class OllamaHuggingFaceContainer extends OllamaContainer {
private final HuggingFaceModel huggingFaceModel;
public OllamaHuggingFaceContainer(HuggingFaceModel model) {
super(DockerImageName.parse("ollama/ollama:0.1.44"));
this.huggingFaceModel = model;
}
Despliegue automatizado de modelos de IA
Posteriormente, dado que Testcontainers permite la invocación de modelos de IA desde dentro de la aplicación, también presenta una forma más conveniente y automatizada de distribuir esos modelos. Si bien los modelos de código abierto se obtienen fácilmente de HuggingFace, se almacenan como una serie de archivos y directorios en repositorios git. Esto no solo es demasiado complicado para los no iniciados, sino que también está lejos de ser la forma más conveniente para que los desarrolladores experimentados trabajen con modelos.
Un repositorio típico de HuggingFace
En lugar de configurar y desplegar un modelo manualmente, puedes automatizar el proceso con un script. Esto, de nuevo, reduce la carga administrativa para los desarrolladores a la vez que añade otro nivel de abstracción para el propio modelo. Esto se ejemplifica en el código siguiente (que es un fragmento de un script de pruebas más largo), que muestra cómo se puede especificar y configurar un modelo desde dentro del código. Además de especificar el directorio desde el cual se extraerá el modelo y el modelo exacto que se utilizará, la variable modelfiledefine una plantilla de prompt.
public class HuggingFaceVisionModelTest {
@Test
public void visionModelWithHuggingFace() throws IOException {
String imageName = "vision-model-from-HuggingFace";
String repository = "xtuner/llava-phi-3-mini-gguf";
String model = "llava-phi-3-mini-int4.gguf";
String visionAdapter = "llava-phi-3-mini-mmproj-f16.gguf";
String modelfile = """
FROM ./llava-phi-3-mini-int4.gguf
FROM ./llava-phi-3-mini-mmproj-f16.gguf
TEMPLATE """{{ if .System }}<|system|>
{{ .System }}<|end|>
{{ end }}{{ if .Prompt }}<|user|>
{{ .Prompt }}<|end|>
{{ end }}<|assistant|>
{{ .Response }}<|end|>
"""
PARAMETER stop "<|user|>"
PARAMETER stop "<|assistant|>"
PARAMETER stop "<|system|>"
PARAMETER stop "<|end|>"
PARAMETER stop "<|endoftext|>"
PARAMETER num_keep 4
PARAMETER num_ctx 4096
""";
try (
OllamaContainer ollama = new OllamaContainer(DockerImageName.parse(imageName).asCompatibleSubstituteFor("ollama/ollama:0.1.44")).withReuse(true)
) {
try {
ollama.start();
} catch (ContainerFetchException ex) {
createImage(imageName, repository, model, modelfile, visionAdapter);
ollama.start();
}
Como se mencionó anteriormente, esto se puede aplicar a cualquier modelo en Ollama, así como al gran número disponible en formato GGUF. Desplegar un modelo de esta manera también facilita la actualización del modelo de IA en futuras iteraciones de tu aplicación, ya que no tendrás que hacer cambios en el entorno, solo en las referencias al modelo en el código.
Conclusión
En resumen:
Los contenedores son paquetes estandarizados de código de aplicación que contienen todos los componentes necesarios, incluidos binarios, dependencias, bibliotecas y archivos de configuración, para ejecutarse en cualquier entorno.
Los beneficios de los contenedores incluyen:
Mayor agilidad empresarial
Escalabilidad mejorada
Mayor eficiencia
Costos más bajos
Los casos de uso de los contenedores incluyen:
Desarrollar aplicaciones cloud-native
Modernizar aplicaciones
Creación de prototipos
Dispositivos IoT
DevOps
Docker es una plataforma que permite a los desarrolladores crear, implementar, configurar y gestionar contenedores.
Algunas de las herramientas de Docker más utilizadas incluyen:
Docker Enterprise
Docker Swarm
Docker Compose
Kubernetes
Testcontainers
Testcontainers es un framework de código abierto que proporciona instancias modulares de dependencias preconfiguradas que pueden ejecutarse en un contenedor, como bases de datos, navegadores y brokers de mensajes.
Testcontainers agiliza las pruebas de aplicaciones al proporcionar:
Aislamiento y consistencia
Paridad de entornos
Gestión de recursos
Escalabilidad y fiabilidad
Otros aumentos de productividad y eficiencia que ofrece Testcontainers incluyen:
La abstracción de dependencias, lo que facilita trabajar con ellas, sin tener que instalar software adicional ni realizar cambios en el/los entorno(s) de la aplicación.
La invocación sencilla de componentes de IA, convirtiéndolos en una parte normal de los procesos DevOps.
La abstracción y el despliegue automatizado de LLMs.
Lecturas adicionales
Te animamos a consultar los recursos a continuación para aprender más sobre contenedores, Docker, DevOps y su papel en la creación de la próxima generación de soluciones impulsadas por IA.
Sigue leyendo

A Developer's Guide to Exploring Milvus 2.6 Features on Zilliz Cloud
Milvus 2.6 marks a shift from “vector search + glue code” to a more advanced retrieval engine, and it is now Generally Available (GA) on Zilliz Cloud (a managed Milvus service).

Zilliz Cloud Audit Logs Goes GA: Security, Compliance, and Transparency at Scale
Zilliz Cloud Audit Logs are now GA, giving enterprises real-time visibility, compliance-ready trails, and stronger security across AWS, GCP, and Azure.

Bringing AI to Legal Tech: The Role of Vector Databases in Enhancing LLM Guardrails
Discover how vector databases enhance AI reliability in legal tech, ensuring accurate, compliant, and trustworthy AI-powered legal solutions.


