Home Chi Sono
Servizi ▼
WordPress Sviluppo Web Server & Hosting Assistenza Tecnica Windows Android
Blog ▼
Tutti gli Articoli WordPress Hosting Plesk Assistenza Computer Windows Android A.I.
Contatti

Come Implementare Plesk LLM Workload Multi-Tenant Isolation 2026: La Mia Procedura Container Sandboxing, GPU Resource Quotas e Cost Attribution per Token Usage

Come Implementare Plesk LLM Workload Multi-Tenant Isolation 2026: La Mia Procedura Container Sandboxing, GPU Resource Quotas e Cost Attribution per Token Usage

Nel 2026, il panorama del multi-tenancy per carichi di lavoro LLM in ambienti Plesk è diventato critico. Ho visto troppi amministratori di sistema spiegare inaspettatamente budget di GPU bruciati per agenti LLM che sfuggivano al controllo, o peggio ancora, scoprire che i dati di un tenant erano raggiungibili da un altro. In questa guida, vi mostro come implementare una strategia di isolamento robusta che combina container sandboxing, quote di risorse GPU dichiarate, e attribuzione dei costi per token consumati.

Perché il Multi-Tenant Isolation per LLM è Non-Negoziabile nel 2026

Quando gestite un ambiente Plesk con ospitati più clienti che eseguono agenti LLM, il problema “noisy neighbor” non è teorico: è operativo. La maggior parte dei provider LLM SaaS aggiungono il multi-tenancy come ripensamento, spediscono un endpoint condiviso senza enforcement di quote, scoprono il problema noisy-neighbor su scala, e passano settimane a retrofittare isolamento che avrebbe dovuto essere nel v1.

In multi-tenant LLM serving, i carichi di lavoro ad alta domanda da un client possono causare violazioni di SLA di latenza per altri. Nel nostro contesto Plesk, questo significa che un tenant che avvia un agente mal configurato può consumare tutte le VRAM della GPU, affamando i workload in tempo reale di un altro customer. Peggio ancora, le misurazioni di carichi di lavoro co-locati mostrano degradazione di performance del 5-50% da interferenza pura.

La soluzione: isolamento strutturale a tre livelli: runtime container, quote di risorse GPU esplicite, e attribuzione di costi per token fino a livello di tenant.

Livello 1: Container Sandboxing per LLM Workload

Nel mio approccio su Plesk, ogni tenant LLM gira in un container isolato con policy di sicurezza hardened. Non sto usando container vanilla: uso una strategia che presume che l’agente possa fare qualcosa che non ho esplicitamente scritto.

Gli agenti sono diversi dai servizi tipici in un modo critico: prendono azioni autonome. Questo significa che il modello di sicurezza deve assumere che un agente potrebbe fare qualcosa che non hai esplicitamente scritto, e progettare per il contenimento piuttosto che per la fiducia. Sandboxare il container stesso.

Ecco la procedura che ho consolidato:

Step 1: Configurare Namespace Kubernetes per Tenant

Nel mio setup Plesk con supporto Kubernetes (o Docker per deployment più piccoli), creo un namespace dedicato per ogni tenant LLM:

kubectl create namespace tenant-acme-llm
kubectl label namespace tenant-acme-llm tenant-id=acme isolation=strict

Questo namespace diventa la “cella” di esecuzione di questo tenant. Tutti i pod dell’agente LLM di ACME girano esclusivamente qui.

Step 2: Applicare Resource Quotas Hard

Applico una ResourceQuota Kubernetes che impone limiti rigorosi:

cat > tenant-acme-quota.yaml << 'EOF'
apiVersion: v1
kind: ResourceQuota
metadata:
  name: acme-hard-quota
  namespace: tenant-acme-llm
spec:
  hard:
    requests.cpu: "8"
    requests.memory: "32Gi"
    requests.nvidia.com/gpu: "1"  # 1 GPU T4 per tenant
    limits.cpu: "10"
    limits.memory: "40Gi"
  scopeSelector:
    matchExpressions:
    - operator: In
      scopeName: PriorityClass
      values: ["workload-llm"]
