01 · Read the usage
Once a night, every container, 30 days back.
The metrics scan runs daily. For each metrics tenant it queries CPU
and memory working set, the configured requests, OOM kills and
restarts for every container in every namespace, over a 30-day
window.
Nothing is collected by the assistant itself. It reads the
Prometheus or Mimir data you already have, read-only.
02 · Find the outliers
Only the containers worth a look go further.
A pre-filter picks the interesting ones, always measured against
the request:
- average use under 30% of the request (reserving too much),
- average, p95 or peak over 85% (about to hit the ceiling),
- any OOM kill or more than 3 restarts in the last day,
- no request at all, but real usage observed.
mimir-querier requests 5.6 GiB and uses about 64 MiB:
roughly 1% of what it reserves.
03 · Look at the shape
Average, p95 and peak, across all replicas.
An average hides bursts, so the sizing never rests on it alone.
For each container the assistant keeps the average, the 95th
percentile and the peak working set, taking the highest value
across all 8 replicas that ran in the window.
Here the three numbers sit close together: 64 MiB average, 76 MiB
p95, 80 MiB peak.
04 · Classify
Flat or spiky? The answer changes the rule.
Volatility is peak divided by p95. At 1.25 or below the
workload is flat: there are no real spikes, so a big gap
between request and limit is just reserved memory nobody uses.
Above 1.25 it is spiky, and a second number decides how much
headroom to reserve: namespace concurrency, whether the pods
around it tend to peak at the same time or take turns.
The querier: 80 / 76 = 1.06, flat.
05 · Draft the change
The model proposes values, inside written rules.
The measured numbers, the classification and the current manifest
go to the language model together with the sizing rules for each
case. It returns a small structured proposal: which values to
change, and why.
It is allowed to say "keep as proposed". Tuning for the sake of
tuning is not the goal.
06 · Clamp it
Code, not the model, has the last word on the numbers.
Models sometimes ignore their own rules, so the hard limits are
enforced again in code after the model answers:
- memory request never below the observed peak,
- memory limit never below the observed peak, never above five times it,
- no more than a 5× gap between request and limit,
- requests never above limits,
- CPU limits removed: CPU is throttled, not killed, so a CPU limit only adds latency.
Here the model suggested a 64 MiB request. That is below the
80 MiB peak, so it was raised.
07 · Write the smallest diff
Only the resource values move.
The change is written back into the file in place, so comments,
key order and every other value stay exactly as they were.
Values are rounded up to standard steps (64, 96, 128 MiB…),
never down.
For a HelmRelease the edit goes into spec.values, where
the chart takes it from, not into rendered output.
08 · The proposal
The evidence travels with the change.
Every resource-touching MR carries the measurements it was sized
from: classification, volatility, namespace concurrency, average,
p95 and peak, how many replicas were observed, and which safety
clamps fired. You can check the arithmetic without opening
Grafana.