FinOps per Kubernetes: Rightsizing, Spot Instances e Riduzione Costi
Il 68% delle organizzazioni che usano Kubernetes in produzione spende il 20-40% in piu del necessario (CNCF Cost Survey 2026). Non perche Kubernetes sia inefficiente, ma perche il provisioning generoso e facile, e il rightsizing richiede disciplina operativa. Il risultato e cluster con CPU utilizzo medio al 15-20% e memoria all'30-40%: risorse pagate ma non usate.
FinOps per Kubernetes e la pratica di misurare, ottimizzare e governare la spesa del cluster. In questo articolo vedremo come usare Kubecost per la visibilita dei costi per namespace, come il VPA aiuta nel rightsizing automatico delle requests, come Karpenter ottimizza il bin-packing dei nodi con spot instances, e quali policy implementare a livello organizzativo per mantenere i costi sotto controllo nel tempo.
Cosa Imparerai
- Installare e configurare Kubecost per visibilita dei costi per namespace/team
- VPA in modalita Off: raccomandazioni automatiche di rightsizing senza disrupzione
- Karpenter: consolidation per bin-packing ottimale dei nodi
- Spot instances su AWS/GCP/Azure con Karpenter e gestione delle interruzioni
- ResourceQuota come strumento di budget per team
- Alert Prometheus per spreco e anomalie di costo
- Come costruire un sistema di chargeback per i team
- Benchmark: risparmi reali ottenibili con ogni tecnica
Perche Kubernetes Tende ad essere Overprovisioned
Prima di ottimizzare, bisogna capire le cause del problema:
- Requests conservative: I team mettono requests alte "per sicurezza" (es. 1 CPU per un'app che ne usa 0.1). Le requests determinano il numero di nodi necessari
- Limits = requests: Molti template CI/CD impostano limits uguale a requests, creando un over-provisioning sistematico
- Namespace vuoti: Namespace di sviluppo con Pod "Hello World" che restano accesi 24/7
- Nodi oversized: Istanze cloud grandi con bin-packing inefficiente (un Pod da 2 vCPU su un nodo da 32 vCPU)
- Nessuna visibilita per team: Se i team non vedono il loro costo, non hanno incentivo a ottimizzare
Kubecost: Visibilita dei Costi per Namespace
Kubecost e lo strumento FinOps piu usato nell'ecosistema Kubernetes. Raccoglie metriche da Prometheus, recupera i prezzi delle istanze cloud (AWS, GCP, Azure) e calcola il costo allocato per namespace, deployment, service account e label.
Installazione Kubecost
# Installa Kubecost con Helm
helm repo add kubecost https://kubecost.github.io/cost-analyzer/
helm repo update
helm install kubecost kubecost/cost-analyzer \
--namespace kubecost \
--create-namespace \
--set kubecostToken="your-token-here" \
--set prometheus.enabled=true \
--set grafana.enabled=true \
--set global.prometheus.enabled=false \
--set global.prometheus.fqdn="http://prometheus.monitoring.svc:9090"
# Port-forward per accedere alla UI
kubectl port-forward svc/kubecost-cost-analyzer 9090:9090 -n kubecost &
# Apri: http://localhost:9090
# Alternativa open-source senza token: OpenCost
helm install opencost opencost/opencost \
--namespace opencost \
--create-namespace
Configurare il Chargeback per Team
# Kubecost usa i label Kubernetes per il cost allocation
# Configura label standard per tutti i workload:
# Nel values.yaml di ogni applicazione (Helm):
podLabels:
team: "team-alpha"
cost-center: "CC-2024-ENG"
environment: "production"
product: "checkout-service"
# Kubecost mostra automaticamente i costi aggregati per questi label.
# Es: costo totale team-alpha nel mese = CPU + Memoria + Storage + GPU + Networking
# API Kubecost per report automatici (da inviare via email settimanale):
curl "http://kubecost:9090/model/allocation?window=7d&aggregate=label:team&accumulate=true" \
| jq '.data[0] | to_entries[] | {team: .key, cost: .value.totalCost}'
VPA per Rightsizing Automatico
Il Vertical Pod Autoscaler (VPA) analizza il consumo storico di CPU e
memoria dei Pod e calcola le requests ottimali. In modalita Off (raccomandazione
senza applicazione automatica) e lo strumento piu sicuro per il rightsizing: fornisce
numeri precisi senza rischio di disruption.
# Installa VPA
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
./hack/vpa-up.sh
# Oppure con Helm
helm repo add fairwinds-stable https://charts.fairwinds.com/stable
helm install vpa fairwinds-stable/vpa \
--namespace vpa \
--create-namespace
---
# vpa-recommendation.yaml - modalita Off: solo raccomandazioni
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-service-vpa
namespace: team-alpha
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-service
updatePolicy:
updateMode: "Off" # non applica automaticamente, solo raccomanda
resourcePolicy:
containerPolicies:
- containerName: api
minAllowed:
cpu: 50m
memory: 64Mi
maxAllowed:
cpu: "4"
memory: 8Gi
# Leggi le raccomandazioni dopo 24-48 ore:
kubectl describe vpa api-service-vpa -n team-alpha
# Output:
# Container Recommendations:
# Container Name: api
# Lower Bound:
# Cpu: 120m
# Memory: 256Mi
# Target: <-- usa questi valori nelle requests
# Cpu: 250m
# Memory: 512Mi
# Upper Bound:
# Cpu: 800m
# Memory: 1500Mi
# Uncapped Target:
# Cpu: 230m
# Memory: 480Mi
Script per Analisi Rightsizing in Bulk
# Trova tutti i Deployment con requests > 2x il consumo reale
kubectl get vpa -A -o json | jq -r '
.items[] |
.metadata.namespace + "/" + .metadata.name + ": " +
(.status.recommendation.containerRecommendations[]? |
"target CPU=" + .target.cpu + " Mem=" + .target.memory)
'
# Script bash per generare report di rightsizing
#!/bin/bash
echo "=== Rightsizing Recommendations ==="
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
echo "--- Namespace: $ns ---"
kubectl get vpa -n "$ns" -o custom-columns=\
"NAME:.metadata.name,\
TARGET-CPU:.status.recommendation.containerRecommendations[0].target.cpu,\
TARGET-MEM:.status.recommendation.containerRecommendations[0].target.memory" \
2>/dev/null
done
Karpenter: Consolidation e Spot Instances
Karpenter ottimizza i costi da due direzioni: usa spot instances per ridurre il costo per nodo del 60-70%, e consolida i nodi per eliminare quelli sottoutilizzati (bin-packing).
NodePool con Spot + Consolidation
# karpenter-cost-optimized.yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: cost-optimized
spec:
template:
spec:
nodeClassRef:
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
name: default
requirements:
# Tipi di istanza ottimali per bin-packing (diverse size)
- key: node.kubernetes.io/instance-type
operator: In
values:
- m7i.xlarge # 4 vCPU, 16GB ~$0.20/h on-demand, ~$0.05/h spot
- m7i.2xlarge # 8 vCPU, 32GB
- m7i.4xlarge # 16 vCPU, 64GB
- m7i.8xlarge # 32 vCPU, 128GB
- c7i.2xlarge # CPU-optimized per workload compute-intensive
- r7i.2xlarge # Memory-optimized per carichi con alta RAM
# Mix spot e on-demand con peso
- key: karpenter.sh/capacity-type
operator: In
values:
- spot # priorita spot (economico)
- on-demand # fallback on-demand
disruption:
consolidationPolicy: WhenUnderutilized
consolidateAfter: 1m # consolida subito i nodi sottoutilizzati
limits:
cpu: "1000" # max 1000 vCPU totali
memory: "4000Gi" # max 4TB RAM totale
---
# Configurazione spot interruption handler
# Necessario per gestire le interruzioni spot (AWS invia 2 min di preavviso)
helm install aws-node-termination-handler \
eks/aws-node-termination-handler \
--namespace kube-system \
--set enableSqsTerminationDraining=true \
--set queueURL=https://sqs.eu-west-1.amazonaws.com/123456789/NodeTerminationHandler
Analisi dei Risparmi con Karpenter
# Verifica quale percentuale dei nodi sono spot
kubectl get nodes -o json | jq '
[.items[] |
{type: .metadata.labels["karpenter.sh/capacity-type"],
instance: .metadata.labels["node.kubernetes.io/instance-type"]}
] |
group_by(.type) |
map({type: .[0].type, count: length})'
# Output esempio:
# [{"type": "on-demand", "count": 3}, {"type": "spot", "count": 12}]
# 80% dei nodi sono spot -> risparmio medio 65% = -$2400/mese
# Kubecost: visualizza costo spot vs on-demand storicamente
# Vai in Cost Allocation > Filter by node type
Namespace Budget: ResourceQuota come Strumento FinOps
Le ResourceQuota non servono solo per l'isolamento tecnico - sono anche uno strumento di governance finanziaria. Assegnando quote basate su budget, si crea un sistema di incentivi che spinge i team a ottimizzare:
# budget-quota-team.yaml
# Calcola le quote basandoti sul budget mensile del team
# Budget: 500 EUR/mese (circa 1500 CPU-hours a 0.033 EUR/CPU-hour)
# Assumendo utilizzo medio 50%: requests max = 2000 CPU-hours / 720 ore = 2.8 vCPU mean
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-alpha-budget-quota
namespace: team-alpha
annotations:
finops/monthly-budget-eur: "500"
finops/last-reviewed: "2026-01-15"
finops/owner: "alice@company.com"
spec:
hard:
# Basato su budget EUR/mese
requests.cpu: "6" # ~500 EUR/mese a tasso medio spot
requests.memory: "12Gi" # proporzionale
requests.storage: "200Gi"
---
# Alert quando il team si avvicina al budget
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: finops-budget-alerts
namespace: monitoring
spec:
groups:
- name: finops
rules:
- alert: TeamBudgetUsageHigh
expr: |
kubecost_namespace_allocation_cpu_cost_hourly * 24 * 30 +
kubecost_namespace_allocation_memory_cost_hourly * 24 * 30 > 400
for: 1h
labels:
severity: warning
annotations:
summary: "Team {{ $labels.namespace }}: costo mensile proiettato > 400 EUR"
Strategie di Risparmio per Workload Diversi
| Tipo Workload | Strategia | Risparmio Atteso |
|---|---|---|
| Web API (stateless) | Spot + HPA + rightsizing VPA | 50-65% |
| Job batch ML | Spot + checkpoint + scale-to-zero | 60-70% |
| Database (stateful) | On-demand reserved + rightsizing | 20-30% |
| CI/CD runners | Spot + scale-to-zero (KEDA) | 70-80% |
| Servizi critici 24/7 | Reserved instances + rightsizing | 30-40% |
Ottimizzazione dei Job CI/CD con KEDA + Spot
# Runners CI/CD sono idle la maggior parte del tempo:
# scala a 0 quando non ci sono job, usa spot
# KEDA ScaledJob per runner GitHub Actions
apiVersion: keda.sh/v1alpha1
kind: ScaledJob
metadata:
name: github-actions-runner
namespace: actions-runners
spec:
jobTargetRef:
template:
spec:
containers:
- name: runner
image: summerwind/actions-runner:latest
resources:
limits:
cpu: "2"
memory: "4Gi"
nodeSelector:
karpenter.sh/capacity-type: spot # usa spot per runners
tolerations:
- key: "spot"
operator: "Exists"
effect: "NoSchedule"
pollingInterval: 30
minReplicaCount: 0 # scala a 0 quando nessun job
maxReplicaCount: 20 # massimo 20 runner paralleli
triggers:
- type: github-runner
metadata:
owner: "myorg"
repos: "myrepo"
targetWorkflowQueueLength: "1"
Dashboard FinOps con Grafana
# PromQL queries per dashboard FinOps Kubernetes
# Costo orario totale del cluster
sum(
kube_pod_container_resource_requests{resource="cpu"} * 0.033 +
kube_pod_container_resource_requests{resource="memory"} / 1073741824 * 0.004
) by (namespace)
# Efficienza CPU per namespace (utilizzo reale / requested)
sum(rate(container_cpu_usage_seconds_total[5m])) by (namespace) /
sum(kube_pod_container_resource_requests{resource="cpu"}) by (namespace) * 100
# Pod con requests molto piu alte del consumo reale (spreco > 70%)
(
kube_pod_container_resource_requests{resource="cpu"} -
rate(container_cpu_usage_seconds_total[24h])
) / kube_pod_container_resource_requests{resource="cpu"} > 0.7
# Importa il dashboard Grafana Kubecost: ID 11270
Best Practices FinOps per Kubernetes
Checklist FinOps Mensile
- Review VPA recommendations: Analizza le raccomandazioni VPA e applica il rightsizing ai Top 10 sprechi
- Controlla nodi idle: Nodi con utilizzo CPU e memoria < 10% per 24 ore devono essere terminati (Karpenter lo fa automaticamente)
- Verifica copertura spot: L'obiettivo e 70-80% dei nodi non-stateful su spot instances
- Report di chargeback: Invia report mensile di costo per team tramite API Kubecost → email o Slack
- Review ResourceQuota: Aumenta le quote solo con giustificazione business, non per default
- Scale-to-zero per ambienti dev: Gli ambienti di sviluppo dovrebbero essere spenti fuori orario lavorativo (-65% costo)
Conclusioni e Prossimi Passi
Il FinOps per Kubernetes non e un'ottimizzazione una-tantum: e un processo continuo che richiede visibilita (Kubecost), raccomandazioni intelligenti (VPA), provisioning efficiente (Karpenter con spot) e governance organizzativa (ResourceQuota come budget). Implementando tutte queste tecniche insieme, le organizzazioni tipicamente ottengono un risparmio del 35-55% sulla bolletta cloud Kubernetes nei primi 3-6 mesi.
Il passo successivo e integrare il FinOps Kubernetes nel ciclo di sviluppo: i developer dovrebbero vedere il costo stimato del loro deployment prima del merge in produzione - un approccio "shift-left" applicato ai costi.

Commenti
Caricamento commenti...
Accedi per commentare