EOF
kubectl apply -f tenant-acme-quota.yaml

La chiave qui: ogni tenant ottiene una GPU dedicata (or fractional share in ambienti più grandi) con limiti CPU e memoria non negoziabili. Se il tenant tenta di exceeder questi limiti, il container viene killed dal kubelet, non rallentato.

Step 3: Network Policy per Isolamento Tenant-to-Tenant

Creo una NetworkPolicy che impedisce a un pod LLM di ACME di comunicare con i dati di un altro tenant:

cat > tenant-acme-netpolicy.yaml << 'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: acme-tenant-isolation
  namespace: tenant-acme-llm
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          tenant-id: acme
    - podSelector:
        matchLabels:
          role: api-gateway
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          tenant-id: acme
  - to:
    - podSelector:
        matchLabels:
          role: llm-provider  # Solo endpoint LLM autorizzati
  - ports:
    - protocol: TCP
      port: 53  # DNS
EOF
kubectl apply -f tenant-acme-netpolicy.yaml

Ora il pod ACME non può raggiungere direttamente altri namespace tenant, solo gli endpoint LLM autorizzati (OpenAI, Claude, self-hosted vLLM).

Step 4: Container Image Hardening e Scanning

Prima di deployare qualsiasi container LLM in Plesk, lo scansiono con trivy (incluso in Plesk 23.0+):

trivy image --severity HIGH,CRITICAL my-llm-agent:v1.2.3
trivy image --scanners secret,config my-llm-agent:v1.2.3

Se il container contiene secrets o vulnerabilità note, il deploy fallisce via Plesk admission controller.

Livello 2: GPU Resource Quotas e Fairness Scheduling

Le quota CPU/memory non sono sufficienti. La vera pressione nel 2026 viene dalle GPU.

A volumi di token più alti per customer (diciamo, 1M token/day ciascuno), l’H100 inizia ad approssimare i limiti di utilizzo e devi una seconda GPU. Questo è il punto di inflessione dove l’architettura shared-pool si rompe e le istanze dedicate per-customer diventano vale il costo.

Nel mio setup Plesk, implemento:

GPU Operator Kubernetes con Fair Queuing

Installo il NVIDIA GPU Operator insieme a kubelet-gpu-sharing per frazione le GPU tra tenant:

helm repo add nvidia https://nvidia.github.io/k8s-device-plugin
helm repo update
helm install nvidia-device-plugin nvidia/nvidia-device-plugin 
  --namespace kube-system 
  --set nvidia.driver.capabilities=compute,utility 
  --set sharing.timeSlicing.enabled=true 
  --set sharing.timeSlicing.replicas=4  # 4 tenant per GPU

Gli scheduler open-source e gli operatori GPU, come quelli catalogati nel progetto genai-workload-manager di IBM, possono aggiungere fair queuing e GPU sharing su vanilla Kubernetes quando il cluster ospita molti agenti workload concorrenti.

Ora ogni tenant ottiene una “fetta” di 25% della GPU (4 tenant per GPU H100). Se un tenant tenta di consumare più di sua quota, il scheduler lo mette in coda equamente con gli altri.

LimitRange per Prevenire Runaway Allocations

Applico un LimitRange aggressivo per namespace tenant:

cat > tenant-acme-limitrange.yaml << 'EOF'
apiVersion: v1
kind: LimitRange
metadata:
  name: acme-pod-limits
  namespace: tenant-acme-llm
spec:
  limits:
  - max:
      cpu: "4"
      memory: "16Gi"
      nvidia.com/gpu: "0.25"  # Max 25% di una GPU per pod singolo
    min:
      cpu: "100m"
      memory: "128Mi"
    type: Container
  - max:
      ephemeralStorage: "50Gi"
    type: Pod
EOF
kubectl apply -f tenant-acme-limitrange.yaml

