Outcome
By the end of this lesson, you will be able to identify three structural reasons K8s defies traditional FinOps tooling, explain ZopNight’s decoupled model, and architect namespace-level scheduling that preserves production.
| Prerequisites | M2.1 (rule library) |
| Time | 9 minutes |
1. Concept
Kubernetes is where most modern workloads run, and it is the worst-served corner of cost tooling.
Three things about how it works cause that. None of them is anybody’s fault, and each one has to be handled rather than worked around.
WHY K8S IS HARD: 1. Cost lives at the wrong level (node bill ≠ workload responsibility) 2. Schedule-stop the cluster loses everything (including prod) 3. Pod-hours ≠ instance-hours (decoupled time accounting)Understand those three and the rest of this module follows. Miss them and every Kubernetes cost decision you make will be slightly wrong.
Reason 1: Cost lives at the wrong level
The cloud bills the cluster’s nodes (EC2 instances backing EKS, VMs backing AKS, GCE VMs backing GKE). But the actual workload, the deployment, the cron job, runs as pods scheduled onto those nodes.
COST BILLING WORKLOAD CONTEXT─────────────────────────────────────────────────────────────────EC2 m5.large × 8 in EKS nodegroup prod-payment-api: 4 pods ml-training-job: 1 pod (resource-hungry) analytics-dashboard: 2 pods sidecar-logger: 7 pods (1 per pod)THE BILL SAYS: "8× m5.large = ~$561/month"
THE WORKLOAD TRUTH: ml-training-job's single pod requests a whole node (2 vCPU) A pod cannot span nodes, so that node is effectively its own The bill doesn't tell you this
WITHOUT POD-LEVEL ATTRIBUTION: You can't tell which workload is driving the cost Showback per team breaks down Cost optimization decisions are blindThis is the fundamental K8s FinOps problem.
Reason 2: Schedule-stop the cluster, lose everything
NAIVE THINKING: "Just stop the cluster off-hours."
REALITY: Stopping the cluster's nodes (scaling its node groups to zero) takes down ALL workloads Not just non-prod Dev workloads stop Production workloads on the same cluster also stop Catastrophic for shared clusters
ZOPNIGHT'S SOLUTION: Schedule the non-prod namespace Not the cluster Cluster keeps running Every workload in that namespace scales to zero Production namespaces unaffectedNamespace-level scheduling preserves the cluster while saving cost on non-prod.
Reason 3: Pod-hours vs instance-hours
A pod’s runtime is independent of the underlying node’s runtime:
SCENARIO: Node m5.large running 10 pods Pod A: scales to zero (pod runtime = 0) Node still running (other 9 pods active) Cluster bill: same (node-hours unchanged)
COST ACCOUNTING question: Did we save anything?
ANSWER depends on accounting model: Naive: no (node still running) Sophisticated: yes (could scale down node if more pods drop)
WITHOUT INSTANCE-LEVEL vs POD-LEVEL ACCOUNTING: FinOps tools either: Show "cluster costs $X" without showing what drives → useless Show pod-level cost via complex allocation → expensive to compute Cannot do both well
ZOPNIGHT'S MODEL: Cluster is the COST entity (bill applies to cluster) Namespace is the SCHEDULING entity (you stop a namespace's workloads) The two are decoupled Workload cost = requested CPU + memory, by namespace and nodeThe decoupled model is the right architecture for K8s cost.
How ZopNight handles these
PROBLEM SOLUTION─────────────────────────────────────────────────────────────────Cost lives at wrong level Requested-cost attribution per workload, broken down by namespace and node
Schedule-stop the cluster Schedule the namespaceloses everything (all its workloads together) Without affecting cluster
Pod-hours vs instance-hours Schedule at the namespace level; the cluster stays the billed entity; requested resources link the twoThe model maps neatly: cluster billed; namespaces scheduled; requested resources glue them together.
What this means in practice
A shared Kubernetes cluster: scheduling the non-prod namespace that holds 12 deployments, so they scale to zero overnight.
EFFECT ON THE CLUSTER: Pods scale to zero Cluster-autoscaler may scale down nodes (if other workloads don't need them) Or: nodes stay running (if other workloads still need capacity)
EFFECT ON COST: The cloud bill falls only if nodes actually scale down Fewer running pods free capacity, but a node that stays up still bills in full
WHERE TO LOOK: Workload cost: requested cost per namespace and workload Cluster cost: node cost, the number the cloud bills ZopNight has no "pod-hours saved" or "node-hours saved" metric; compare the two views to see the outcomeThe two perspectives together give the full picture.
How workload cost is attributed
HOW WORKLOAD COST GETS COMPUTED:
Cluster cost = node cost (what the cloud bills) Workload cost = requested cost: what its pods REQUEST (CPU + memory), priced against the nodes Example: order-api, 4 replicas x (500m CPU, 1 GB) its requested cost is its slice of the node bill Capacity no workload requests stays as unallocated node cost
BREAKDOWN options: By namespace: the workloads in each namespace By node: which nodes carry which workloads Per workload: kind, namespace, replicas, requested cost
NAMESPACES DRIVE THE BREAKDOWN One environment per namespace: environment cost is easy to read Mixed namespaces: you still see workloads, but not clean totalsThis is why keeping each environment in its own namespace matters for K8s cost work: the namespace is both the cost breakdown and the scheduling boundary.
Common K8s cost patterns
PATTERN COST IMPACT ZOPNIGHT ACTION───────────────────────────────────────────────────────────────────Cluster over-provisioned High L1 + L4 (right-size) M3.3.L1
Non-prod workloads 24/7 High L3 (scale to zero) M3.2 patterns
CronJobs scheduled but never used Moderate L4 (suspend cronjobs)
Stateful workloads always-on High L5 (statefulset scheduling)
Cross-cluster duplication High L6 (cross-cluster) M3.4 multi-account
Idle workloads accumulating High M3.3.L3 (7 shapes)The lessons L2-L6 in this module cover each scheduling pattern.
2. Demo
A team’s K8s cost reality:
ESTATE: 1 EKS cluster, ~$8,400/monthWORKLOADS: Production deployments: 3 (always-on, ~60% of node capacity) Production cronjobs: 12 (cron-driven, ~5% of node capacity) Staging deployments: 8 (working hours, ~25% of capacity) Dev deployments: 5 (working hours, ~10% of capacity)
NAIVE APPROACH: "Stop the cluster nights/weekends" Problem: also stops production Cannot do
ZOPNIGHT APPROACH: Schedule at the namespace level (the K8s scheduling unit): Attach the Business Hours schedule (8 AM - 8 PM weekdays) to the staging and dev namespaces (or add both namespaces to one group and attach it there) Action: workloads scale to zero outside hours
Effect: Cluster keeps running Production namespaces are never attached, so they keep running Staging + dev workloads scale to zero off-hours Cluster-autoscaler scales nodes down when pods drop
COST IMPACT: Non-prod share: 35% of $8,400 = $2,940/mo Off 108 of 168 hours a week (64%): saves ~$1,890/mo Before: $8,400/mo (24/7 cluster + workloads) After: ~$6,510/mo (production preserved; non-prod scheduled) Savings: ~$1,890/mo = 22.5% reduction No impact on production Engineers don't notice (their dev environment came back at 8 AM)The math works because namespace-level scheduling is granular enough to leave production alone.
3. Hands-on (5 min)
Assess your K8s estate for scheduling opportunity:
□ STEP 1: Inventory Number of clusters: _____ Number of namespaces: _____ Number of deployments: _____ Number of CronJobs: _____ Number of StatefulSets: _____
□ STEP 2: Namespace hygiene check Is each namespace a single environment (no prod mixed in)? □ Yes □ No Is each namespace owned by one team? □ Yes □ No
□ STEP 3: Identify non-prod Namespaces that are dev/staging/test only: _____ Currently always-on (24/7): _____
□ STEP 4: Estimate savings Non-prod fraction of cluster cost: _____% Expected scheduling savings: ~64% of that = $_____/mo (108 off-hours out of 168 hours a week)
□ STEP 5: Plan rollout First cluster to schedule: __________ Pilot deployments: __________ Target completion: __________A 20-minute assessment reveals the opportunity. The savings are usually significant.
4. Knowledge check
Q1
Why can’t you just stop the cluster off-hours?
A. The cloud provider does not allow a cluster to be stopped at all
B. The control plane keeps billing, so stopping it saves nothing
C. Stopping the cluster’s nodes stops ALL workloads, including prod
D. Restarting the nodes each morning hurts application performance
Show answer
Correct: C. A cluster-level Stop scales the node groups to zero, so every workload on the cluster stops, production included. Namespace-level scheduling (stopping only the non-prod namespaces) preserves production while stopping non-prod. Cluster-level is too coarse for shared clusters.
Q2
The cost model decouples:
A. Cluster cost from pod cost
B. Cluster runtime (the billing entity) from workload runtime (the scheduling entity)
C. Memory from CPU
D. Resource requests from the limits on each and every single individual pod
Show answer
Correct: B. The two are independent: the cluster keeps running while a namespace’s workloads scale to zero. Requested CPU and memory link them for attribution.
Q3
Workload cost attribution rolls up to:
A. Cluster-level cost; the nodes are billed, and each workload’s share is what its pods request (CPU + memory)
B. Per-pod cost only
C. The namespace, which is the real billing entity as far as the cloud provider is actually concerned here today
D. Network cost
Show answer
Correct: A. The cloud bills the cluster’s nodes. Each workload’s share is its requested CPU and memory, and the breakdown runs by namespace and by node.
5. Apply
K8s scheduling is on the Inventory page: filter to EKS, GKE or AKS namespaces and attach a schedule to the namespace. Individual Deployments, StatefulSets and CronJobs are not schedulable on their own; the namespace is the boundary.
For your team: keep each environment in its own namespace first; then namespace scheduling (this module).
Related lessons
- L2: K8s hierarchy (next)
- L3: Deployment to zero
- L4: Suspend cronjobs
- L5: StatefulSets
- L6: Cross-cluster patterns
- T3.M3.3: K8s discipline