Prometheus Sizing Report
Generate hardware sizing recommendations from Prometheus-collected test data on
Kubernetes/OpenShift clusters.
When to use
- User has Prometheus/Thanos test data and wants hardware sizing recommendations
- User has stats files (from analyze-prometheus.py or similar) and wants a sizing report
- User asks for capacity planning based on observed resource usage
- User wants to convert load test results into hardware requirements
Workflow
Follow these steps in order. Each step builds on the previous one. Do not skip the
interview or test methodology steps — without understanding the test structure, the
data cannot be interpreted correctly.
Step 1: Interview — Understand the Environment
Before reading any data, gather this information from the user. If any of these are
unknown, help the user find them from the test artifacts (kubeconfig, cluster nodes,
installed operators).
Cluster topology:
- Single Node (SNO) or Multi-Node (MNO)?
- If MNO: how many control plane nodes? How many workers? Are control planes schedulable?
- Hardware per node: CPU count (logical vs physical cores), RAM, disk types
Software stack:
- Kubernetes/OpenShift version
- Key operators or platform components installed (the user defines these — do not assume
a fixed set of components like ACM or GitOps)
- Any external services that should be excluded from sizing (e.g., external object storage)
Workload under test:
- What workload was applied? (managed clusters, application deployments, policy enforcement, etc.)
- What is the scale? (number of managed clusters, pods, namespaces, etc.)
Step 2: Interview — Understand the Test Methodology
This is critical. Different test structures require different analysis approaches.
Ask the user to describe:
Test phases:
- How many phases does the test have?
- What does each phase represent? (idle baseline, ramp-up, active workload, steady state, cooldown)
- Duration of each phase
- Was the workload applied all at once or in incremental batches?
If batched/stepped:
- How many batches?
- What was added in each batch? (e.g., 8 clusters per batch)
- Was there a measurement window between batches?
- Was the workload active (churn/activity) or static during measurement?
Phase mapping to data:
- Which directories or time ranges correspond to which phases?
- Are there separate analysis runs per phase, or one continuous run?
Example test structures the skill should handle:
- Simple two-phase: deploy everything, measure active + steady state
- Multi-phase stepped: idle baseline, incremental batches with measurement windows, steady state
- Single continuous: one long run with no distinct phases
- Before/after: baseline measurement, then change applied, then re-measure
Step 3: Collect Data
The skill supports two data input methods:
Method A: Pre-collected stats files (preferred)
Stats files from analyze-prometheus.py (or compatible tools) contain pre-computed
statistics. Read references/stats-format.md for the exact file format.
Resolving unit uncertainty — consult analyze-prometheus.py directly:
If any metric's unit is unclear, read the script at
acm-deploy-load/analyze-prometheus.py (in the project root). It is the
authoritative source for both the Prometheus query and the unit conversion applied.
Two things to look up for any stats file:
The y_unit string passed to query_thanos() for that metric (e.g.,
"DISK_USAGE", "MEMORY", "CORES"). Search for the output filename
(e.g., "pvc-usage", "db-size") to find the call.
The conversion block in query_thanos() that handles that y_unit value.
Each branch shows the exact divisor:
MEMORY → bytes / (1024^3) → GiB (binary)
DISK_USAGE → bytes / (1000^3) → GB (decimal) — applies to disk-util,
etcd DB size, AND PVC usage
NET → bytes / (1024^2) → MiB/s
DISK_TPUT_MB → bytes / (1000^2) → MB/s
Do not rely on the stats filename or intuition alone to infer units. Always verify
against the script when in doubt.
Directory structure per analysis run:
{analysis-run}/
├── analysis # Metadata: cluster version, time range, duration
├── node/stats/ # Node-level CPU, memory, disk, network
├── etcd/stats/ # etcd DB size, latencies, leader elections
├── cluster/stats/ # Cluster-wide aggregates
├── resource/stats/ # API object counts, PVC usage
├── {component}/stats/ # Per-component CPU, memory, network
└── {component}/csv/ # Raw time-series (1-minute resolution)
Key files to read for sizing (node-level):
node/stats/cpu-node.stats — CPU usage in cores (use P95 for sizing)
node/stats/mem-node.stats — Memory usage in GiB (use Max for sizing)
node/stats/disk-iops-*-node.stats, disk-tput-*-node.stats — Disk I/O (IOPS and throughput in MB/s)
node/stats/disk-util-*-node.stats — Disk partition used space in GB (bytes/1000^3); NOT a percentage
node/stats/net-rcv-node.stats, net-xmt-node.stats — Network in MiB/s
etcd/stats/db-size.stats — etcd database size in GB (decimal, bytes/1000^3)
Key files for component breakdown:
{component}/stats/cpu-{component}.stats — Component CPU in cores
{component}/stats/mem-{component}.stats — Component memory in GiB
Warning: A component directory often contains multiple memory stats files —
sub-groupings (e.g., mem-acm-obs-rcv-total.stats) and per-pod files
(e.g., mem-acm-obs-pods.stats). Always use the file whose name matches
mem-{component}.stats exactly for the component's total memory footprint.
Sub-component files will undercount and must not be used to characterize or
compare the component as a whole (e.g., as a fraction of a parent aggregate).
Key files for resource counts:
resource/stats/*.stats — Object counts (pods, configmaps, secrets, etc.)
resource/stats/pvc-usage.stats — PVC consumption in GB (decimal, bytes/1000^3)
Reading stats files:
The stats format is pandas describe() output with added percentiles. Each column
is a separate series (node name, pod name, or aggregate label). Key rows:
count — number of data points (1-minute samples)
mean, std — average and standard deviation
min, 25%, 50%, 75%, 95%, 99%, max — distribution
Units (already converted in stats files):
- CPU: cores
- Memory: GiB (bytes / 1024^3)
- Network: MiB/s (bytes / 1024^2)
- Disk usage: GB (bytes / 1000^3)
- Disk throughput: MB/s (bytes / 1000^2)
- IOPS: operations/second
- etcd DB size: GB (bytes / 1000^3)
- PVC usage: GB (bytes / 1000^3)
For multi-node clusters, stats files will have one column per node. Sum or take the
per-node value depending on the metric (CPU and memory are per-node; use each node's
value independently for per-node sizing).
Method B: Direct Prometheus queries
If the user has a live Prometheus/Thanos endpoint, query it directly. Read
references/prometheus-queries.md for a catalog of useful queries.
Prometheus queries return raw values in base units (bytes, seconds). Convert:
- CPU: cores (from irate of cpu_seconds_total)
- Memory: bytes → GiB (divide by 1024^3)
- Network: bytes/s → MiB/s (divide by 1024^2)
- Disk: bytes → GB (divide by 1000^3)
Step 4: Analyze and Size
Sizing metrics
- CPU sizing metric: P95 (95th percentile) — represents sustained load excluding
brief spikes. P95 is preferred over max because momentary spikes (GC, cert rotation)
should not drive hardware selection.
- Memory sizing metric: Max — memory is the binding constraint in most deployments.
Unlike CPU, memory pressure causes OOM kills, so size for the worst case observed.
Per-phase analysis
For each test phase, extract:
- Node-level CPU P95 and Memory Max
- Per-component CPU P95 and Memory Max (for component breakdown)
- Disk usage max (root, etcd, container storage partitions)
- Network throughput P95
- etcd DB size (P95 or Max)
- Resource counts (pods, namespaces, configmaps)
Growth rate analysis (for stepped/batched tests)
If the test has incremental batches, compute per-unit growth rates:
- CPU cores per added unit (cluster, namespace, pod batch, etc.)
- Memory GiB per added unit
- etcd GiB per added unit
Use these to identify which components scale most aggressively.
Hardware tier calculation
For each deployment scale and phase, compute hardware requirements at multiple
utilization targets:
| Target |
Use Case |
Formula |
| 60% |
Conservative — maximum headroom |
observed / 0.60 |
| 75% |
Balanced — recommended default |
observed / 0.75 |
| 90% |
Cost-optimized — minimal headroom |
observed / 0.90 |
Round up to the nearest standard hardware tier:
- CPU tiers (vCPUs): 8, 12, 16, 24, 32, 48, 64, 96, 128
- Memory tiers (GiB): 32, 64, 96, 128, 192, 256, 384, 512, 768, 1024
For multi-node clusters, compute per-node requirements. Note whether workload is
evenly distributed or if control plane nodes have different resource profiles than
workers.
Operational limits to flag
Check and report on these limits when applicable:
- etcd DB size: Default quota is 8 GiB binary = 8.59 GB decimal. Stats files
are in decimal GB (bytes/1000^3), so compare observed values against 8.59 GB,
not 8.00. Flag if observed or projected size approaches this limit.
- Max pods: Default is 250 per node (110 on some configurations). Report
non-terminated pod count as a percentage of the limit. Use
cluster/stats/nonterm-pods-cluster.stats for this — NOT
resource/stats/pods.stats, which includes terminated and completed pods
(e.g., finished Jobs) and will overcount against the limit.
- Memory pressure: Flag if observed memory exceeds 80% of installed RAM.
Step 5: Generate Report
Ask the user which output format they prefer:
- PDF (via reportlab) — best for sharing with field consultants
- Markdown — best for embedding in wikis, docs, or repos
- Plain text — best for quick consumption in terminal
Report structure
Regardless of format, include these sections:
- Test Environment — cluster topology, hardware, software versions, workload description
- Test Methodology — phases, durations, what each phase represents
- Resource Consumption — per-phase tables for CPU, memory, disk, network
- For stepped tests: show per-batch progression
- For multi-scenario tests: cross-scenario comparison
- Component Breakdown — which components consume what share of resources
- Identify the top consumers and fastest-growing components
- Disk and Storage — filesystem usage, PVC consumption, etcd DB size
- Network — throughput at P95 and peak, NIC recommendation
- Hardware Sizing Recommendations — utilization target tables, consolidated tiers
- Key Findings — bullet-point summary of the most important takeaways
PDF generation
When generating PDF output, use reportlab with:
- BaseDocTemplate with TableOfContents
- Letter page size, 0.6-inch margins
- Styled tables with header row, alternating row colors
- Highlight rows for peak/worst-case values
Read the references/pdf-patterns.md file for reportlab code patterns.
Markdown generation
Use standard GitHub-flavored Markdown with tables. Include a table of contents
using heading links.
Plain text generation
Use fixed-width formatted tables. Keep line width under 120 characters.
Important considerations
Never assume components. The user defines what software stack they're running.
Don't hardcode ACM, MCE, GitOps, or any specific operator set. Ask what components
are installed and which namespace groupings matter.
Always understand the test methodology first. Data without context is meaningless.
A CPU spike during provisioning means something very different from a CPU spike during
steady state.
Memory is usually the binding constraint for Kubernetes control planes. CPU
drops significantly at steady state; memory does not. Flag this pattern when observed.
Separate peak from steady state. Hardware recommendations should cover the
worst case (peak during active workload), but also call out the steady-state
baseline since many deployments spend most of their time in steady state.
Be explicit about units. CPU in cores, memory in GiB (binary), disk in GB
(decimal), network in MiB/s. State units in every table header.
For multi-node clusters, report per-node. Sizing recommendations are per-machine.
Show how total cluster resources map to per-node requirements.
Flag non-obvious risks. Max-pods limits, etcd quota, memory-to-CPU imbalance,
disk I/O bottlenecks — call these out even if the user didn't ask.
Report observations, not theories. Describe what the data shows; do not speculate
about why. If two tests differ, state the difference and note that the cause is unknown
unless the test design explicitly isolates that variable. Avoid causal language
("X drives Y", "because of Z") unless it is directly supported by the test data.
Recommendations for further investigation are appropriate; assertions about root cause
are not.
Source: redhat-performance/acm-deploy-load — distributed by TomeVault.
1---2name: redhat-performance-acm-deploy-load-prometheus-sizing-report3description: Prometheus Sizing Report4---56# Prometheus Sizing Report78Generate hardware sizing recommendations from Prometheus-collected test data on9Kubernetes/OpenShift clusters.1011## When to use1213- User has Prometheus/Thanos test data and wants hardware sizing recommendations14- User has stats files (from analyze-prometheus.py or similar) and wants a sizing report15- User asks for capacity planning based on observed resource usage16- User wants to convert load test results into hardware requirements1718## Workflow1920Follow these steps in order. Each step builds on the previous one. Do not skip the21interview or test methodology steps — without understanding the test structure, the22data cannot be interpreted correctly.2324### Step 1: Interview — Understand the Environment2526Before reading any data, gather this information from the user. If any of these are27unknown, help the user find them from the test artifacts (kubeconfig, cluster nodes,28installed operators).2930**Cluster topology:**31- Single Node (SNO) or Multi-Node (MNO)?32- If MNO: how many control plane nodes? How many workers? Are control planes schedulable?33- Hardware per node: CPU count (logical vs physical cores), RAM, disk types3435**Software stack:**36- Kubernetes/OpenShift version37- Key operators or platform components installed (the user defines these — do not assume38 a fixed set of components like ACM or GitOps)39- Any external services that should be excluded from sizing (e.g., external object storage)4041**Workload under test:**42- What workload was applied? (managed clusters, application deployments, policy enforcement, etc.)43- What is the scale? (number of managed clusters, pods, namespaces, etc.)4445### Step 2: Interview — Understand the Test Methodology4647This is critical. Different test structures require different analysis approaches.48Ask the user to describe:4950**Test phases:**51- How many phases does the test have?52- What does each phase represent? (idle baseline, ramp-up, active workload, steady state, cooldown)53- Duration of each phase54- Was the workload applied all at once or in incremental batches?5556**If batched/stepped:**57- How many batches?58- What was added in each batch? (e.g., 8 clusters per batch)59- Was there a measurement window between batches?60- Was the workload active (churn/activity) or static during measurement?6162**Phase mapping to data:**63- Which directories or time ranges correspond to which phases?64- Are there separate analysis runs per phase, or one continuous run?6566**Example test structures the skill should handle:**67- Simple two-phase: deploy everything, measure active + steady state68- Multi-phase stepped: idle baseline, incremental batches with measurement windows, steady state69- Single continuous: one long run with no distinct phases70- Before/after: baseline measurement, then change applied, then re-measure7172### Step 3: Collect Data7374The skill supports two data input methods:7576#### Method A: Pre-collected stats files (preferred)7778Stats files from analyze-prometheus.py (or compatible tools) contain pre-computed79statistics. Read `references/stats-format.md` for the exact file format.8081**Resolving unit uncertainty — consult analyze-prometheus.py directly:**82If any metric's unit is unclear, read the script at83`acm-deploy-load/analyze-prometheus.py` (in the project root). It is the84authoritative source for both the Prometheus query and the unit conversion applied.85Two things to look up for any stats file:86871. **The `y_unit` string** passed to `query_thanos()` for that metric (e.g.,88 `"DISK_USAGE"`, `"MEMORY"`, `"CORES"`). Search for the output filename89 (e.g., `"pvc-usage"`, `"db-size"`) to find the call.90912. **The conversion block** in `query_thanos()` that handles that `y_unit` value.92 Each branch shows the exact divisor:93 - `MEMORY` → `bytes / (1024^3)` → GiB (binary)94 - `DISK_USAGE` → `bytes / (1000^3)` → GB (decimal) — applies to disk-util,95 etcd DB size, AND PVC usage96 - `NET` → `bytes / (1024^2)` → MiB/s97 - `DISK_TPUT_MB` → `bytes / (1000^2)` → MB/s9899Do not rely on the stats filename or intuition alone to infer units. Always verify100against the script when in doubt.101102**Directory structure per analysis run:**103```104{analysis-run}/105├── analysis # Metadata: cluster version, time range, duration106├── node/stats/ # Node-level CPU, memory, disk, network107├── etcd/stats/ # etcd DB size, latencies, leader elections108├── cluster/stats/ # Cluster-wide aggregates109├── resource/stats/ # API object counts, PVC usage110├── {component}/stats/ # Per-component CPU, memory, network111└── {component}/csv/ # Raw time-series (1-minute resolution)112```113114**Key files to read for sizing (node-level):**115- `node/stats/cpu-node.stats` — CPU usage in cores (use P95 for sizing)116- `node/stats/mem-node.stats` — Memory usage in GiB (use Max for sizing)117- `node/stats/disk-iops-*-node.stats`, `disk-tput-*-node.stats` — Disk I/O (IOPS and throughput in MB/s)118- `node/stats/disk-util-*-node.stats` — Disk partition **used space in GB** (bytes/1000^3); NOT a percentage119- `node/stats/net-rcv-node.stats`, `net-xmt-node.stats` — Network in MiB/s120- `etcd/stats/db-size.stats` — etcd database size in **GB** (decimal, bytes/1000^3)121122**Key files for component breakdown:**123- `{component}/stats/cpu-{component}.stats` — Component CPU in cores124- `{component}/stats/mem-{component}.stats` — Component memory in GiB125126**Warning:** A component directory often contains multiple memory stats files —127sub-groupings (e.g., `mem-acm-obs-rcv-total.stats`) and per-pod files128(e.g., `mem-acm-obs-pods.stats`). Always use the file whose name matches129`mem-{component}.stats` exactly for the component's total memory footprint.130Sub-component files will undercount and must not be used to characterize or131compare the component as a whole (e.g., as a fraction of a parent aggregate).132133**Key files for resource counts:**134- `resource/stats/*.stats` — Object counts (pods, configmaps, secrets, etc.)135- `resource/stats/pvc-usage.stats` — PVC consumption in **GB** (decimal, bytes/1000^3)136137**Reading stats files:**138The stats format is pandas `describe()` output with added percentiles. Each column139is a separate series (node name, pod name, or aggregate label). Key rows:140- `count` — number of data points (1-minute samples)141- `mean`, `std` — average and standard deviation142- `min`, `25%`, `50%`, `75%`, `95%`, `99%`, `max` — distribution143144**Units (already converted in stats files):**145- CPU: cores146- Memory: GiB (bytes / 1024^3)147- Network: MiB/s (bytes / 1024^2)148- Disk usage: GB (bytes / 1000^3)149- Disk throughput: MB/s (bytes / 1000^2)150- IOPS: operations/second151- etcd DB size: GB (bytes / 1000^3)152- PVC usage: GB (bytes / 1000^3)153154For multi-node clusters, stats files will have one column per node. Sum or take the155per-node value depending on the metric (CPU and memory are per-node; use each node's156value independently for per-node sizing).157158#### Method B: Direct Prometheus queries159160If the user has a live Prometheus/Thanos endpoint, query it directly. Read161`references/prometheus-queries.md` for a catalog of useful queries.162163Prometheus queries return raw values in base units (bytes, seconds). Convert:164- CPU: cores (from irate of cpu_seconds_total)165- Memory: bytes → GiB (divide by 1024^3)166- Network: bytes/s → MiB/s (divide by 1024^2)167- Disk: bytes → GB (divide by 1000^3)168169### Step 4: Analyze and Size170171#### Sizing metrics172173- **CPU sizing metric: P95 (95th percentile)** — represents sustained load excluding174 brief spikes. P95 is preferred over max because momentary spikes (GC, cert rotation)175 should not drive hardware selection.176- **Memory sizing metric: Max** — memory is the binding constraint in most deployments.177 Unlike CPU, memory pressure causes OOM kills, so size for the worst case observed.178179#### Per-phase analysis180181For each test phase, extract:1821. Node-level CPU P95 and Memory Max1832. Per-component CPU P95 and Memory Max (for component breakdown)1843. Disk usage max (root, etcd, container storage partitions)1854. Network throughput P951865. etcd DB size (P95 or Max)1876. Resource counts (pods, namespaces, configmaps)188189#### Growth rate analysis (for stepped/batched tests)190191If the test has incremental batches, compute per-unit growth rates:192- CPU cores per added unit (cluster, namespace, pod batch, etc.)193- Memory GiB per added unit194- etcd GiB per added unit195196Use these to identify which components scale most aggressively.197198#### Hardware tier calculation199200For each deployment scale and phase, compute hardware requirements at multiple201utilization targets:202203| Target | Use Case | Formula |204|--------|----------|---------|205| 60% | Conservative — maximum headroom | observed / 0.60 |206| 75% | Balanced — recommended default | observed / 0.75 |207| 90% | Cost-optimized — minimal headroom | observed / 0.90 |208209Round up to the nearest standard hardware tier:210- **CPU tiers (vCPUs):** 8, 12, 16, 24, 32, 48, 64, 96, 128211- **Memory tiers (GiB):** 32, 64, 96, 128, 192, 256, 384, 512, 768, 1024212213For multi-node clusters, compute per-node requirements. Note whether workload is214evenly distributed or if control plane nodes have different resource profiles than215workers.216217#### Operational limits to flag218219Check and report on these limits when applicable:220- **etcd DB size:** Default quota is 8 GiB binary = 8.59 GB decimal. Stats files221 are in decimal GB (bytes/1000^3), so compare observed values against 8.59 GB,222 not 8.00. Flag if observed or projected size approaches this limit.223- **Max pods:** Default is 250 per node (110 on some configurations). Report224 non-terminated pod count as a percentage of the limit. Use225 `cluster/stats/nonterm-pods-cluster.stats` for this — NOT226 `resource/stats/pods.stats`, which includes terminated and completed pods227 (e.g., finished Jobs) and will overcount against the limit.228- **Memory pressure:** Flag if observed memory exceeds 80% of installed RAM.229230### Step 5: Generate Report231232Ask the user which output format they prefer:233- **PDF** (via reportlab) — best for sharing with field consultants234- **Markdown** — best for embedding in wikis, docs, or repos235- **Plain text** — best for quick consumption in terminal236237#### Report structure238239Regardless of format, include these sections:2402411. **Test Environment** — cluster topology, hardware, software versions, workload description2422. **Test Methodology** — phases, durations, what each phase represents2433. **Resource Consumption** — per-phase tables for CPU, memory, disk, network244 - For stepped tests: show per-batch progression245 - For multi-scenario tests: cross-scenario comparison2464. **Component Breakdown** — which components consume what share of resources247 - Identify the top consumers and fastest-growing components2485. **Disk and Storage** — filesystem usage, PVC consumption, etcd DB size2496. **Network** — throughput at P95 and peak, NIC recommendation2507. **Hardware Sizing Recommendations** — utilization target tables, consolidated tiers2518. **Key Findings** — bullet-point summary of the most important takeaways252253#### PDF generation254255When generating PDF output, use reportlab with:256- BaseDocTemplate with TableOfContents257- Letter page size, 0.6-inch margins258- Styled tables with header row, alternating row colors259- Highlight rows for peak/worst-case values260261Read the `references/pdf-patterns.md` file for reportlab code patterns.262263#### Markdown generation264265Use standard GitHub-flavored Markdown with tables. Include a table of contents266using heading links.267268#### Plain text generation269270Use fixed-width formatted tables. Keep line width under 120 characters.271272## Important considerations273274- **Never assume components.** The user defines what software stack they're running.275 Don't hardcode ACM, MCE, GitOps, or any specific operator set. Ask what components276 are installed and which namespace groupings matter.277278- **Always understand the test methodology first.** Data without context is meaningless.279 A CPU spike during provisioning means something very different from a CPU spike during280 steady state.281282- **Memory is usually the binding constraint** for Kubernetes control planes. CPU283 drops significantly at steady state; memory does not. Flag this pattern when observed.284285- **Separate peak from steady state.** Hardware recommendations should cover the286 worst case (peak during active workload), but also call out the steady-state287 baseline since many deployments spend most of their time in steady state.288289- **Be explicit about units.** CPU in cores, memory in GiB (binary), disk in GB290 (decimal), network in MiB/s. State units in every table header.291292- **For multi-node clusters, report per-node.** Sizing recommendations are per-machine.293 Show how total cluster resources map to per-node requirements.294295- **Flag non-obvious risks.** Max-pods limits, etcd quota, memory-to-CPU imbalance,296 disk I/O bottlenecks — call these out even if the user didn't ask.297298- **Report observations, not theories.** Describe what the data shows; do not speculate299 about why. If two tests differ, state the difference and note that the cause is unknown300 unless the test design explicitly isolates that variable. Avoid causal language301 ("X drives Y", "because of Z") unless it is directly supported by the test data.302 Recommendations for further investigation are appropriate; assertions about root cause303 are not.304305---306> Source: [redhat-performance/acm-deploy-load](https://github.com/redhat-performance/acm-deploy-load) — distributed by [TomeVault](https://tomevault.io).307<!-- tomevault:4.0:skill_md:2026-05-22 -->