Gestione degli embeddings

Gli embeddings sono rappresentazioni vettoriali dei testi.

Un modello di embedding trasforma una frase in un vettore numerico che rappresenta il significato semantico del contenuto.

Esempio semplificato:

“nuoto stile libero”
→ [0.12, -0.45, 0.98, …]

Testi con significato simile producono vettori simili.

Nel laboratorio utilizziamo il modello:

  • nomic-embed-text, distribuito tramite Ollama.

Verifica dei modelli disponibili:

kubectl exec -n ai deploy/ollama ollama list

Output:

  • nomic-embed-text
  • qwen2.5:1.5b

Ruoli dei modelli:

Modello Funzione
qwen2.5 LLM che genera le risposte
nomic-embed-text genera embeddings

 

Deployment di Qdrant su Kubernetes

Dopo aver installato un LLM locale con Ollama su Kubernetes, il passo successivo per costruire un sistema AI realmente utile è permettere al modello di consultare una knowledge base personalizzata.

Questo approccio prende il nome di RAG – Retrieval Augmented Generation: il modello non si limita alle conoscenze con cui è stato addestrato, ma recupera informazioni da un database vettoriale contenente documenti indicizzati tramite embeddings.

Nel laboratorio Kubernetes utilizzato in questo articolo la pipeline è composta da:

  • Ollama → esecuzione del modello LLM
  • Qdrant → database vettoriale per gli embeddings
  • Open WebUI → interfaccia grafica e gestione della knowledge base
  • Embedding model → trasformazione dei documenti in vettori

Architettura semplificata:

User


Open WebUI

|– Query → Ollama (LLM)

└── Search → Qdrant (vector DB)


Knowledge Base
Deploy Qdrant su k8s

Per il deployment di Qdrant è possibile utilizzare il chart Helm ufficiale.

Repository Helm: https://qdrant.github.io/qdrant-helm

Aggiungiamo il repository:

  • helm repo add qdrant https://qdrant.github.io/qdrant-helm
  • helm repo update

Installazione nel namespace dedicato all’AI:

helm install qdrant qdrant/qdrant \
–namespace ai \
–create-namespace

Dopo pochi secondi verifichiamo lo stato dei pod:

  • kubectl get pods -n ai  — Output atteso: qdrant-0 1/1 Running

Il servizio Kubernetes esposto sarà:

kubectl get svc -n ai

Esempio:

qdrant ClusterIP 10.43.x.x 6333/TCP

La porta 6333 espone l’API REST di Qdrant.

Verifica rapida:

kubectl exec -n ai qdrant-0 wget -qO- http://localhost:6333/healthz

Risposta attesa: healthz check passed

LLM & RAG: L’importanza del dato

Nel precedente articolo abbiamo costruito una piattaforma AI completamente on-premises, eseguendo un Large Language Model locale su Kubernetes tramite Ollama.

Questa configurazione rappresenta un primo passo fondamentale: ci consente di interrogare un modello linguistico senza dipendere da servizi cloud esterni, mantenendo il pieno controllo dei dati.

Tuttavia, un limite importante rimane.

Il modello, per quanto potente, non conosce il contesto specifico della nostra organizzazione: documentazione interna, procedure, knowledge base, ticket o dati proprietari.

Il problema: LLM senza contesto

Un LLM locale:

  • non è aggiornato con dati aziendali
  • non può accedere a documenti interni
  • risponde solo in base al training generico

Questo lo rende poco utile in scenari reali enterprise.

La soluzione: Retrieval Augmented Generation (RAG)

Per superare questo limite entra in gioco il paradigma di Retrieval Augmented Generation (RAG).

RAG combina due elementi:

  1. Retrieval → recupero di informazioni rilevanti da una base dati
  2. Generation → generazione della risposta usando il modello LLM

In pratica:

Domanda utente

Ricerca nei dati aziendali (vector DB)

Contesto rilevante

LLM (Ollama)

Risposta arricchita e contestualizzata
Il ruolo del Vector Database

Per rendere possibile il retrieval, i documenti devono essere trasformati in vettori tramite embeddings.

Qui entra in gioco un componente chiave:

➡️ Vector Database

Nel nostro laboratorio utilizzeremo Qdrant , un database ottimizzato per:

  • ricerca semantica
  • gestione embeddings
  • alta performance su workload AI

