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