Skip to content

Latest commit

 

History

History
667 lines (613 loc) · 21.9 KB

File metadata and controls

667 lines (613 loc) · 21.9 KB

Savings Calculator

Note: This page is an interactive calculator that requires JavaScript. If you are viewing this on GitHub, visit the live version instead.

Estimate how much you could save by right-sizing your Kubernetes workloads with attune. Enter your current resource allocation and actual usage below, and see the projected monthly and annual savings instantly.

The calculator uses the same pricing model as the operator's built-in EstimatedMonthlySavings computation (configurable via AttuneDefaults).


<style> .calc-container { max-width: 900px; margin: 0 auto; font-family: inherit; } .calc-section { background: var(--md-code-bg-color, #f5f5f5); border-radius: 8px; padding: 24px; margin-bottom: 24px; } .calc-section h3 { margin-top: 0; margin-bottom: 16px; border-bottom: 2px solid var(--md-primary-fg-color, #009688); padding-bottom: 8px; } .calc-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; } @media (max-width: 600px) { .calc-grid { grid-template-columns: 1fr; } } .calc-field { display: flex; flex-direction: column; } .calc-field label { font-size: 0.85em; font-weight: 600; margin-bottom: 4px; color: var(--md-default-fg-color--light, #666); } .calc-field input, .calc-field select { padding: 10px 12px; border: 1px solid var(--md-default-fg-color--lightest, #ccc); border-radius: 6px; font-size: 1em; background: var(--md-default-bg-color, #fff); color: var(--md-default-fg-color, #333); } .calc-field input:focus, .calc-field select:focus { outline: none; border-color: var(--md-primary-fg-color, #009688); box-shadow: 0 0 0 2px rgba(0, 150, 136, 0.2); } .calc-field .help-text { font-size: 0.75em; color: var(--md-default-fg-color--lighter, #999); margin-top: 2px; } .calc-results { background: var(--md-primary-fg-color, #009688); color: #fff; border-radius: 8px; padding: 24px; margin-top: 24px; } .calc-results h3 { margin-top: 0; color: #fff; border-bottom: 2px solid rgba(255,255,255,0.3); padding-bottom: 8px; } .results-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; margin-bottom: 16px; } @media (max-width: 600px) { .results-grid { grid-template-columns: 1fr; } } .result-card { background: rgba(255,255,255,0.15); border-radius: 6px; padding: 16px; text-align: center; } .result-card .result-value { font-size: 2em; font-weight: 700; margin: 4px 0; } .result-card .result-label { font-size: 0.85em; opacity: 0.9; } .breakdown-table { width: 100%; border-collapse: collapse; margin-top: 16px; } .breakdown-table th, .breakdown-table td { padding: 8px 12px; text-align: left; border-bottom: 1px solid rgba(255,255,255,0.2); color: #fff; } .breakdown-table th { font-size: 0.85em; text-transform: uppercase; opacity: 0.8; } .savings-bar-container { margin-top: 16px; } .savings-bar-label { display: flex; justify-content: space-between; font-size: 0.85em; margin-bottom: 4px; } .savings-bar { height: 24px; background: rgba(255,255,255,0.2); border-radius: 12px; overflow: hidden; position: relative; } .savings-bar-fill { height: 100%; border-radius: 12px; transition: width 0.4s ease; } .savings-bar-fill.cpu { background: #4dd0e1; } .savings-bar-fill.mem { background: #81c784; } .calc-presets { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; } .preset-btn { padding: 6px 14px; border: 1px solid var(--md-primary-fg-color, #009688); border-radius: 20px; background: transparent; color: var(--md-primary-fg-color, #009688); cursor: pointer; font-size: 0.85em; transition: all 0.2s; } .preset-btn:hover { background: var(--md-primary-fg-color, #009688); color: #fff; } .calc-add-row { display: inline-flex; align-items: center; gap: 4px; padding: 6px 14px; border: 1px dashed var(--md-primary-fg-color, #009688); border-radius: 6px; background: transparent; color: var(--md-primary-fg-color, #009688); cursor: pointer; font-size: 0.85em; margin-top: 8px; } .calc-add-row:hover { background: rgba(0, 150, 136, 0.1); } .workload-row { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr 1fr 1fr auto; gap: 8px; align-items: end; margin-bottom: 8px; } @media (max-width: 768px) { .workload-row { grid-template-columns: 1fr 1fr; } } .workload-row input { padding: 8px 10px; border: 1px solid var(--md-default-fg-color--lightest, #ccc); border-radius: 6px; font-size: 0.9em; background: var(--md-default-bg-color, #fff); color: var(--md-default-fg-color, #333); width: 100%; box-sizing: border-box; } .workload-row input:focus { outline: none; border-color: var(--md-primary-fg-color, #009688); } .workload-header { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr 1fr 1fr auto; gap: 8px; margin-bottom: 4px; font-size: 0.75em; font-weight: 600; color: var(--md-default-fg-color--light, #666); text-transform: uppercase; } @media (max-width: 768px) { .workload-header { display: none; } } .mobile-label { display: none; font-size: 0.7em; font-weight: 600; color: var(--md-default-fg-color--light, #666); text-transform: uppercase; margin-bottom: 2px; } @media (max-width: 768px) { .mobile-label { display: block; } } .remove-btn { background: none; border: none; color: #e53935; cursor: pointer; font-size: 1.2em; padding: 4px 8px; border-radius: 4px; } .remove-btn:hover { background: rgba(229, 57, 53, 0.1); } .note-box { background: rgba(255,255,255,0.1); border-radius: 6px; padding: 12px 16px; margin-top: 16px; font-size: 0.85em; line-height: 1.5; } .under-provisioned td { color: #ffcc80 !important; } .under-provisioned td:first-child::before { content: "\26A0\FE0F "; } .under-prov-note { background: rgba(255, 152, 0, 0.2); border-left: 3px solid #ffcc80; border-radius: 0 6px 6px 0; padding: 10px 14px; margin-top: 12px; font-size: 0.85em; line-height: 1.5; display: none; } </style>

Quick Presets

Start with a typical scenario, then customize the numbers to match your environment.

Small team (5 services) Mid-size (20 services) Large platform (100 services) Start from scratch

Cloud Pricing

CPU price ($/vCPU-hour) AWS on-demand ~$0.031, GCP ~$0.034, Azure ~$0.036
Memory price ($/GiB-hour) AWS ~$0.004, GCP ~$0.005, Azure ~$0.004
CPU overhead 10% headroom 20% headroom — recommended 30% headroom 50% headroom (bursty workloads)
Memory overhead 10% headroom 20% headroom 30% headroom — recommended 50% headroom

Your Workloads

Enter each service's current resource requests, actual P95 usage, and replica count.

Service name CPU req (m) CPU P95 (m) Mem req (Mi) Mem P95 (Mi) Replicas
+ Add workload

Projected Savings with Attune

Monthly savings
$0
Annual savings
$0
Resource reduction
0%
CPU utilization: 0% requested → 0% after right-sizing
Memory utilization: 0% requested → 0% after right-sizing
Service CPU: current → right-sized Memory: current → right-sized Monthly savings
Under-provisioned workloads detected. Rows marked with a warning have P95 usage that exceeds the current resource request (after applying the overhead). Right-sizing these workloads would increase their requests, preventing throttling and OOMKills. This improves reliability rather than reducing cost.
How this is calculated: For each workload, the right-sized value is P95_usage x (1 + overhead/100). Monthly cost uses (cores x CPU_price + GiB x mem_price) x 730 hours. Savings are the difference between current and right-sized costs across all replicas. Actual savings may be higher due to improved bin-packing enabling node consolidation.
<script> let workloadId = 0; function addWorkloadRow(name, cpuReq, cpuP95, memReq, memP95, replicas) { const container = document.getElementById('workloadRows'); const id = workloadId++; const div = document.createElement('div'); div.className = 'workload-row'; div.id = 'row-' + id; div.innerHTML = `
Service name
CPU req (m)
CPU P95 (m)
Mem req (Mi)
Mem P95 (Mi)
Replicas
× `; container.appendChild(div); calculate(); } function removeRow(id) { const row = document.getElementById('row-' + id); if (row) row.remove(); calculate(); } function getWorkloads() { const rows = document.querySelectorAll('.workload-row'); const workloads = []; rows.forEach(row => { const inputs = row.querySelectorAll('input'); workloads.push({ name: inputs[0].value || 'unnamed', cpuReq: parseFloat(inputs[1].value) || 0, cpuP95: parseFloat(inputs[2].value) || 0, memReq: parseFloat(inputs[3].value) || 0, memP95: parseFloat(inputs[4].value) || 0, replicas: parseInt(inputs[5].value) || 1 }); }); return workloads; } function calculate() { const cpuPrice = parseFloat(document.getElementById('cpuPrice').value) || 0; const memPrice = parseFloat(document.getElementById('memPrice').value) || 0; const cpuMargin = 1 + parseFloat(document.getElementById('overheadCpu').value) / 100; const memMargin = 1 + parseFloat(document.getElementById('overheadMem').value) / 100; const hoursPerMonth = 730; const workloads = getWorkloads(); let totalMonthlySavings = 0; let totalCpuReq = 0, totalCpuP95 = 0, totalMemReq = 0, totalMemP95 = 0; let totalCurrentCost = 0, totalNewCost = 0; const breakdown = []; workloads.forEach(w => { const cpuTuned = Math.max(w.cpuP95 * cpuMargin, w.cpuP95); const memTuned = Math.max(w.memP95 * memMargin, w.memP95); const cpuCurrentCost = (w.cpuReq / 1000) * cpuPrice * hoursPerMonth * w.replicas; const memCurrentCost = (w.memReq / 1024) * memPrice * hoursPerMonth * w.replicas; const cpuNewCost = (Math.min(cpuTuned, w.cpuReq) / 1000) * cpuPrice * hoursPerMonth * w.replicas; const memNewCost = (Math.min(memTuned, w.memReq) / 1024) * memPrice * hoursPerMonth * w.replicas; const monthlySaved = (cpuCurrentCost + memCurrentCost) - (cpuNewCost + memNewCost); totalMonthlySavings += monthlySaved; totalCurrentCost += cpuCurrentCost + memCurrentCost; totalNewCost += cpuNewCost + memNewCost; totalCpuReq += w.cpuReq * w.replicas; totalCpuP95 += w.cpuP95 * w.replicas; totalMemReq += w.memReq * w.replicas; totalMemP95 += w.memP95 * w.replicas; const cpuUnderProv = cpuTuned > w.cpuReq && w.cpuReq > 0; const memUnderProv = memTuned > w.memReq && w.memReq > 0; breakdown.push({ name: w.name, cpuReq: w.cpuReq, cpuNew: Math.round(cpuUnderProv ? cpuTuned : Math.min(cpuTuned, w.cpuReq)), memReq: w.memReq, memNew: Math.round(memUnderProv ? memTuned : Math.min(memTuned, w.memReq)), saved: monthlySaved, underProv: cpuUnderProv || memUnderProv }); }); document.getElementById('monthlySavings').textContent = '$' + Math.round(totalMonthlySavings).toLocaleString(); document.getElementById('annualSavings').textContent = '$' + Math.round(totalMonthlySavings * 12).toLocaleString(); const reduction = totalCurrentCost > 0 ? Math.round(((totalCurrentCost - totalNewCost) / totalCurrentCost) * 100) : 0; document.getElementById('overallReduction').textContent = reduction + '%'; const cpuUtilBefore = totalCpuReq > 0 ? Math.round((totalCpuP95 / totalCpuReq) * 100) : 0; const cpuTunedTotal = totalCpuP95 * cpuMargin; const cpuUtilAfter = cpuTunedTotal > 0 ? Math.min(100, Math.round((totalCpuP95 / Math.min(cpuTunedTotal, totalCpuReq)) * 100)) : 0; document.getElementById('cpuUtilBefore').textContent = cpuUtilBefore + '%'; document.getElementById('cpuUtilAfter').textContent = cpuUtilAfter + '%'; document.getElementById('cpuBar').style.width = cpuUtilAfter + '%'; const memUtilBefore = totalMemReq > 0 ? Math.round((totalMemP95 / totalMemReq) * 100) : 0; const memTunedTotal = totalMemP95 * memMargin; const memUtilAfter = memTunedTotal > 0 ? Math.min(100, Math.round((totalMemP95 / Math.min(memTunedTotal, totalMemReq)) * 100)) : 0; document.getElementById('memUtilBefore').textContent = memUtilBefore + '%'; document.getElementById('memUtilAfter').textContent = memUtilAfter + '%'; document.getElementById('memBar').style.width = memUtilAfter + '%'; const tbody = document.getElementById('breakdownBody'); tbody.innerHTML = ''; let hasUnderProv = false; breakdown.forEach(b => { const tr = document.createElement('tr'); if (b.underProv) { tr.className = 'under-provisioned'; hasUnderProv = true; } const cpuArrow = b.cpuNew > b.cpuReq ? '\u2191' : '\u2192'; const memArrow = b.memNew > b.memReq ? '\u2191' : '\u2192'; const savingsText = b.underProv && b.saved === 0 ? 'needs more resources' : '$' + Math.round(b.saved).toLocaleString() + '/mo'; tr.innerHTML = ` ${b.name} ${b.cpuReq}m ${cpuArrow} ${b.cpuNew}m ${b.memReq}Mi ${memArrow} ${b.memNew}Mi ${savingsText} `; tbody.appendChild(tr); }); if (breakdown.length > 0) { const totalRow = document.createElement('tr'); totalRow.innerHTML = ` Total $${Math.round(totalMonthlySavings).toLocaleString()}/mo `; tbody.appendChild(totalRow); } document.getElementById('underProvNote').style.display = hasUnderProv ? 'block' : 'none'; } function applyPreset(type) { document.getElementById('workloadRows').innerHTML = ''; workloadId = 0; if (type === 'small') { addWorkloadRow('api-gateway', 1000, 200, 1024, 300, 3); addWorkloadRow('user-service', 500, 80, 512, 150, 2); addWorkloadRow('order-service', 500, 120, 512, 200, 2); addWorkloadRow('notification-svc', 250, 30, 256, 80, 2); addWorkloadRow('worker', 1000, 150, 2048, 400, 2); } else if (type === 'medium') { addWorkloadRow('api-gateway', 2000, 400, 2048, 600, 5); addWorkloadRow('user-service', 1000, 150, 1024, 300, 3); addWorkloadRow('order-service', 1000, 200, 1024, 350, 4); addWorkloadRow('payment-service', 500, 100, 512, 200, 3); addWorkloadRow('inventory-svc', 500, 80, 512, 180, 3); addWorkloadRow('notification-svc', 500, 50, 512, 100, 2); addWorkloadRow('search-service', 2000, 600, 4096, 1200, 3); addWorkloadRow('recommendation', 1000, 200, 2048, 500, 2); addWorkloadRow('auth-service', 500, 60, 256, 80, 3); addWorkloadRow('email-worker', 500, 40, 512, 100, 2); addWorkloadRow('report-generator', 2000, 300, 4096, 800, 2); addWorkloadRow('cache-warmer', 500, 50, 1024, 200, 2); addWorkloadRow('data-pipeline', 1000, 400, 2048, 600, 3); addWorkloadRow('metrics-agg', 500, 100, 1024, 300, 2); addWorkloadRow('frontend-ssr', 500, 80, 512, 150, 4); addWorkloadRow('cdn-origin', 500, 60, 256, 100, 2); addWorkloadRow('webhook-handler', 250, 30, 256, 60, 2); addWorkloadRow('scheduler', 500, 50, 512, 120, 1); addWorkloadRow('migration-runner', 1000, 50, 1024, 200, 1); addWorkloadRow('healthcheck-svc', 250, 20, 128, 40, 2); } else if (type === 'large') { addWorkloadRow('api-gateway', 4000, 800, 4096, 1200, 10); addWorkloadRow('user-service', 2000, 300, 2048, 600, 8); addWorkloadRow('order-service', 2000, 400, 2048, 700, 8); addWorkloadRow('payment-service', 1000, 200, 1024, 400, 5); addWorkloadRow('search-service', 4000, 1200, 8192, 2400, 6); addWorkloadRow('recommendation', 2000, 500, 4096, 1000, 4); addWorkloadRow('ml-inference', 4000, 800, 8192, 2000, 4); addWorkloadRow('data-pipeline', 2000, 600, 4096, 1200, 6); addWorkloadRow('70+ other services (avg)', 500, 100, 512, 150, 210); } else { addWorkloadRow('', 500, 100, 512, 150, 3); } calculate(); } // Initialize with small preset document.addEventListener('DOMContentLoaded', function() { applyPreset('small'); }); // Also run immediately in case DOMContentLoaded already fired if (document.readyState !== 'loading') { applyPreset('small'); } </script>

Understanding the Numbers

How right-sized values are calculated

For each workload, the right-sized resource request is:

right_sized = P95_usage x (1 + overhead/100)

This matches the operator's default algorithm: take the 95th percentile of observed CPU usage (or 99th for memory), multiply by the overhead, and clamp to the configured bounds.

Why actual savings may be higher

This calculator computes direct resource savings, the difference between what you're requesting now and what you'd request after right-sizing. But the real impact goes further:

  • Node consolidation: Lower pod requests mean better bin-packing. Your cluster autoscaler (or Karpenter) can fit more pods per node and scale down unused nodes. This often adds 10-30% on top of the per-pod savings.

  • Reduced spot/reserved waste: Right-sized workloads let you buy smaller reserved instances or committed use discounts, matching actual need instead of peak overestimate.

  • Operational time saved: No more quarterly resource review meetings. No more "why is the cluster full when utilization is 8%" investigations.

Common findings

Cluster size Typical monthly waste Typical reduction
5-10 services $200-1,000 40-70%
20-50 services $2,000-10,000 50-75%
100+ services $15,000-100,000+ 40-65%

These ranges are based on industry benchmarks from CAST AI, Datadog, and ScaleOps reports.


Ready to capture these savings?