Kubernetes Cost Optimization: Jak ušetřit 40–60 % na cloud infrastruktuře v roce 2026¶
Kubernetes se stal standardem pro provoz enterprise aplikací. S tím ale přichází i výzva, kterou mnoho týmů podceňuje: náklady na cloud infrastrukturu rostou rychleji než produktivita. Průzkum CNCF 2026 ukazuje, že průměrná organizace přeplácí za Kubernetes infrastrukturu 35–50 % — a to pouze kvůli špatně nastaveným resource requests, nepoužívanému compute a absenci FinOps kultury.
Tento článek je praktický průvodce, jak tyto ztráty eliminovat.
Kde peníze mizí — anatomie Kubernetes waste¶
Over-provisioning resource requests¶
Největší zdroj plýtvání. Vývojáři konzervativně nastavují requests a limits, protože nikdo nechce, aby jim aplikace OOMkilloval. Výsledek: průměrné využití CPU v clusteru bývá 15–25 %, paměti 40–60 %.
# Typický "safe" setting od vývojáře
resources:
requests:
cpu: "500m" # Reálně aplikace používá 50m
memory: "512Mi" # Reálně 120Mi
limits:
cpu: "2000m"
memory: "2Gi"
Tento pod zabírá billing slot pro 500m CPU a 512Mi RAM — i když 90 % toho nikdy nevyužije.
Idle namespaces a zombie workloady¶
Development a staging prostředí běží 24/7, přestože jsou aktivní 8 hodin denně. Zapomenuté jobs, completed CronJobs s historií, staré ReplicaSets — to vše platíte.
Neoptimální instance typy¶
Spuštění memory-intensive workloadu na compute-optimized instanci (nebo naopak) — zaplatíte za kapacitu, kterou nemůžete využít.
Resource Optimization — konkrétní kroky¶
1. Goldilocks — automatický návrh resource requests¶
Goldilocks analyzuje skutečné využití přes VPA (Vertical Pod Autoscaler) a doporučuje správné hodnoty.
# Instalace
helm repo add fairwinds-stable https://charts.fairwinds.com/stable
helm install goldilocks fairwinds-stable/goldilocks \
--namespace goldilocks \
--create-namespace
# Označit namespace pro analýzu
kubectl label namespace production goldilocks.fairwinds.com/enabled=true
# Goldilocks dashboard
kubectl -n goldilocks port-forward svc/goldilocks-dashboard 8080:80
Dashboard ukáže pro každý deployment doporučené requests/limits na základě skutečného P50/P99 využití za posledních N dní.
2. VPA (Vertical Pod Autoscaler) v recommendation mode¶
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-service-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-service
updatePolicy:
updateMode: "Off" # Jen doporučení, neaplikuje automaticky
resourcePolicy:
containerPolicies:
- containerName: api
minAllowed:
cpu: 50m
memory: 64Mi
maxAllowed:
cpu: 2000m
memory: 4Gi
controlledResources: ["cpu", "memory"]
# Zobrazit doporučení
kubectl describe vpa api-service-vpa -n production
# Hledejte: "Target" hodnoty pro cpu a memory
3. HPA s custom metrikami¶
Horizontal Pod Autoscaler na základě custom business metrik (ne jen CPU):
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-service-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-service
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "100"
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # 5 min před scale-down
policies:
- type: Percent
value: 25
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
4. KEDA pro event-driven autoscaling¶
Pro workloady řízené frontami (Kafka, SQS, Redis):
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: worker-scaledobject
namespace: production
spec:
scaleTargetRef:
name: queue-worker
minReplicaCount: 0 # Scale to zero!
maxReplicaCount: 50
triggers:
- type: aws-sqs-queue
metadata:
queueURL: https://sqs.eu-west-1.amazonaws.com/123456789/my-queue
queueLength: "10" # 1 replica per 10 messages
awsRegion: eu-west-1
Scale-to-zero je klíčový — worker bez zpráv = 0 podů = 0 nákladů.
Node Optimization¶
Spot/Preemptible instances s Karpenter¶
Karpenter je moderní node autoscaler od AWS, který umí inteligentně mixovat spot a on-demand instance:
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: default
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot", "on-demand"]
- key: kubernetes.io/arch
operator: In
values: ["amd64", "arm64"] # ARM = levnější
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"]
nodeClassRef:
name: default
disruption:
consolidationPolicy: WhenUnderutilized
consolidateAfter: 30s
limits:
cpu: 1000
memory: 4000Gi
Karpenter konsoliduje uzly automaticky — pokud 3 workloady sedí na 3 nodech, přepakuje je na 1 node a zbývající 2 vypne.
Pravidelné čištění idle nodů¶
#!/bin/bash
# Skript pro identifikaci nevyužitých nodů
kubectl get nodes -o json | jq -r '
.items[] |
select(.status.conditions[] | select(.type=="Ready" and .status=="True")) |
{
name: .metadata.name,
cpu_capacity: .status.capacity.cpu,
mem_capacity: .status.capacity.memory,
age: .metadata.creationTimestamp
}
' | jq -r '.name + " | CPU: " + .cpu_capacity + " | Age: " + .age'
# Zkontrolovat skutečné využití přes metrics-server
kubectl top nodes --sort-by=cpu | awk '$3+0 < 20 {print "LOW CPU:", $0}'
Namespace a Environment Cleanup¶
Automatické mazání development prostředí¶
#!/usr/bin/env python3
"""Auto-cleanup idle development namespaces"""
import subprocess
import json
from datetime import datetime, timezone, timedelta
def get_namespace_last_activity(namespace: str) -> datetime:
"""Zjistí poslední aktivitu v namespace podle events"""
result = subprocess.run(
["kubectl", "get", "events", "-n", namespace,
"--sort-by=.lastTimestamp", "-o", "json"],
capture_output=True, text=True
)
events = json.loads(result.stdout)
if not events["items"]:
return datetime.min.replace(tzinfo=timezone.utc)
last_event = events["items"][-1]
timestamp = last_event["lastTimestamp"]
return datetime.fromisoformat(timestamp.replace("Z", "+00:00"))
def cleanup_idle_namespaces(max_idle_hours: int = 48):
"""Smaže namespaces s prefixem 'dev-' které jsou idle více než N hodin"""
result = subprocess.run(
["kubectl", "get", "namespaces", "-o", "json"],
capture_output=True, text=True
)
namespaces = json.loads(result.stdout)
now = datetime.now(timezone.utc)
cutoff = now - timedelta(hours=max_idle_hours)
for ns in namespaces["items"]:
name = ns["metadata"]["name"]
if not name.startswith("dev-"):
continue
last_activity = get_namespace_last_activity(name)
if last_activity < cutoff:
idle_hours = (now - last_activity).total_seconds() / 3600
print(f"Deleting idle namespace {name} (idle {idle_hours:.0f}h)")
subprocess.run(["kubectl", "delete", "namespace", name])
if __name__ == "__main__":
cleanup_idle_namespaces(max_idle_hours=48)
CronJob pro noční scale-down¶
# Scale staging na 0 replik přes noc
apiVersion: batch/v1
kind: CronJob
metadata:
name: staging-scale-down
namespace: staging
spec:
schedule: "0 20 * * 1-5" # Pondělí-Pátek 20:00
jobTemplate:
spec:
template:
spec:
serviceAccountName: scaler-sa
containers:
- name: kubectl
image: bitnami/kubectl:latest
command:
- /bin/sh
- -c
- |
kubectl scale deployment --all --replicas=0 -n staging
kubectl scale statefulset --all --replicas=0 -n staging
restartPolicy: OnFailure
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: staging-scale-up
namespace: staging
spec:
schedule: "0 8 * * 1-5" # Pondělí-Pátek 8:00
jobTemplate:
spec:
template:
spec:
serviceAccountName: scaler-sa
containers:
- name: kubectl
image: bitnami/kubectl:latest
command:
- /bin/sh
- -c
- |
kubectl scale deployment --all --replicas=2 -n staging
restartPolicy: OnFailure
FinOps — Kubecost a cost visibility¶
Kubecost pro granulární cost tracking¶
# Instalace Kubecost
helm repo add kubecost https://kubecost.github.io/cost-analyzer/
helm install kubecost kubecost/cost-analyzer \
--namespace kubecost \
--create-namespace \
--set kubecostToken="your-token" \
--set prometheus.server.persistentVolume.size=50Gi
Kubecost umožňuje vidět náklady per namespace, deployment, label nebo team — klíčové pro chargeback model.
Cost allocation labels¶
# Každý workload musí mít cost labels
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-service
labels:
app: api-service
team: backend # Tým
cost-center: "CC-1042" # Nákladové středisko
environment: production
product: core-platform
# Kubecost query per team
curl "http://kubecost/model/allocation?window=30d&aggregate=label:team&idle=true" | \
jq '.data[0] | to_entries | sort_by(-.value.totalCost) |
.[] | "\(.key): $\(.value.totalCost | round)"'
Výsledky — reálné čísla¶
Po implementaci těchto opatření v typickém enterprise clusteru:
| Optimalizace | Typická úspora |
|---|---|
| Right-sizing requests (Goldilocks) | 20–30 % |
| Spot instances (70 % workloadů) | 60–70 % na compute |
| Scale-to-zero pro dev/staging | 40–60 % na nonprod |
| Karpenter konsolidace | 10–20 % |
| Cleanup idle resources | 5–15 % |
| Celkem | 40–60 % celkových nákladů |
Implementační plán¶
Týden 1–2: Viditelnost - Nasadit Kubecost nebo OpenCost - Přidat cost allocation labels na všechny workloady - Audit resource utilization přes Goldilocks
Týden 3–4: Quick wins - Right-size top 20 nejvíce over-provisioned deploymentů - Zapnout scale-to-zero pro dev/staging přes noc - Vyčistit zombie workloady
Měsíc 2: Automatizace - Karpenter nebo Cluster Autoscaler s spot pool - HPA/KEDA pro klíčové services - Automatický namespace cleanup
Měsíc 3+: FinOps kultura - Chargeback reporty per team - Cost budgets a alerting - Kvartální review s development týmy
Závěr¶
Kubernetes cost optimization není jednorázová akce — je to kontinuální proces. Začněte viditelností (Kubecost), pokračujte right-sizingem (Goldilocks + VPA), a automatizujte škálování (HPA, KEDA, Karpenter). Výsledkem je infrastruktura, která roste s vašimi potřebami, ne navzdory nim.
CORE SYSTEMS pomáhá enterprise organizacím implementovat FinOps kulturu a Kubernetes cost governance. Kontaktujte nás pro audit vaší infrastruktury.
Potřebujete pomoc s implementací?
Naši experti vám pomohou s návrhem, implementací i provozem. Od architektury po produkci.
Kontaktujte nás