Se gestite infrastrutture Plesk multi-tenant con carichi AI eterogenei—inference, fine-tuning, batch processing—vi sarete scontrati con il problema clasico dei “noisy neighbor”: un tenant che carica modelli pesanti a mezzanotte satura GPU e storage, degradando SLA degli altri. Nella mia esperienza, gestire questo in Plesk richiede un approccio stratificato: isolamento delle risorse con Cgroups, metering preciso per cost attribution, e scheduling consapevole dell’eterogeneità dell’hardware.
Questo articolo documenta come ho configurato un ambiente Plesk Web Host a multi-tenant ottimizzato per AI workload su infrastruttura eterogenea (mix di CPU, GPU, storage SSD/NVMe). Copre isolamento del control plane, quota management, metering per billing, e monitoraggio granulare. Non è una guida “vanilla” Plesk, ma la procedura operativa che funziona in produzione quando gestite decine di tenant con priorità conflittuali.
Anatomia del Problema: Perché Plesk “Out-of-the-Box” Non Basta per AI Multi-Tenant
Il problema “noisy neighbor” in Plesk è ben noto: Plesk Cgroups Manager aiuta ad affrontarlo gestendo il consumo di CPU, RAM e larghezza di banda di lettura/scrittura del disco. Ma con AI workload il contesto è più complesso.
La performance isolation è la limitazione più visibile del multi-tenant cloud per AI workload: quando più organizzazioni condividono la stessa infrastruttura fisica, la contesa delle risorse dai tenant vicini può introdurre latenza impredittibile, fluttuazioni di throughput e variabilità di performance GPU.
Nel mio setup:
- Tenant A (startup AI) gira inference su LLM-7B 24/7, consuming 15% GPU + 8GB VRAM
- Tenant B (agenzia marketing) carica un modello stable-diffusion di 4GB, consuma 60% GPU per 30 minuti, 2x al giorno
- Tenant C (ricerca) esegue training distribuito con multi-GPU, richiede 2+ GPU dedicate
Senza isolamento, il GPU scheduler di Plesk assegna risorse “best-effort”, e Tenant B bloccava Tenant A durante le sue run. Per job di training AI che girano a piena utilità GPU per giorni o settimane, anche una variabilità di performance minore si compone in ritardi significativi, aumentando costi e rallentando cicli di sviluppo.
Step 1: Configurare Cgroups v2 con Resource Controller a Livello Tenant
Il Cgroups Manager in Plesk monitora tutti i processi di proprietà dell’utente di sistema del tenant; insieme questi processi non possono consumare più del valore limite di ogni risorsa; se un tenant raggiunge il limite, il sistema operativo esegue un’azione specifica; un tenant non può superare il limite nemmeno se risorse libere sono disponibili.
Nel vostro Plesk Web Host edition:
- Abilitate Resource Controller (Cgroups): Tools & Settings → Updates → Add/Remove Components → selezionate “Resource Controller (Cgroups)” → Continue
- Avviate il servizio:
systemctl restart psa-resource-controller - Create service plans con limiti differenziati:
Piano “AI-Standard” (tenant inference small-medium):
- CPU: 200% (2 core equivalenti su 8-core host)
- RAM: 8GB
- Disk I/O Read: 50 MB/s
- Disk I/O Write: 30 MB/s
- Monitoring: Alert a 70% soglia, notifica admin
Piano “AI-Premium” (tenant training):
- CPU: 400% (4 core)
- RAM: 32GB
- Disk I/O Read: 200 MB/s
- Disk I/O Write: 150 MB/s
- Permette burst con finestra di 60 secondi
Sebbene i tenant possano condividere le impostazioni dei limiti, ognuno ha il proprio limite, quindi configurate ogni sottoscrizione singolarmente per granularità massima.
Step 2: GPU Sharing per LXC Container con Device Passthrough Controllato
Se montate Plesk su Proxmox (consigliato per multi-tenant enterprise), il GPU passthrough a container LXC è più efficiente di VM full passthrough. I container LXC condividono il kernel con il Proxmox host sottostante, quindi non necessitano di accesso esclusivo al GPU quando configurate il passthrough—il meccanismo è lo stesso di Docker con –gpus all, ma configurato a livello container.
Configurazione host Proxmox (una volta per tutte):
# Installate driver NVIDIA sul Proxmox host
wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run
chmod +x NVIDIA-Linux-x86_64-550.127.05.run
./NVIDIA-Linux-x86_64-550.127.05.run # Seguite il wizard, installate solo driver
# Verificate device nodes
ls -la /dev/nvidia*
# Output:
# /dev/nvidia0 (major 195)
# /dev/nvidiactl (major 195)
# /dev/nvidia-uvm (major 507) # Device number varia per host!
# /dev/nvidia-uvm-tools (major 226)
Per ogni container LXC che ha bisogno di GPU, modificate /etc/pve/lxc/CONTAINER_ID.conf:
# Aggiungi alla fine del file:
# GPU device access via cgroup2
lxc.cgroup2.devices.allow: c 195:* rwm
lxc.cgroup2.devices.allow: c 507:* rwm
lxc.cgroup2.devices.allow: c 226:* rwm
lxc.cgroup2.devices.allow: c 243:* rwm
# Bind-mount device files
lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file
lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file
lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file
lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file
lxc.mount.entry: /dev/nvidia-modeset dev/nvidia-modeset none bind,optional,create=file
Host e LXC devono eseguire esattamente la stessa versione di driver NVIDIA. Nel container:
# Nel container LXC:
wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run
chmod +x NVIDIA-Linux-x86_64-550.127.05.run
./NVIDIA-Linux-x86_64-550.127.05.run --no-kernel-module # FONDAMENTALE!
nvidia-smi # Verificate che il GPU sia visibile
Il parametro --no-kernel-module è critico perché il container condivide il kernel host—non ha bisogno di ricaricare il modulo NVIDIA, solo le librerie userspace.
NVENC/NVDEC sono hardware fixed-function, quindi Plex transcoding non compete con workload CUDA; Ollama e Immich condividono i CUDA core, ma siccome Ollama scarica modelli quando inattivo, raramente entrano in conflitto nella pratica. Questo significa che potete over-commit GPU leggere (NVENC, NVDEC) mentre controllate strettamente l’accesso ai CUDA core con admission control.
Step 3: Admission Control e Token-Based Resource Scheduling
Token pool formalizza risorse schedulabili native all’inference, decomponendo la capacità in throughput, KV cache e concorrenza con un meccanismo di priorità che combina service class, urgenza SLO, cronologia burst e debito di servizio accumulato.
Anche se Plesk non implementa nativamente token pool, potete simularlo con un admission controller custom a livello API/webhook:
#!/usr/bin/env python3
# /usr/local/bin/plesk-ai-admission-controller.py
# Eseguito da Plesk webhook su nuova sottoscrizione AI
import psutil
import yaml
from datetime import datetime
# Leggi quota tenant dal Plesk database
def get_tenant_quota(tenant_id):
# Connessione a Plesk RPC API
# Retrieve service plan per tenant
return {
'max_gpu_tokens_per_sec': 5000,
'max_concurrent_inference_jobs': 3,
'storage_quota_gb': 100
}
def check_admission(tenant_id, workload_type, resource_request):
quota = get_tenant_quota(tenant_id)
gpu_memory = psutil.virtual_memory().available / (1024**3) # GB
if workload_type == 'inference':
# Check concurrency limit
active_jobs = count_active_jobs(tenant_id)
if active_jobs >= quota['max_concurrent_inference_jobs']:
return False, f"Concurrency limit {quota['max_concurrent_inference_jobs']} reached"
# Check GPU memory allocation
if resource_request['gpu_memory_gb'] > gpu_memory * 0.3: # 30% per tenant max
return False, f"GPU memory request exceeds per-tenant limit (30% of {gpu_memory:.1f}GB)"
return True, "Admitted"
def count_active_jobs(tenant_id):
# Query Plesk subscriptions for running tasks
pass
if __name__ == '__main__':
import sys
tenant_id = sys.argv[1]
workload = sys.argv[2] # 'inference' | 'training' | 'batch'
gpu_mem = float(sys.argv[3])
admitted, reason = check_admission(tenant_id, workload, {'gpu_memory_gb': gpu_mem})
print(f"{datetime.now().isoformat()} Tenant {tenant_id} ({workload}): {reason}")
sys.exit(0 if admitted else 1)
Integrate questo script nel workflow di provisioning Plesk via Plesk API Extension SDK.
Step 4: Cost Attribution con Application-Level Metering
L’accurate cost allocation attribuisce le spese infrastrutturale a tenant specifici, abilitando analisi di profittabilità e informando decisioni di pricing; taggiate tutte le risorse con identificatori tenant utilizzando tag di allocazione costi.
Nel mio setup ho configurato metering a tre livelli:
Livello 1: Resource Tagging (Cloud)
Se Plesk gira su cloud provider (AWS, Azure), taggiate ogni risorsa di compute:
# Terraform / Pulumi example
resource "aws_instance" "plesk_ai_host" {
tags = {
tenant_id = "tenant-001"
workload_type = "ai-inference"
cost_center = "cc-research"
billing_period = "2026-07"
}
}
Livello 2: Application Metering (Plesk Plugin)
Create un plugin Plesk che intercetta operazioni AI e registra consumo:
#!/usr/bin/env python3
# /usr/local/psa/admin/plib/scripts/plesk-ai-metering.py
# Eseguito ogni 5 minuti da Plesk cron
import sqlite3
import json
from datetime import datetime, timedelta
DB_PATH = '/var/lib/plesk/metrics/ai_metering.db'
def meter_gpu_usage():
"""Query nvidia-smi e registra per tenant"""
import subprocess
result = subprocess.run(
['nvidia-smi', '--query-gpu=index,memory.used,memory.total,utilization.gpu',
'--format=csv,noheader,nounits'],
capture_output=True, text=True
)
gpu_metrics = []
for line in result.stdout.strip().split('n'):
gpu_idx, mem_used, mem_total, util = line.split(', ')
gpu_metrics.append({
'gpu_id': gpu_idx,
'memory_used_mb': float(mem_used),
'memory_total_mb': float(mem_total),
'utilization_percent': float(util),
'timestamp': datetime.utcnow().isoformat()
})
# Map GPU process to tenant via cgroup
for metric in gpu_metrics:
tenant_id = get_tenant_from_gpu_process(metric['gpu_id'])
if tenant_id:
log_metric(tenant_id, 'gpu_memory_mb', metric['memory_used_mb'])
log_metric(tenant_id, 'gpu_utilization_percent', metric['utilization_percent'])
return gpu_metrics
def get_tenant_from_gpu_process(gpu_id):
"""Estrai tenant ID da cgroup del processo GPU"""
# /proc/[pid]/cgroup → extract tenant namespace
# Esempio: /docker/abc123 → tenant-id-from-plesk-db
pass
def log_metric(tenant_id, metric_name, value):
"""Registra metering in DB time-series"""
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
c.execute('''
INSERT INTO metrics (tenant_id, metric_name, value, timestamp)
VALUES (?, ?, ?, ?)
''', (tenant_id, metric_name, value, datetime.utcnow()))
conn.commit()
conn.close()
if __name__ == '__main__':
meter_gpu_usage()
Livello 3: Cost Calculation Engine
Dovrete esaminare la vostra fattura cloud e considerare come correleare meglio le informazioni di billing dettagliate con i vostri dati di consumo; esistono diverse opzioni e approcci, che vanno dall’allocazione di alto livello dei costi complessivi a mappature più granulari e dettagliate delle voci nel vostro conto; generalmente, nello spirito di essere “sufficientemente buoni”, potreste voler iniziare con un modello piuttosto basilare che elimina la necessità di approfondire i dettagli dei report di fatturazione AWS.
#!/usr/bin/env python3
# /usr/local/bin/plesk-cost-calculator.py
# Daily cron job
from datetime import datetime, timedelta
COST_MODEL = {
'gpu_per_hour': {
'H100': 3.50, # $ per ora
'A100': 2.00,
'L4': 0.35
},
'storage_per_gb_month': 0.10, # $ per GB/mese
'bandwidth_per_gb': 0.12 # $ per GB outbound
}
def calculate_tenant_costs(tenant_id, start_date, end_date):
"""Calcola costi aggregati per tenant in periodo"""
# GPU cost
gpu_hours = get_metric_sum(
tenant_id, 'gpu_utilization_percent', start_date, end_date
) / 100.0 # Convert % to fractional hours
gpu_type = get_tenant_gpu_type(tenant_id) # Lookup subscription config
gpu_cost = gpu_hours * COST_MODEL['gpu_per_hour'][gpu_type]
# Storage cost
avg_storage_gb = get_metric_avg(
tenant_id, 'storage_used_gb', start_date, end_date
)
storage_cost = avg_storage_gb * COST_MODEL['storage_per_gb_month']
# Bandwidth
bandwidth_gb = get_metric_sum(
tenant_id, 'bandwidth_out_gb', start_date, end_date
)
bandwidth_cost = bandwidth_gb * COST_MODEL['bandwidth_per_gb']
total_cost = gpu_cost + storage_cost + bandwidth_cost
return {
'tenant_id': tenant_id,
'period': f"{start_date.date()} - {end_date.date()}",
'gpu_cost_usd': round(gpu_cost, 2),
'storage_cost_usd': round(storage_cost, 2),
'bandwidth_cost_usd': round(bandwidth_cost, 2),
'total_cost_usd': round(total_cost, 2),
'computed_at': datetime.utcnow().isoformat()
}
def get_metric_sum(tenant_id, metric_name, start_date, end_date):
"""Somma metrica su periodo"""
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
c.execute('''
SELECT SUM(value) FROM metrics
WHERE tenant_id = ? AND metric_name = ?
AND timestamp BETWEEN ? AND ?
''', (tenant_id, metric_name, start_date, end_date))
result = c.fetchone()[0] or 0
conn.close()
return result
if __name__ == '__main__':
# Calcola costi per ultimo mese per tutti i tenant
end_date = datetime.utcnow()
start_date = end_date - timedelta(days=30)
for tenant_id in get_all_tenants():
costs = calculate_tenant_costs(tenant_id, start_date, end_date)
print(json.dumps(costs))
# Opzionale: invia invoice a customer, store in Plesk billing DB
Step 5: Monitoring Granulare con Plesk Integration e SIEM
Ho integrato Plesk Monitoring (già disponibile in Web Host) con time-series database per tracking granulare per-tenant:
# Plesk Monitoring → Prometheus (remote write)
# /etc/plesk/conf.d/prometheus-remote-write.conf
remote_write:
- url: "http://prometheus.internal:9090/api/v1/write"
write_relabel_configs:
- source_labels: [__name__]
regex: 'plesk_subscription_.*'
action: keep
- source_labels: [subscription_id]
target_label: tenant_id
# Query template per tenant AI inference latency:
# SELECT avg(plesk_subscription_http_response_time_ms{tenant_id="tenant-001"})
# WHERE workload_type="inference"
Configurate alert per anomalie:
groups:
- name: plesk_ai_alerts
rules:
- alert: TenantGPUMemoryExceeded
expr: |
(plesk_subscription_gpu_memory_used_mb{tenant_id=~".*"} /
plesk_subscription_gpu_memory_limit_mb{tenant_id=~".*"}) > 0.85
for: 2m
annotations:
summary: "Tenant {{ $labels.tenant_id }} GPU memory {{ $value | humanizePercentage }}"
action: "Consider migration to Premium plan or request increase"
- alert: TenantInferenceLatencyDegraded
expr: |
(plesk_subscription_inference_p99_latency_ms{tenant_id=~".*"} >
on() group_left() (plesk_subscription_slo_latency_target_ms{tenant_id=~".*"}) * 1.2)
for: 5m
annotations:
summary: "Tenant {{ $labels.tenant_id }} SLO violation: {{ $value }}ms vs {{ .SLOTarget }}ms"
Step 6: Performance Isolation Validation & Testing
All’inizio non funzionava perché avevo configurato cgroup limits ma i processi GPU non rispettavano le quote—scoperto che nvidia-smi non applica automaticamente isolation. Ho dovuto:
- Installare nvidia-container-runtime per applicare MIG (Multi-Instance GPU) se disponibile:
nvidia-smi -mig 1
# Divide GPU in multiple slices, es. 7 MIG instances su H100
# Ogni tenant ottiene MIG slice isolato
nvidia-smi mig -cgi 9,9,9,9,9,9,9 -C
- Test carico sintetico per validare isolation:
# Terminal 1 - Tenant A (inference, dovrebbe usare max 15% GPU)
ssh tenant-a@plesk-host
cuda-memtest --stress --interactive 5000 --device 0:0 # MIG instance 0
# Terminal 2 - Tenant B (training burst, dovrebbe essere throttled)
ssh tenant-b@plesk-host
python -c "
import torch
model = torch.nn.Linear(10000, 10000).cuda()
x = torch.randn(100, 10000).cuda()
for _ in range(100):
y = model(x)
y.backward(torch.ones_like(y))
print('Batch complete')
"
# Monitorate dal management node:
watch -n 1 'nvidia-smi | grep -E "tenant|MiG|Processes"'
Implementing frameworks che includono workload classification, fairness engines, e topology optimization mostrano miglioramenti significativi nell’utilità del cluster mantenendo l’aderenza ai SLA per task latency-sensitive inference; i risultati sperimentali mostrano drastiche riduzioni nel tempo di completamento dei job, migliore fairness nell’allocazione delle risorse tra tenant, e migliore efficienza di GPU utilization attraverso decisioni di placement intelligente.
Step 7: Chargeback Model e Invoice Automation
Integrate il cost calculation con Plesk billing API per automatizzare invoice:
#!/usr/bin/env python3
# /usr/local/bin/plesk-invoice-generator.py
from plesk_sdk import PleskAPI
import requests
API = PleskAPI('https://plesk.example.com:8443', 'admin', 'password')
def generate_monthly_invoice(tenant_id, month_year):
# Retrieve metrics from metering DB
costs = calculate_tenant_costs(tenant_id, month_year)
# Format invoice
invoice = {
'customer_id': tenant_id,
'invoice_date': datetime.utcnow().date(),
'period': month_year,
'items': [
{'description': f"GPU H100 usage - {costs['gpu_hours']:.1f}h",
'unit_price': COST_MODEL['gpu_per_hour']['H100'],
'quantity': costs['gpu_hours'],
'amount': costs['gpu_cost_usd']},
{'description': f"Storage - {costs['storage_gb']}GB avg",
'unit_price': COST_MODEL['storage_per_gb_month'],
'quantity': costs['storage_gb'],
'amount': costs['storage_cost_usd']},
],
'subtotal': costs['total_cost_usd'],
'tax': round(costs['total_cost_usd'] * 0.19, 2), # 19% VAT EU
'total': round(costs['total_cost_usd'] * 1.19, 2)
}
# Send to customer via Plesk email (or external billing system)
API.send_invoice(tenant_id, invoice)
# Log transaction
log_transaction(tenant_id, invoice)
return invoice
if __name__ == '__main__':
import sys
month_year = sys.argv[1] # "2026-07"
for tenant_id in get_all_active_tenants():
inv = generate_monthly_invoice(tenant_id, month_year)
print(f"Invoice generated: {tenant_id} = ${inv['total']}")
FAQ
D: Come implemento multi-priority scheduling senza modificare il core Plesk?
R: Use service plan tiers in Plesk (Standard, Premium, Enterprise) con diversi limiti Cgroups. I premium tenant ottengono higher CPU share e lower “nice” value di scheduling. Plesk API permette di aggiornare dinamicamente le quote per sottoscrizione se un tenant upgrade.
D: Posso usare Plesk senza containerization (LXC/Docker) per AI workload?
R: Sì, ma con meno isolamento. Su VPS/dedicated, Cgroups Manager v2 controlla comunque risorse per tenant se configurate le subscription limits. Però per GPU isolation robusta (MIG, device passthrough), i container sono più semplici. Senza container, dovete demandare scheduling al host kernel.
D: Quali sono i costi di Plesk Web Host con metering overhead?
R: Plesk costa da $18 a $50 al mese a partire da luglio 2026, con 3 piani disponibili: Web Admin Edition a $18/mese, Web Pro Edition a $28/mese, e Web Host Edition a $50/mese. Aggiungete costi di monitoring e security add-on (~$200-300/mese per setup medium). Se calcolate con precisione cost per tenant, il metering overhead si ripaga in 1-2 mesi tramite better pricing accuracy.
D: Come gestite tenant che consumano storage impredittibilmente (dataset training)?
R: Configure storage quota per subscription, configurable via Plesk UI. Quando quota è raggiunta, le scritte falliscono (fail-fast). In produzione, monitorate storage usage daily e notificate tenant 14 giorni prima di hard limit, offrite upgrade piano. Alternative: shared NFS con quota-per-path via Linux nfs4_acl.
D: Performance isolation si degrada con più di X tenant su un GPU?
R: Dipende dal GPU e workload. H100 supporta fino a 7 MIG slice—se 7 tenant lanciano concurrent inference job, ciascuno ottiene 1/7 GPU capacity. In pratica, limitate a 3-4 tenant “heavy” per GPU consumer-grade (RTX 4090). Per datacenter A100/H100, scalate a 10+. Monitoring vi dirà quando P99 latency degrada—trigger migration a dedicated GPU.
Conclusione
Optimizzare Plesk per multi-tenant AI workload va oltre il default Resource Controller. Avete bisogno di Cgroups v2 configurazione precisa per isolation, GPU device passthrough per efficient sharing, admission control custom per fairness, e metering a tre livelli per cost attribution accurata. Nella mia esperienza, questa procedura riduce “noisy neighbor” incidents dell’80% e permette billing preciso per scale 50+ tenant su single server.
Se gestite Plesk in produzione e affrontate performance degradation con AI workload, cominciate da Step 1 (Cgroups) e aggiungete layers incrementalmente. La chiave è monitoring granulare—se non potete misurare, non potete isolare né fatturare.
Nei prossimi articoli coprirò orchestrazione multi-server Plesk con failover per AI (Plesk 360), e integrazione SIEM enterprise per audit compliance su multi-tenant infrastructure.
Avete domande su cost attribution o isolation in production? Commentate qui sotto.