Γιατί το Kubernetes Κοστίζει Περισσότερο απ' ό,τι Νομίζετε
Αν τρέχετε workloads σε Kubernetes, πιθανότατα πληρώνετε 30-50% περισσότερο από ό,τι χρειάζεται. Κι αυτό δεν είναι υπερβολή — τα στοιχεία του 2026 δείχνουν ότι πάνω από το 68% των οργανισμών ξοδεύει 20-40% παραπάνω στο Kubernetes λόγω λανθασμένης διαμόρφωσης πόρων, idle instances και — ειλικρινά — απλής αμέλειας στο κομμάτι της βελτιστοποίησης.
Το πρόβλημα είναι δομικό. Στο Kubernetes, οι δαπάνες συμβαίνουν σε επίπεδο node (δηλαδή υποδομή), αλλά οι εφαρμογές τρέχουν σε επίπεδο pod. Αυτό το χάσμα δημιουργεί ένα τεράστιο blind spot — χωρίς τα κατάλληλα εργαλεία, είναι σχεδόν αδύνατο να ξέρεις ποια εφαρμογή ευθύνεται για ποιο κόστος. Κι έτσι τα bills φουσκώνουν χωρίς να καταλαβαίνει κανείς γιατί.
Λοιπόν, σε αυτόν τον οδηγό θα δούμε πώς να μειώσετε δραστικά τις δαπάνες Kubernetes χρησιμοποιώντας δύο εξαιρετικά αποτελεσματικά εργαλεία: τον Vertical Pod Autoscaler (VPA) για right-sizing σε επίπεδο pod και τον Karpenter για έξυπνο provisioning σε επίπεδο node. Όλα με πρακτικά YAML παραδείγματα και εντολές kubectl που μπορείτε να εφαρμόσετε αμέσως.
Κατανόηση του Προβλήματος: Resource Requests vs Πραγματική Χρήση
Το Kubernetes χρησιμοποιεί δύο μηχανισμούς ελέγχου πόρων:
- Resource Requests: Η ελάχιστη ποσότητα CPU/μνήμης που εγγυάται το Kubernetes σε κάθε pod. Ο scheduler χρησιμοποιεί αυτές τις τιμές για να αποφασίσει πού θα τοποθετήσει το pod.
- Resource Limits: Το μέγιστο CPU/μνήμης που μπορεί να καταναλώσει ένα pod. Αν ξεπεράσει το limit μνήμης, γίνεται OOMKill. Αν ξεπεράσει το CPU limit, εφαρμόζεται throttling.
Εδώ κρύβεται η ρίζα του προβλήματος: ο scheduler βασίζεται στα requests, όχι στην πραγματική κατανάλωση.
Φανταστείτε 200 pods που ζητούν 2 CPU cores αλλά χρησιμοποιούν μόνο 200m. Αυτό σημαίνει 400 δεσμευμένοι πυρήνες ενώ η πραγματική ζήτηση είναι μόλις 40. Ναι, πληρώνετε για 400 cores ενώ χρησιμοποιείτε 40. Αυτό πονάει.
Οι έλεγχοι του 2026 δείχνουν ότι 80-90% των pods έχουν υπερβολικά υψηλά CPU/memory requests «για κάθε ενδεχόμενο» (spoiler: αυτό το "κάθε ενδεχόμενο" σας κοστίζει μια περιουσία). Ακόμα και μια σχετικά μικρή μείωση 5-15% ανά pod μπορεί να οδηγήσει σε 30-60% εξοικονόμηση σε επίπεδο cluster μέσω καλύτερου bin-packing.
Βήμα 1: Μέτρηση Πραγματικής Χρήσης Πόρων
Πριν αλλάξετε οτιδήποτε, πρέπει να καταλάβετε τι πραγματικά χρησιμοποιούν τα pods σας. Ξεκινήστε με ένα γρήγορο snapshot — θα εκπλαγείτε με αυτό που θα βρείτε:
# Δείτε τη χρήση πόρων ανά pod
kubectl top pods -A --sort-by=cpu
# Δείτε τη χρήση ανά node
kubectl top nodes
# Συγκρίνετε requests vs πραγματική χρήση σε ένα namespace
kubectl top pods -n production --containers
Αν βλέπετε pods που ζητούν 1Gi μνήμη αλλά χρησιμοποιούν μόνο 50Mi, ή pods με 1000m CPU request αλλά πραγματική χρήση 100m — αυτά είναι οι πρωταρχικοί στόχοι σας για right-sizing. Κατά τη γνώμη μου, αυτό το πρώτο βήμα είναι και το πιο αποκαλυπτικό.
Για πιο λεπτομερή ανάλυση, εγκαταστήστε Prometheus και Grafana:
# Εγκατάσταση kube-prometheus-stack μέσω Helm
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install monitoring prometheus-community/kube-prometheus-stack \
--namespace monitoring --create-namespace
Χρησιμοποιήστε αυτό το PromQL query για να βρείτε pods με υπερβολικά requests:
# CPU: Αναλογία πραγματικής χρήσης / request (τιμές κάτω από 0.5 = σπατάλη)
sum(rate(container_cpu_usage_seconds_total{container!=""}[5m])) by (pod, namespace)
/
sum(kube_pod_container_resource_requests{resource="cpu"}) by (pod, namespace)
Βήμα 2: Εγκατάσταση και Ρύθμιση του VPA
Ο Vertical Pod Autoscaler (VPA) αναλύει την ιστορική χρήση πόρων και προσαρμόζει αυτόματα τα CPU/memory requests ώστε να ταιριάζουν στην πραγματική κατανάλωση. Σκεφτείτε τον σαν έναν "λογιστή" που παρακολουθεί τι πραγματικά ξοδεύετε και σας λέει τι χρειάζεστε στ' αλήθεια.
Εγκατάσταση VPA
# Κλωνοποίηση του VPA repository
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
# Εγκατάσταση VPA components
./hack/vpa-up.sh
# Επαλήθευση εγκατάστασης
kubectl get pods -n kube-system | grep vpa
Λειτουργίες VPA: Ποια να Επιλέξετε
Ο VPA υποστηρίζει τρεις λειτουργίες, κι η επιλογή μεταξύ τους είναι πιο σημαντική απ' ό,τι φαίνεται:
- Off (Recommendation only): Αναλύει τη χρήση και παράγει συστάσεις χωρίς να πειράζει τίποτα. Ιδανικό για αρχή — κι αυτό θα σας πρότεινα.
- Initial: Ορίζει τα requests μόνο κατά τη δημιουργία νέων pods. Πιο ασφαλές από το Auto.
- Auto: Κάνει αυτόματο restart στα pods για να εφαρμόσει νέες τιμές. Ισχυρό, αλλά χρειάζεται προσοχή.
Η σύστασή μας: Ξεκινήστε πάντα με updateMode: "Off" για να δείτε τις συστάσεις πριν εφαρμόσετε οτιδήποτε. Σοβαρά, μην πηδήξετε κατευθείαν στο Auto mode.
Παράδειγμα VPA σε Recommendation Mode
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Off" # Μόνο συστάσεις, χωρίς αυτόματες αλλαγές
resourcePolicy:
containerPolicies:
- containerName: my-app-container
minAllowed:
cpu: "50m"
memory: "64Mi"
maxAllowed:
cpu: "2000m"
memory: "4Gi"
controlledResources: ["cpu", "memory"]
Εφαρμόστε το και ελέγξτε τις συστάσεις:
# Εφαρμογή του VPA object
kubectl apply -f my-app-vpa.yaml
# Δείτε τις συστάσεις μετά από μερικές ώρες
kubectl describe vpa my-app-vpa -n production
Η έξοδος θα μοιάζει κάπως έτσι:
Recommendation:
Container Recommendations:
Container Name: my-app-container
Lower Bound:
Cpu: 25m
Memory: 131072k
Target:
Cpu: 50m
Memory: 262144k
Upper Bound:
Cpu: 200m
Memory: 524288k
Βλέπετε; Αν το pod σας ζητούσε 1 CPU core, η σύσταση λέει ότι χρειάζεται μόλις 50m. Αυτή είναι η εξοικονόμηση που αφήνετε στο τραπέζι.
Goldilocks: Dashboard για VPA Συστάσεις
Το Goldilocks της Fairwinds είναι ένα πολύ βολικό εργαλείο — σας δίνει ένα web dashboard που οπτικοποιεί τις συστάσεις VPA για κάθε deployment. Αντί να τρέχετε kubectl describe σε κάθε VPA object ξεχωριστά, τα βλέπετε όλα μαζεμένα:
# Εγκατάσταση Goldilocks
helm repo add fairwinds-stable https://charts.fairwinds.com/stable
helm install goldilocks fairwinds-stable/goldilocks \
--namespace goldilocks --create-namespace
# Ενεργοποίηση για ένα namespace
kubectl label namespace production goldilocks.fairwinds.com/enabled=true
Μετά την ενεργοποίηση, το Goldilocks δημιουργεί αυτόματα VPA objects για κάθε deployment στο namespace και παρουσιάζει τις συστάσεις σε ένα εύχρηστο dashboard. Ειλικρινά, αξίζει τα 5 λεπτά εγκατάστασης.
Βήμα 3: Ασφαλής Μετάβαση σε Auto Mode VPA
Αφού επαληθεύσετε ότι οι συστάσεις είναι λογικές (δώστε τουλάχιστον 1-2 εβδομάδες για αξιόπιστα δεδομένα), μπορείτε να ενεργοποιήσετε τον auto mode.
Σημαντικό: ο VPA κάνει restart στα pods για να εφαρμόσει αλλαγές. Αυτό σημαίνει ότι χρειάζεστε οπωσδήποτε PodDisruptionBudgets, αλλιώς ρισκάρετε downtime:
# Δημιουργία PodDisruptionBudget πρώτα
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
namespace: production
spec:
minAvailable: 2 # Τουλάχιστον 2 pods πρέπει να είναι πάντα διαθέσιμα
selector:
matchLabels:
app: my-app
# VPA σε Auto Mode με ασφαλή όρια
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Auto"
resourcePolicy:
containerPolicies:
- containerName: my-app-container
minAllowed:
cpu: "50m"
memory: "64Mi"
maxAllowed:
cpu: "2000m"
memory: "4Gi"
controlledResources: ["cpu", "memory"]
Σύγκρουση VPA με HPA — Μια Κλασική Παγίδα
Μια παγίδα στην οποία πέφτουν πολλοί: μη χρησιμοποιείτε VPA και HPA στο ίδιο metric. Αν ο HPA κλιμακώνει βάσει CPU utilization και ο VPA αλλάζει τα CPU requests, δημιουργείται ένα conflict που μπορεί να φέρει απρόβλεπτη συμπεριφορά. Η λύση:
- Χρησιμοποιήστε VPA για memory right-sizing
- Χρησιμοποιήστε HPA με custom metrics (requests/sec, queue depth) αντί για CPU
- Ή κρατήστε τον VPA σε recommendation mode και εφαρμόστε αλλαγές χειροκίνητα
Βήμα 4: Εγκατάσταση Karpenter για Έξυπνο Node Provisioning
Ενώ ο VPA βελτιστοποιεί σε επίπεδο pod, ο Karpenter κάνει τη "βρώμικη δουλειά" σε επίπεδο node. Αντί να λειτουργεί με Node Groups (όπως ο παλαιότερος Cluster Autoscaler), ο Karpenter κοιτάζει τα pending pods και ρωτά απευθείας το cloud provider API ποιο instance ταιριάζει καλύτερα — τόσο σε τιμή όσο και σε μέγεθος.
Τα βασικά πλεονεκτήματα:
- Provisioning σε 30-60 δευτερόλεπτα (αντί αρκετών λεπτών με τον Cluster Autoscaler)
- Αυτόματη επιλογή του πιο οικονομικού instance type
- Consolidation: Εντοπίζει underutilized nodes και μεταφέρει τα pods τους αλλού
- Native υποστήριξη Spot instances
NodePool για On-Demand Workloads
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: default
spec:
template:
metadata:
labels:
node-type: general
managed-by: karpenter
spec:
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
- key: node.kubernetes.io/instance-type
operator: In
values:
- m6i.large
- m6i.xlarge
- m6i.2xlarge
- m7i.large
- m7i.xlarge
- c6i.large
- c6i.xlarge
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: topology.kubernetes.io/zone
operator: In
values:
- eu-west-1a
- eu-west-1b
- eu-west-1c
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 30s
budgets:
- nodes: "10%"
- nodes: "0"
schedule: "0 9 * * 1-5" # Παύση consolidation στις 9πμ τις καθημερινές
duration: 8h # Για 8 ώρες (ώρες αιχμής)
limits:
cpu: "100"
memory: "400Gi"
Προσέξτε τα disruption budgets — η ρύθμιση nodes: "0" κατά τις ώρες αιχμής σημαίνει ότι ο Karpenter δεν θα κουνήσει κανένα node όσο τα συστήματά σας βρίσκονται υπό φόρτο. Αυτή η λεπτομέρεια κάνει τεράστια διαφορά στην πράξη.
NodePool για Spot Instances
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: spot-workloads
spec:
template:
metadata:
labels:
node-type: spot
managed-by: karpenter
spec:
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: spot-nodes
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
- key: node.kubernetes.io/instance-type
operator: In
values:
- m6i.large
- m6i.xlarge
- m5.large
- m5.xlarge
- c6i.large
- c6i.xlarge
- c5.large
- c5.xlarge
- r6i.large
- r6i.xlarge
- m7i.large
- m7i.xlarge
- c7i.large
- c7i.xlarge
- r7i.large
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
taints:
- key: spot-instance
value: "true"
effect: NoSchedule
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 10s # Πιο επιθετικό consolidation για Spot
budgets:
- nodes: "15%"
limits:
cpu: "200"
memory: "800Gi"
Σημειώστε τον μεγάλο αριθμό instance types — αυτό δεν είναι τυχαίο. Όσο περισσότερα instance types βάζετε, τόσο μεγαλύτερη η πιθανότητα να βρεθεί διαθέσιμο Spot capacity. Σε Spot, η ποικιλία σώζει.
Για να τρέξετε workloads σε Spot nodes, προσθέστε toleration στα deployments:
apiVersion: apps/v1
kind: Deployment
metadata:
name: batch-processor
spec:
replicas: 5
template:
spec:
tolerations:
- key: spot-instance
operator: Equal
value: "true"
effect: NoSchedule
containers:
- name: processor
image: my-app:latest
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
memory: "1Gi"
Βήμα 5: Εφαρμογή ResourceQuotas και LimitRanges
Ακόμα και με VPA και Karpenter στη θέση τους, χρειάζεστε guardrails σε επίπεδο namespace. Χωρίς αυτά, ένας developer μπορεί (κατά λάθος ή σκόπιμα) να ζητήσει 64Gi RAM σε ένα dev namespace. Δε θέλετε αυτό.
# ResourceQuota: Περιορίζει τη συνολική κατανάλωση ανά namespace
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: development
spec:
hard:
requests.cpu: "10"
requests.memory: "20Gi"
limits.cpu: "20"
limits.memory: "40Gi"
pods: "50"
# LimitRange: Ορίζει defaults και μέγιστα ανά pod
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: development
spec:
limits:
- type: Container
default:
cpu: "500m"
memory: "256Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
max:
cpu: "4"
memory: "8Gi"
min:
cpu: "50m"
memory: "64Mi"
Τα LimitRanges είναι ιδιαίτερα χρήσιμα γιατί ορίζουν default values — οπότε ακόμα κι αν ένας developer ξεχάσει να βάλει resources στο manifest του, το Kubernetes θα εφαρμόσει κάτι λογικό αντί να αφήσει το pod χωρίς κανέναν περιορισμό.
Βήμα 6: Παρακολούθηση Αποτελεσμάτων και Συνεχής Βελτιστοποίηση
Η εφαρμογή VPA + Karpenter δεν είναι κάτι που ρυθμίζεις μια φορά και ξεχνάς. Τα workloads αλλάζουν, νέα deployments προστίθενται, κι οι τιμές cloud αλλάζουν. Χρειάζεται συνεχής παρακολούθηση.
# Prometheus metrics για Karpenter consolidation
karpenter_nodeclaims_disrupted_total{reason="consolidation"}
karpenter_consolidation_actions_performed_total
# Utilization ανά node
sum(kube_pod_container_resource_requests{resource="cpu"}) by (node)
/
kube_node_status_allocatable{resource="cpu"}
Δημιουργήστε ένα εβδομαδιαίο review process — δεν χρειάζεται να είναι κάτι φαντεζί, αρκούν 15 λεπτά:
- Ελέγξτε τις VPA συστάσεις — υπάρχουν νέες ευκαιρίες right-sizing;
- Κοιτάξτε τα Karpenter consolidation events — λειτουργεί όπως πρέπει;
- Συγκρίνετε τα μηνιαία κόστη — βλέπετε πραγματική μείωση στο cloud bill;
- Ψάξτε για orphaned PersistentVolumeClaims και idle LoadBalancers (αυτά τα ξεχασμένα resources είναι σιωπηλοί δολοφόνοι του budget)
Πίνακας Αναμενόμενης Εξοικονόμησης
| Τεχνική | Αναμενόμενη Εξοικονόμηση | Πολυπλοκότητα |
|---|---|---|
| Right-sizing pod requests/limits | 30-60% | Μεσαία |
| VPA (Vertical Pod Autoscaler) | 20-40% | Χαμηλή |
| Karpenter consolidation | 15-30% | Μεσαία |
| Spot instances | 60-90% | Μεσαία-Υψηλή |
| ResourceQuotas/LimitRanges | 10-20% | Χαμηλή |
| Συνδυασμός όλων | 40-70% | - |
Αυτά τα νούμερα δεν είναι θεωρητικά — αντικατοπτρίζουν πραγματικές εξοικονομήσεις που βλέπουν οργανισμοί σε production clusters. Φυσικά, τα αποτελέσματα εξαρτώνται από το πόσο "χαλαρά" ήταν τα requests σας αρχικά.
Συχνές Ερωτήσεις (FAQ)
Τι είναι το right-sizing στο Kubernetes και γιατί είναι σημαντικό;
Το right-sizing σημαίνει να προσαρμόζετε τα resource requests και limits κάθε pod ώστε να ταιριάζουν με την πραγματική χρήση. Είναι η πιο αποτελεσματική τεχνική μείωσης κόστους γιατί ο scheduler βασίζεται στα requests για την τοποθέτηση pods σε nodes. Υπερβολικά υψηλά requests = πληρώνετε για πόρους που κάθονται idle.
Μπορώ να χρησιμοποιήσω VPA και HPA ταυτόχρονα;
Ναι, αλλά με προσοχή. Ο βασικός κανόνας: δεν πρέπει να χρησιμοποιούν το ίδιο metric. Χρησιμοποιήστε τον VPA για memory right-sizing και τον HPA με custom metrics (π.χ. requests/second, queue depth) αντί για CPU utilization. Αν αμφιβάλλετε, κρατήστε τον VPA σε recommendation mode.
Σε τι διαφέρει ο Karpenter από τον Cluster Autoscaler;
Η μεγαλύτερη διαφορά: ο Karpenter δεν χρησιμοποιεί Node Groups. Κοιτάζει τα pending pods, επικοινωνεί απευθείας με το cloud provider API και βρίσκει το βέλτιστο instance type. Κάνει provisioning σε 30-60 δευτερόλεπτα αντί για αρκετά λεπτά, επιλέγει αυτόματα τον πιο οικονομικό τύπο, και έχει ενσωματωμένο consolidation που αφαιρεί underutilized nodes.
Ποια workloads είναι κατάλληλα για Spot instances;
Spot instances ταιριάζουν σε fault-tolerant workloads: batch processing, CI/CD pipelines, background tasks, data processing, stateless microservices. Αποφύγετε τα Spot για βάσεις δεδομένων, stateful εφαρμογές και critical system components (CoreDNS, Prometheus, controllers). Πάντα με PodDisruptionBudgets και πολλαπλούς instance types.
Πόσο χρόνο χρειάζεται για να δω αποτελέσματα μετά την εφαρμογή VPA;
Ο VPA χρειάζεται τουλάχιστον 24-48 ώρες για βασικές συστάσεις. Για πιο αξιόπιστες συστάσεις όμως, αφήστε τον να συλλέξει δεδομένα για 1-2 εβδομάδες ώστε να καλύψει peak hours, off-peak περιόδους και weekends. Τα αποτελέσματα στο cloud bill φαίνονται συνήθως μέσα στον πρώτο μήνα.