Questo forza il tenant a frammentare i carichi di lavoro LLM in pod più piccoli, evitando un singolo agente che monopolizza la GPU.

Livello 3: Cost Attribution per Token Usage

Qui è dove la maggior parte dei deployment fallisce. Ho visto team che implementano isolamento perfetto a livello di container, ma nessuna tracciamento di costi. Sei mesi dopo: “Perché il tenant ACME usa il 60% della nostra fattura API LLM ma paga il 10% dello spazio su disco?”

Runaway cost è consumo di token illimitato su molte chiamate LLM, di solito perché un agente è bloccato in un loop, una chiamata ricorsiva a tool non termina, o una sessione di chat accumula contesto senza riassunto.

Step 1: Strumentazione Token Accounting al Deployment

L’attribuzione dei costi è onesta solo quando i tag sono allegati al momento della creazione della richiesta. Il tagging retroattivo dai log manca sempre dei casi limite e sotto-riporta le tracce agentic lunghe.

Nel mio setup Plesk con Langfuse (o alternativa open-source), ogni LLM call include questi metadati:

import langfuse
from openai import OpenAI

fuse = langfuse.Langfuse(
    public_key="pk-...",
    secret_key="sk-..."
)

# Tag ogni trace con tenant, user, agente, task
trace = fuse.trace(
    name="acme-data-cleaning-agent",
    user_id="acme-tenant",
    metadata={
        "tenant_id": "acme",
        "agent_type": "data-cleaner",
        "task_id": "clean-salesforce-export",
        "environment": "production"
    }
)

client = OpenAI(api_key="...")

with trace.span(name="llm-classification") as span:
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": record_text}],
        max_tokens=500
    )
    
    # Registra token granulari
    span.generation(
        model="gpt-4o",
        input=record_text,
        output=response.choices[0].message.content,
        usage={
            "input_tokens": response.usage.prompt_tokens,
            "output_tokens": response.usage.completion_tokens,
            "cache_read_tokens": getattr(response.usage, 'cache_read_input_tokens', 0),
            "cache_write_tokens": getattr(response.usage, 'cache_creation_input_tokens', 0)
        }
    )

Ora ogni token è tracciato fino al tenant.

Step 2: Real-Time Cost Monitoring e Alerting

Le soluzioni di tracciamento dei costi LLM usano il monitoraggio real-time per attivare notifiche automatizzate via email, Slack, o webhook quando l’utilizzo colpisce percentuali predefinite di un budget. Questi sistemi possono essere configurati con regole di enforcement automatizzate che limitano il traffico o bloccano richieste una volta raggiunto un hard cap.

Nel mio setup Plesk, creo un dashboard Prometheus + Grafana che monitora costi per tenant:

cat > cost-monitoring-prometheus.yaml << 'EOF'
groups:
- name: llm_cost_alerts
  interval: 1m
  rules:
  - alert: TenantMonthlyBudgetWarning
    expr: |
      (sum(rate(llm_token_cost_usd_total{tenant_id="acme"}[1h])) * 720) > 500
    for: 5m
    annotations:
      summary: "Tenant {{ $labels.tenant_id }} on track for {{ $value }}$ this month"
      action: "Check agent logs for runaway loops"
  
  - alert: TenantBudgetHardCap
    expr: |
      sum(llm_token_cost_usd_total{tenant_id="acme"}) > 2000
    for: 1m
    annotations:
      summary: "Tenant {{ $labels.tenant_id }} exceeded hard cap. Throttling requests."
      action: "Activate rate limiter for this tenant"
EOF

Quando ACME raggiunge il 90% del budget mensile, un alert via Slack mi avvisa. A 100%, il rate limiter automaticamente throttla le nuove richieste LLM fino alla fine del ciclo di fatturazione.

Step 3: Kill Switch per Runaway Agents

