The mission: stop paying for idle pods and stop debugging production by kubectl logs-ing one pod at a time. Two builds, one outcome: a cluster that scales itself and explains itself.
[ CLUSTER PARAMS ]
- SCALING
- HPA (traffic) + VPA (right-sizing)
- LOGGING
- Fluentd DaemonSet → Elasticsearch → Kibana
- RESULT
- −20% cloud over-provisioning
- MTTR
- minutes, not hours
#Architecture
┌─────────────── K8S CLUSTER ───────────────┐
traffic ─▶ ingress ─▶ [ pods ×N ] ◀── HPA (CPU/RPS) │
│ │ ▲ │
│ │ └────── VPA (requests) │
│ stdout/stderr │
│ ▼ │
│ [ fluentd DaemonSet ] │
└───────────────┼───────────────────────────┘
▼
[ elasticsearch ] ──▶ [ kibana ]
(retention ILM) (dashboards
+ alerts)
#Step 1 — HPA on a Signal Users Feel
CPU-only autoscaling lies for I/O-bound services. Scale on requests-per-second alongside CPU:
| |
#Step 2 — VPA in Recommendation Mode First
VPA in Auto mode restarts pods to resize them. Run it in Off (recommend-only) for two weeks, then set requests from its data:
| |
#Step 3 — EFK: One Query Instead of Forty Pods
Fluentd runs as a DaemonSet, tails every container, enriches with pod metadata, ships to Elasticsearch:
fluentd:
configMapConfigs:
- fluentd-prometheus-conf
fileConfigs:
01_sources.conf: |
<source>
@type tail
path /var/log/containers/*.log
<parse> @type cri </parse>
</source>
02_enrich.conf: |
<filter kubernetes.**>
@type kubernetes_metadata # namespace, pod, labels
</filter>Retention is an ILM policy — hot 7 days, warm 30, delete at 90. Log storage stopped being a surprise line item.
#Results
| Metric | Before | After |
|---|---|---|
| Cloud over-provisioning | baseline | −20% |
| MTTR (prod incidents) | hours | minutes |
| Log search | per-pod ssh/kubectl | one Kibana query |
| Scale events | manual, reactive | automatic, boring |