Evoluzione dell’architettura

Con l’introduzione del RAG, lo stack evolve:

Utente

OpenWebUI

Ollama (LLM)

Qdrant (Vector DB)

Knowledge Base

Ora il modello non risponde più “a memoria”, ma: risponde usando i tuoi dati

Perché il RAG è fondamentale

I motivi principali sono:

  • AI contestualizzata per i dati aziendali
  • maggiore accuratezza delle risposte
  • riduzione delle allucinazioni
  • nessuna necessità di retraining del modello
  • pieno controllo dei dati (on-prem)

Nel prossimo articolo vedremo nel dettaglio:

  1. deployment di Qdrant su Kubernetes
  2. gestione degli embeddings
  3. caricamento della knowledge base
  4. integrazione con OpenWebUI e Ollama

LLM in locale con Kubernetes e Ollama

L’intelligenza artificiale sta diventando sempre più presente nelle moderne architetture.

Tuttavia, molte organizzazioni preferiscono eseguire i modelli AI in locale, evitando di inviare dati sensibili a servizi cloud esterni.

In questo articolo vedremo come eseguire un Large Language Model (LLM) all’interno di un cluster Kubernetes utilizzando Ollama, una piattaforma che consente di gestire ed eseguire modelli linguistici direttamente on-premise.

L’obiettivo è integrare un modello AI locale nel laboratorio Kubernetes, mantenendo il pieno controllo su infrastruttura e dati.

Perché Ollama

Ollama è un runtime leggero progettato per eseguire modelli linguistici in modo semplice e rapido.

Le sue principali caratteristiche sono:

  • esecuzione di modelli LLM in locale.
  • nessuna dipendenza da servizi cloud esterni.
  • API REST semplici da integrare.
  • gestione dei modelli con pochi comandi.
  • runtime leggero e facilmente containerizzabile.

Queste caratteristiche rendono Ollama una soluzione ideale per esperimenti AI in ambienti Kubernetes.

Architettura dello stack AI

Nel laboratorio K8s lo stack AI è composto da tre componenti principali:

  • Ollama → L’ incubatore per il modello linguistico. Vedetelo come un hypervisor per LLM: non virtualizza sistemi operativi, ma fornisce l’ambiente necessario per eseguire modelli AI in locale.
  • Open WebUI → Fornisce un’interfaccia web per interagire con il modello.
  • Qdrant → Database vettoriale utilizzato per scenari RAG.

Il flusso è il seguente:

Utente


OpenWebUI


API Ollama


Modello LLM locale

Questa tipologia di architettura permette di costruire assistenti AI completamente locali, senza dipendere da provider esterni.

Deploy di Ollama in Kubernetes

La runtime Ollama può essere distribuito come un normale deployment Kubernetes.

Esempio di deployment:

È poi necessario esporre il servizio all’interno del cluster:

Una volta avviato il deployment, l’API Ollama sarà raggiungibile all’interno del cluster Kubernetes.
Installazione di un modello

Ollama permette di scaricare i modelli direttamente dal proprio registry.

Nel lab ho scelto il modello Qwen 2.5 1.5B che ho installato con il seguente comando:

  • kubectl -n ai exec deploy/ollama ollama pull qwen2.5:1.5b
Il modello viene scaricato e salvato all’interno del container Ollama.
Per conoscere i modelli installati:
  • kubectl -n ai exec deploy/ollama ollama list

Verifica dell’API Ollama

Ollama espone via API REST; può essere interrogata per verificare i modelli disponibili:

  • curl http://ollama:11434/api/tags

Risposta:

E’ la conferma che il modello è stato caricato correttamente.

Collegare un’interfaccia Web

Al fine di semplificare l’interazione con il modello è possibile installare la Open WebUI che fornisce:

  • interfaccia tipo ChatGPT
  • selezione del modello
  • cronologia delle conversazioni
  • integrazione con database vettoriali

L’interfaccia comunica con Ollama utilizzando l’endpoint:

  • http://ollama:11434
Il vantaggio è che una volta configurato, è possibile utilizzare il modello direttamente dal browser.

Perchè utilizzare un LLM locale in k8s?

Utilizzare i modelli linguistici localmente apre diversi scenari interessanti quali ad esempio:

  • assistenti AI interni aziendali
  • ricerca documentale avanzata
  • copiloti per applicazioni interne
  • integrazione AI in architetture a microservizi

Ciò permette di mantenere controllo completo su infrastruttura, dati e ciclo di vita dei modelli.

Nel prossimo articolo estenderemo lo stack AI introducendo Retrieval Augmented Generation (RAG) utilizzando Qdrant.

Installazione di Kasten K10 su Kubernetes: guida passo-passo

Installare Kasten K10 in un cluster Kubernetes è un’operazione semplice, ma richiede alcune verifiche preliminari fondamentali, specialmente lato snapshot e CSI driver.

In questo articolo vedremo:

  • Verificare prerequisiti con script ufficiale
  • Installazione di K10
  • Etichettatura delle SnapshotClass
  • Avvio e verifica finale

Verifica prerequisiti con k10_primer

Prima di installare K10 è obbligatorio verificare che il cluster sia pronto.

Kasten fornisce uno script ufficiale chiamato k10_primer.sh.

curl https://docs.kasten.io/downloads/8.0.4/tools/k10_primer.sh | bash

Lo script verifica:

  • La presenza del CSI driver (nel mio caso synology)
  • Snapshot CRD installate
  • VolumeSnapshotClass disponibili
  • Permessi RBAC
  • Storage supportato
  • Compatibilità Kubernetes

Output attesoCluster meets K10 requirements

Se compaiono dei warning o errori in particolare sulle snapshot, bisogna riconfigurare correttamente la SnapshotClass come indicato ad esempio nella nota 1.

Installazione di K10

Aggiungere il repository Helm:

  • helm repo add kasten https://charts.kasten.io/
  • helm repo update

Creare il namespace:

  • kubectl create namespace kasten-io

Installazione di K10:

  • helm install k10 kasten/k10 \
  • –namespace kasten-io

Per verificare lo stato dei pod avviare il semplice comando:

  • kubectl -n kasten-io get pods

Quando tutti i pod sono Running, K10 è operativo.

Nota 1: Etichettare la VolumeSnapshotClass

Affinché K10 possa utilizzare le snapshot CSI, è necessario etichettare la VolumeSnapshotClass attraverso dapprima la verifica che le snapshotclass siano disponibili:

  • kubectl get volumesnapshotclass

Dopo averle individuate (nel mio laboratorio sono indicate come Synology iSCSI), devono essere etichettate con il comando:

  • kubectl label volumesnapshotclass <NOME_SNAPSHOTCLASS> \
  • k10.kasten.io/is-snapshot-class=true

Questa label è fondamentale: senza di essa K10 non utilizzerà quella snapshot class per i backup.

Verifica: kubectl get volumesnapshotclass –show-labels

Avvio e accesso a K10

Come ogni microservizio è possibile esporre k10 via LoadBalancer o Ingress.

Dopo aver indivuduato il servizio k10 (kubectl -n kasten-io get svc gateway) lanciate i corretti comandi per esporre il servizio K10.

Dalla dashboard verificate che:
  • Lo storage venga rilevato
  • Le snapshot siano disponibili

Ora è possibile creare una prima policy di test che sfrutti unicamente le snapshot come primo repository.

In un prossimo articolo mostreremo come aggiungere attraverso le location profile un repository.

Conclusione

L’installazione di Kasten K10 è lineare, ma la parte critica è:

  • Verificare preliminare con k10_primer
  • Configurazione corretta delle SnapshotClass

Ricostruire un Cluster RKE2 con Storage Synology CSI + Snapshot

L’obiettivo del presente articolo è illustrare come realizzare un cluster Kubernetes basato su RKE2, con la seguente architettura:

Reinstallazione Pulita di RKE2

Dopo i test precedenti, al fine di avere una situazione pulita ho deciso di ripartire da zero.

Disinstallazione completa

Su ogni nodo:

  • sudo /usr/local/bin/rke2-uninstall.sh
  • sudo rm -rf /var/lib/rancher /var/lib/kubelet /etc/cni /var/lib/cni /opt/cni

Pulizia mount iSCSI eventualmente attivi:

  • sudo umount -l /var/lib/kubelet/plugins/kubernetes.io/csi/*/*/globalmount

Reboot nodi.

Installazione RKE2 Server (Management Node)

Configurazione /etc/rancher/rke2/config.yaml:

  • write-kubeconfig-mode: “0644”
  • tls-san:
  •   – “IP”
  • node-ip: “IP”

In Kubernetes (k8s), TLS SAN è l’acronimo di  Subject Alternative Name. E’ un’estensione del certificato SSL/TLS X.509 utilizzata per specificare molteplici nomi host (DNS) o indirizzi IP validi per un singolo certificato

Installazione:

  • curl -sfL https://get.rke2.io | sh –
  • sudo systemctl enable rke2-server –now

Esportazione kubeconfig:

  • export KUBECONFIG=/etc/rancher/rke2/rke2.yaml

Join Worker Nodes

Su ciascun worker:

  • curl -sfL https://get.rke2.io | INSTALL_RKE2_TYPE=”agent” sh –

All’interno del file di configurazione /etc/rancher/rke2/config.yaml è necessario aggiungere le voci server e il token di accesso

  • server: https://ip-management:9345
  • token: <server_token>

NB: per generare il token sul management lanciare il comando:

  • sudo cat /var/lib/rancher/rke2/server/node-token

Avvio: sudo systemctl enable rke2-agent –now

Al fine di separare i diversi ruoli nel cluster conviene assegnare delle label ai worker con i seguenti comandi:

  • kubectl label node nuc-01 workload=apps
  • kubectl label node nuc-02 workload=apps
  • kubectl label node nuc-03 workload=apps

Taint Management: Il taint (macchia/marchio) di Kubernetes è una proprietà applicata a un nodo per “respingere” i pod, impedendo loro di pianificarvi sopra a meno che non abbiano una specifica toleration (tolleranza). Nel nostro caso viene applicato al nodo di mangement.

  • kubectl taint nodes nuc-mng node-type=management:NoSchedule

Risultato: Nessun microservizio girerà sul management, lo scheduling è forzato sui worker

Ingress NGINX: Un controller Kubernetes Ingress è un componente software specializzato che gestisce il traffico in entrata verso le applicazioni in esecuzione 

Installazione via Helm:

  • helm install ingress-nginx ingress-nginx/ingress-nginx \
  •   –namespace ingress-nginx \
  •   –create-namespace \
  •   –set controller.nodeSelector.workload=apps

Cert-Manager: cert-manager è un add-on open-source per Kubernetes che automatizza la gestione, l’emissione e il rinnovo dei certificati TLS/SSL

Installazione CRD:

  • kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.crds.yaml
  • Installazione Helm (disabilitando startupapicheck nel lab):
  • helm install cert-manager jetstack/cert-manager \
  •   –namespace cert-manager \
  •   –create-namespace \
  •   –set startupapicheck.enabled=false \
  •   –set nodeSelector.workload=apps

Installazione Synology CSI Driver

E’ necessario che ogni singolo worker veda via iSCSI lo storage in modo che il comando sudo iscsiadm -m discovery -t sendtargets -p <IP_NAS> mostri i target.

Per installare il csi driver:

  • git clone https://github.com/SynologyOpenSource/synology-csi.git
  • cd synology-csi

Nella cartella

Modificare il file ~/synology-csi/config/client-info.yaml inserendo le credenziali richieste

Creare un secret:

kubectl -n kube-system create secret generic synology-csi-secret \
–from-literal=username='<DSM_USER>’ \
–from-literal=password='<DSM_PASSWORD>’

e per ultimo caricare il config.yaml nel config map:

kubectl -n kube-system create configmap synology-csi-config \
–from-file=config.yml=./config.yml

Gli ultimi passaggi sono quelli di creare una Storage Class (sc) e una Volume Snapshot Class al fine di rendere il cluster k8s in grado di:

  • Gestire snapshot nativi
  • Ripristini di PVC da snapshot
  • Pronto per integrazione con Kasten K10

🏁 Stato Finale del Cluster

✔ RKE2
✔ Calico
✔ Ingress NGINX
✔ Cert-Manager
✔ Synology CSI (iSCSI)
✔ Snapshot Kubernetes
✔ Separazione Management / Workload

Prossimi step: 

  • Kasten K10
  • Disaster Recovery test
  • AI workload
  • Database stateful