Interrompi un agente run quando il token count, tool-call count, retry count, o span depth supera il ceiling per quel tipo di agente. Il controllo appartiene al framework dell’agente perché un loop runaway deve essere fermato mentre è ancora in esecuzione, poiché un alert che si innesca dopo il completamento dell’esecuzione arriva troppo tardi per prevenire token sprecati.

Nel mio setup con LangChain, implemento un token budget wrapper:

from langchain.callbacks.base import BaseCallbackHandler
import time

class TokenBudgetCallback(BaseCallbackHandler):
    def __init__(self, tenant_id: str, max_tokens: int = 50000):
        self.tenant_id = tenant_id
        self.max_tokens = max_tokens
        self.token_count = 0
        self.start_time = time.time()
    
    def on_llm_end(self, response, **kwargs):
        tokens_used = response.llm_output.get('token_usage', {}).get('total_tokens', 0)
        self.token_count += tokens_used
        
        # Kill switch: se raggiungi il 100% del budget, arresta l'agente
        if self.token_count >= self.max_tokens:
            raise RuntimeError(
                f"Tenant {self.tenant_id} exceeded token budget "
                f"({self.token_count}/{self.max_tokens} tokens used). "
                f"Agent execution halted."
            )
        
        # Warning al 80%
        if self.token_count >= int(self.max_tokens * 0.8):
            logger.warning(
                f"Tenant {self.tenant_id} at 80% token budget. "
                f"Used {self.token_count}/{self.max_tokens} tokens."
            )

# Usa nel tuo agente LangChain
agent = initialize_agent(
    tools=tools,
    llm=llm,
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    callbacks=[TokenBudgetCallback(tenant_id="acme", max_tokens=50000)]
)

Ora se l’agente ACME entra in un loop, si ferma dopo 50k token. Non 500k. La differenza è circa $15.

Integrare Tutto in Plesk: Workflow Completo

Nel mio deployment Plesk finali, il flusso è:

  1. Tenant Onboarding: Un nuovo customer richiede un agente LLM ospitato. Creo un namespace Kubernetes con ResourceQuota, LimitRange, e NetworkPolicy preconfigirate.
  2. Container Deployment: Scansiono il container del cliente con trivy. Se passa, lo deploy in Kubernetes. Se fallisce, blocco e chiedo remediation.
  3. GPU Allocation: Il NVIDIA GPU Operator alloca una fetta della GPU H100 al tenant. Fair queuing garantisce che nessun tenant monopolizza.
  4. Token Tracking: Ogni LLM call è loggata in Langfuse con tag tenant. Prometheus scrape le metriche ogni minuto.
  5. Cost Alerting: Se il tenant raggiunge il 90% del budget, Slack mi avvisa. A 100%, il token rate limiter attiva.
  6. Runaway Prevention: Se l’agente entra in un retry loop, il token budget callback lo ferma entro 50k token.

Metriche da Monitorare

Nel mio dashboard Grafana giornaliero verifico:

  • GPU Utilization per Tenant: Ogni tenant dovrebbe usare ~25% della GPU (o la sua fetta allocata). Se uno usi 90%, è un problema.
  • Token Cost per Tenant: ACME spende $X/day? È in linea con il loro SLA?
  • Latency Percentiles: P99 latenza per LLM inference. Se uno tenant causa latency spike per gli altri, la NetworkPolicy sta fallendo.
  • Runaway Agent Detections: Quanti agenti sono stati stoppati dal token budget callback? Se è più di 5/week, il prodotto del cliente ha bug.
  • Container Escape Attempts: Trivy ha rilevato vulnerabilità post-deploy? Falco ha loggato syscall sospette? (Questo è il controllo di sicurezza avanzato.)

Problemi Che Ho Affrontato (E Come Risolverli)

Problema 1: Container Image Scanning Too Strict
All’inizio, il trivy scanner bloccava ogni immagine con anche una vulnerabilità teorica di basso livello. Nessun tenant poteva deployare. Ho aggiunto una whitelist di CVE mitigabili (p.es., CVE in librerie non usate dal loro agente specifico) e ora richiedo solo che i tenant patchino vulnerabilità CRITICAL e HIGH.

