Come Testcontainers semplifica lo sviluppo di applicazioni basate sull'IA
Nell’attuale panorama aziendale frenetico e sempre più competitivo, le organizzazioni devono trovare modi per accelerare il completamento dei loro progetti di trasformazione digitale. Riduci al minimo i ritardi nella distribuzione di nuove applicazioni o funzionalità e potrai coglierne i benefici in termini di produttività ed efficienza in meno tempo, ottenendo un vantaggio sui tuoi concorrenti. Prolungare lo sviluppo di nuovo software o il rilascio di aggiornamenti, tuttavia, può erodere la tua quota di mercato, poiché altre aziende nel tuo settore innovano più rapidamente.
I container sono emersi come la tecnologia di riferimento per le aziende che cercano di semplificare le proprie pratiche di sviluppo applicativo, consentendo agli sviluppatori di creare applicazioni più sicure, robuste e scalabili - in meno tempo che mai.
In questo articolo, ispirato a questo intervento di Oleg Šelajev in un recente Unstructured Data Meetup ospitato da Zilliz, esploriamo il concetto di containerizzazione e uno dei suoi strumenti essenziali, Docker, e come riducono la complessità del processo di sviluppo applicativo. Rivolgeremo poi la nostra attenzione a un framework open-source, Testcontainers, che amplia considerevolmente le capacità di Docker - consentendo agli sviluppatori di semplificare lo sviluppo di applicazioni basate sull’IA.
Cosa sono i container?
I container sono pacchetti standardizzati di codice applicativo contenenti tutti i componenti richiesti, inclusi binari, dipendenze, librerie e file di configurazione, per funzionare in qualsiasi ambiente. Tradizionalmente, le applicazioni erano monolitiche, con diversi componenti all’interno di un’unica codebase. Oggi, gli sviluppatori dividono il software in componenti in base alla loro funzione o servizio, ciascuno con la propria codebase e dipendenze–containerizzazione o architettura a microservizi.
I vantaggi dei container includono:
Maggiore agilità aziendale: dividere le applicazioni in servizi consente agli sviluppatori di lavorare contemporaneamente su diverse parti del progetto. Allo stesso modo, la modularità dei container consente aggiornamenti e nuove funzionalità senza modificare l’intera applicazione.
Scalabilità migliorata: se un’applicazione può essere impacchettata all’interno di un container, è sufficiente distribuire un container aggiuntivo per creare un’altra istanza. Nelle applicazioni modulari, nel frattempo, i team possono semplicemente avviare nuovi container per il servizio che si avvicina alla capacità massima.
Maggiore efficienza: poiché i container sono altamente portabili e indipendenti dalla piattaforma, gli sviluppatori non devono dedicare tempo alla configurazione e alla manutenzione di ambienti diversi.
Costi inferiori: sollevare gli sviluppatori dalla responsabilità di gestire server e ambienti li rende più produttivi altrove, cioè nello sviluppo effettivo delle applicazioni! Questo riduce le ore-uomo necessarie per completare i progetti - abbassandone il costo.
I casi d’uso dei container includono:
Sviluppo di applicazioni cloud-native: le architetture containerizzate sfruttano appieno le capacità del cloud, tra cui scalabilità migliorata, maggiore accesso ai dati e cybersicurezza più solida.
Modernizzazione delle applicazioni: i container possono essere utilizzati per rifattorizzare applicazioni legacy al fine di aumentarne la compatibilità con un ambiente cloud, rendendole più performanti, efficienti nell’uso delle risorse e più facili da aggiornare.
Prototipazione: poiché sono indipendenti dalla piattaforma, i container evitano la necessità di configurare ambienti diversi per sviluppo, test, staging, ecc - rendendo più facile per gli sviluppatori sperimentare nuove funzionalità.
Dispositivi Internet of Things (IoT): essendo leggeri ed efficienti in termini di risorse, i container forniscono un meccanismo ideale di distribuzione software per hardware IoT con risorse limitate.
- DevOps: i container vengono utilizzati per creare ambienti coerenti e isolati, garantendo che un’applicazione venga eseguita allo stesso modo indipendentemente da dove viene distribuita, con conseguente distribuzione più rapida e collaborazione migliorata tra i team di sviluppo e operations.
Che cos’è Docker?
Docker è una piattaforma che consente agli sviluppatori di creare, distribuire, configurare e gestire container. Docker è infatti così sinonimo di container che questi vengono spesso chiamati “container Docker. In realtà, tuttavia, quando le persone menzionano Docker, si riferiscono in realtà al motore Docker: l’ambiente di runtime su cui i container vengono creati ed eseguiti. Questo motore si trova sotto i container per virtualizzare il sistema operativo (OS) del dispositivo host e assegnare loro la quantità ottimale di risorse, come CPU e memoria.
Questo è in netto contrasto con una soluzione simile nelle macchine virtuali (VM), che richiedono un proprio OS e risorse computazionali. Oltre a rendere i container indipendenti dall’ambiente, l’uso di Docker li rende intensivi in termini di risorse, più veloci da avviare e arrestare, e con maggiore portabilità rispetto alle VM.
Strumenti Docker ampiamente utilizzati
Per quanto Docker sia potente, la sua popolarità è dovuta in gran parte al suo ricco ecosistema di strumenti e librerie associati. Ecco alcuni degli strumenti Docker più notevoli.
Docker Enterprise: una versione di Docker progettata per distribuzioni critiche, che offre funzionalità avanzate di sicurezza, gestione e orchestrazione per release certificate e di livello produzione.
Docker Swarm: uno strumento di clustering e orchestrazione per Docker che consente agli utenti di gestire più nodi Docker come un unico sistema virtuale, permette una scalabilità e un bilanciamento del carico più semplici.
Docker Compose: uno strumento per semplificare lo sviluppo e la gestione di applicazioni Docker complesse e multi-container.
Kubernetes: una piattaforma open-source di orchestrazione dei container che automatizza la distribuzione, la scalabilità e la gestione di grandi applicazioni containerizzate su cluster di server.
Puoi scoprire di più sull’ecosistema docker in questo articolo https://middleware.io/blog/understanding-the-docker-ecosystem/
Testcontainers: gestione semplificata delle dipendenze
Testcontainers è un framework open-source che fornisce istanze leggere e modulari di database, browser, message broker e altre dipendenze preconfigurate che possono essere eseguite in un container. Mettendo a disposizione dell’utente una libreria di API programmatiche per manipolare i container, Testcontainers astrae i dettagli complicati di ciascuna dipendenza, rendendo più semplice lavorarci. Questo riduce il costo operativo dei progetti, incoraggiando al contempo la sperimentazione e facilitando uno sviluppo semplificato.
Una selezione delle oltre 50 integrazioni modulari di Testcontainers
Come suggerisce il nome, Testcontainers offre i maggiori incrementi di produttività quando si tratta di test delle applicazioni, inclusi test del livello di accesso ai dati, di integrazione e di accettazione.
I vantaggi dell’esecuzione dei test delle applicazioni con Testcontainers includono:
Isolamento e coerenza: containerizzare i componenti garantisce che ogni test venga eseguito in uno stato nuovo e noto. Ad esempio, testare i database in isolamento elimina le incoerenze che derivano da configurazioni diverse dei database locali, mentre i browser containerizzati consentono test dell’interfaccia utente più affidabili rimediando alle variazioni causate dalle configurazioni dei browser locali.
Parità dell’ambiente: gli sviluppatori possono eseguire test end-to-end in un ambiente che assomiglia molto alla produzione, riducendo le discrepanze tra gli ambienti e aumentando al contempo l’accuratezza dei risultati dei test.
Gestione delle risorse: poiché i container hanno tempi di esecuzione brevi, puoi gestire le risorse in modo più efficiente, consentendo al contempo un’esecuzione e una dismissione rapide dei test.
Scalabilità e affidabilità: di conseguenza, i test possono essere scalati facilmente senza dover considerare le risorse disponibili.
Oltre a rendere i test delle applicazioni più semplici ed efficaci, Testcontainers può aiutarti a sviluppare applicazioni IA indipendenti dall’ambiente. Rivolgiamo ora la nostra attenzione ad alcuni degli altri modi principali in cui Testcontainers aumenta la produttività.
Esecuzione di un’istanza Milvus
Per un primo esempio di come usare Testcontainers per aumentare la produttività, creiamo un’istanza Milvus, come mostrato nell’esempio di codice qui sotto (tutti gli esempi di codice in questo articolo sono scritti in Java, ma la libreria Testcontainers è disponibile anche in Python, Node.js Go, Rust e molti altri linguaggi):
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());
}
}
Invece di dover installare e configurare un’istanza Milvus separata e integrarla con la tua applicazione, Testcontainers fornisce un’astrazione facile da usare che invoca semplicemente l’istanza dall’interno del codice.
Per prima cosa, creiamo una nuova istanza di un container Milvus, specificando la versione passando l’immagine container appropriata come parametro. Oltre a semplificare lo sviluppo delle applicazioni, questo rende anche più semplice aggiornare le applicazioni in futuro, poiché devi solo specificare l’immagine aggiornata. Da lì, eseguiamo il container usando il metodo .start() e poi passiamo il container come parametro a una nuova istanza di un Milvus Service Client che ti consente di interagire con il servizio all’interno del container.
Oltre a creare un’istanza Milvus in appena poche righe di codice, questo approccio garantisce anche che il componente non vada mai fuori sincronia con i requisiti dell’applicazione, perché viene invocato dall’applicazione stessa.
Integrazione di Ollama e HuggingFace per l’integrazione degli LLM
Un secondo modo in cui Testcontainers aumenta la produttività è semplificando l’integrazione di componenti IA, come gli LLM, all’interno delle applicazioni. Questo ti consente di effettuare facilmente chiamate LLM dall’interno del codice per aggiungere funzionalità LLM alle applicazioni e, altrettanto importante, rende i componenti IA una parte normale del processo DevOps.
Nel frammento di codice qui sotto, creiamo una classe OllamaHuggingFaceContainer, che estende l’OllamaContainer esistente per avviare un’istanza che esegue un container Ollama che esegue un modello di deep learning scaricato da HuggingFace.
Ollama è una piattaforma che ti consente di servire modelli per l’inferenza localmente sul tuo dispositivo, mentre HuggingFace è un hub indispensabile per scaricare modelli IA open source. Combinando le due risorse, ottieni accesso alle decine di modelli supportati da Ollama, nonché a qualsiasi modello in GGUF (GPT-Generated Unified Format), di cui ce ne sono 23.000 su 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;
}
Distribuzione automatizzata di modelli di IA
Successivamente, poiché Testcontainers consente l’invocazione di modelli di IA dall’interno dell’applicazione, offre anche un modo più comodo e automatizzato per distribuire tali modelli. Sebbene i modelli open-source siano facilmente ottenibili da HuggingFace, vengono archiviati come una serie di file e directory in repository git. Questo non solo è troppo complicato per i non iniziati, ma è anche tutt’altro che il modo più comodo per gli sviluppatori esperti di lavorare con i modelli.
Un tipico repository HuggingFace
Invece di configurare e distribuire manualmente un modello, puoi automatizzare il processo con uno script. Anche questo riduce il carico amministrativo per gli sviluppatori, aggiungendo al contempo un ulteriore livello di astrazione per il modello stesso. Ciò è esemplificato nel codice qui sotto (che è un frammento di uno script di test più lungo), che mostra come un modello possa essere specificato e configurato dall’interno del codice. Oltre a specificare la directory da cui il modello deve essere prelevato e il modello esatto da utilizzare, la variabile modelfiledefinisce un modello di 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();
}
Come accennato in precedenza, questo può essere applicato a qualsiasi modello su Ollama, così come al vastissimo numero disponibile in formato GGUF. Distribuire un modello in questo modo rende anche più facile aggiornare il modello di IA nelle iterazioni future della tua applicazione, poiché non dovrai apportare modifiche all’ambiente, ma solo ai riferimenti al modello nel codice.
Conclusione
In sintesi:
I container sono pacchetti standardizzati di codice applicativo che contengono tutti i componenti richiesti, inclusi binari, dipendenze, librerie e file di configurazione, per l’esecuzione in qualsiasi ambiente.
I vantaggi dei container includono:
Maggiore agilità aziendale
Scalabilità migliorata
Maggiore efficienza
Costi inferiori
I casi d’uso dei container includono:
Sviluppo di applicazioni cloud-native
Modernizzazione delle applicazioni
Prototipazione
dispositivi IoT
DevOps
Docker è una piattaforma che consente agli sviluppatori di creare, distribuire, configurare e gestire container.
Alcuni degli strumenti Docker più utilizzati includono:
Docker Enterprise
Docker Swarm
Docker Compose
Kubernetes
Testcontainers
Testcontainers è un framework open-source che fornisce istanze modulari di dipendenze preconfigurate che possono essere eseguite in un container, come database, browser e message broker.
Testcontainers semplifica il testing delle applicazioni fornendo:
Isolamento e coerenza
Parità degli ambienti
Gestione delle risorse
Scalabilità e affidabilità
Altri miglioramenti in termini di produttività ed efficienza offerti da Testcontainers includono:
L’astrazione delle dipendenze, rendendole più facili da utilizzare, senza dover installare software aggiuntivo o apportare modifiche agli ambienti dell’applicazione.
La semplice invocazione di componenti AI, rendendoli una parte normale dei processi DevOps.
L’astrazione e la distribuzione automatizzata degli LLM.
Ulteriori letture
Ti invitiamo a consultare le risorse qui sotto per saperne di più su container, Docker, DevOps e sul loro ruolo nella creazione della prossima generazione di soluzioni basate sull’AI.
Continua a leggere

Why Teams Are Migrating from Weaviate to Zilliz Cloud — and How to Do It Seamlessly
Explore how Milvus scales for large datasets and complex queries with advanced features, and discover how to migrate from Weaviate to Zilliz Cloud.

Legal Document Analysis: Harnessing Zilliz Cloud's Semantic Search and RAG for Legal Insights
Enhance legal document analysis with Zilliz Cloud’s Semantic Search and RAG. Improve accuracy, efficiency, and scalability for contracts, case law, and compliance.

Introducing DeepSearcher: A Local Open Source Deep Research
In contrast to OpenAI’s Deep Research, this example ran locally, using only open-source models and tools like Milvus and LangChain.