Problema 2: GPU Time Slicing Overhead
Dividere una GPU H100 tra 4 tenant via time-slicing aggiunge ~15% overhead di context switch. Per tenant con latency-critical inference, ho introdotto una GPU pool “premium” dove i tenant pagano di più per una dedica 50% della GPU (2 per GPU). La maggior parte sceglie la pool standard e tollera il 15% hit.

Problema 3: Token Attribution Lag
Langfuse aggiorna i costi con ~2-3 minuti di latenza. Se voglio alerting quasi real-time, ho aggiunto uno strato di proxy gateway (Portkey o Kong) che log token localmente, e poi riconcilio con Langfuse ogni 5 minuti. Il sistema di rate limiting usa il conteggio locale, non aspetta Langfuse.

FAQ

Devo usare Kubernetes per questo isolamento? Non posso usare solo Docker + Plesk?

Dipende dalla scala. Per 5-10 tenant, Docker + Plesk funziona con un’attenta configurazione manuale di cgroup e Network namespaces. Per 50+ tenant, Kubernetes diventa non-negoziabile perché il scheduling e l’enforcement delle quote diventano troppo complessi manualmente. Nel mio caso, ho migrato da Docker a Kubernetes quando ho raggiunto 15 tenant.

Qual è il costo di infrastruttura di questo setup?

Assumendo una singola GPU H100 ($40k upfront, $1000/anno amortized) che serve 4 tenant: ~$250/tenant/anno in hardware. Aggiungi Kubernetes cluster (~$500/month), monitoring stack ($200/month), e container registry ($100/month). Se ogni tenant paga almeno $2000/anno per hosting + LLM API, il margine è sano.

Cosa succede se un tenant non rispetta le quote e tenta di fuggire dal container?

Tre linee di difesa:
(1) NetworkPolicy blocca le comunicazioni non autorizzate.
(2) ResourceQuota e LimitRange fanno crash il pod.
(3) Falco (runtime security) registra qualsiasi syscall sospetta (p.es., tentativo di accedere a /proc di un altro container). Se Falco vede qualcosa, l’agente pod è immediate evicted e io investigato.

Come gestisco le GPU con different vram capacità (T4 da 16GB vs H100 da 80GB)?

Creo node pool separate in Kubernetes: uno per “standard” (T4), uno per “large” (A100), uno per “ultra” (H100). Etichetto i pod con `nodeSelector` GPU-type per matchare i tenant alle risorse corrette. Un tenant che richiede un modello da 40GB (Llama 3-70B) viene schedulato su H100. Uno che usa GPT-4o (API call, nessun modello locale) va su T4.

Come integro questo con il controllo degli accessi IAM di Plesk esistente?

Mappo ogni Plesk subscription a un tenant Kubernetes + Langfuse org + Prometheus job. Quando un admin Plesk accede al panel, vede solo i costi LLM, le quote GPU, e i log del container del suo tenant. La mapping la gestisco via OIDC (Keycloak integrato con Plesk) per tenere sincronizzate le identità.

Conclusione

L’isolamento multi-tenant robusto per carichi di lavoro LLM in Plesk nel 2026 non è una features, è una necessità. Ho consolidato questa procedura attraverso tre strati: container sandboxing (namespace, ResourceQuota, NetworkPolicy), GPU resource quotas (scheduling fair, time slicing), e cost attribution con kill switch automatici.

Il beneficio: i tuoi tenant possono eseguire agenti LLM in autonomia senza rischio di interferenza, overshooting di budget, o data leaks. Il margine di hosting rimane prevedibile e le tue metriche di costo sono oneste fino a livello di singolo token.

Se state affrontando il problema opposto (tenant che vi chiedono di ridurre i loro costi LLM), questo articolo vi dà i lever per mostrare esattamente dove il loro agente spende i token. Buona implementazione!

Share: