refactor(docker): container CPU and memory live in the one metric store (#5752)
#5752 "Container CPU and memory history lives in the one metric store; docker_metrics and docker_metrics_hourly are retired". Container cpu_pct and mem_pct already went to plugin_metrics for alerting; now mem_usage_bytes does too, and every snapshot in an ingest (backlog included) is recorded at its own time. The container history chart, Busiest Containers, a service's history and the MCP container tools read plugin_metrics under resource "<host>/<container>", rolled up and pruned by the shared metric retention. docker_012_drop_metrics drops both tables. Their history is not copied (lesson #5221 "A data migration's cost is set by the live table"); the store already holds 7 days of container CPU and memory %. Docker retention now prunes only lifecycle events, and Settings -> Thresholds has one "Metric retention" card for every source. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -40,7 +40,7 @@ EXCLUDED: frozenset[str] = frozenset({
|
||||
# Run history; its logs live on disk, not in the database.
|
||||
"ansible_runs",
|
||||
"docker_containers", "docker_disk_usage", "docker_events", "docker_host_status",
|
||||
"docker_images", "docker_logs", "docker_metrics", "docker_metrics_hourly",
|
||||
"docker_images", "docker_logs",
|
||||
"docker_swarm_nodes", "docker_swarm_services",
|
||||
"traefik_access_summaries", "traefik_certs", "traefik_global_stats", "traefik_metrics",
|
||||
"traefik_status_codes",
|
||||
|
||||
+7
-11
@@ -23,8 +23,9 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
# Time-series columns pruned at data.retention_days (#5155). A plugin adds its
|
||||
# own with prune_by_age() in setup(), so the core never imports plugin models.
|
||||
# Tables kept another way are not listed: plugin_metrics and the docker tables
|
||||
# are rolled up (below), and the audit log has its own, longer window.
|
||||
# Tables kept another way are not listed: plugin_metrics is rolled up (below),
|
||||
# the docker events and logs have their own windows, and the audit log has its
|
||||
# own, longer window.
|
||||
_AGED: list = [MonitorResult.checked_at, AnsibleRun.started_at, AlertEvent.transitioned_at,
|
||||
EmailOutbox.created_at]
|
||||
|
||||
@@ -40,9 +41,7 @@ def prune_by_age(column) -> None:
|
||||
_METRIC_COUNTS = [("raw_rows_rolled", "plugin_metrics: raw samples rolled into hourly"),
|
||||
("rollup_pruned", "plugin_metrics_hourly: past the rollup window"),
|
||||
("latest_pruned", "plugin_metric_latest: stopped reporting")]
|
||||
_DOCKER_COUNTS = [("raw_rows_rolled", "docker_metrics: raw samples rolled into hourly"),
|
||||
("rollup_pruned", "docker_metrics_hourly: past the rollup window"),
|
||||
("events_pruned", "docker_events: past the events window"),
|
||||
_DOCKER_COUNTS = [("events_pruned", "docker_events: past the events window"),
|
||||
("logs_age_pruned", "docker_logs: past the age window"),
|
||||
("logs_size_pruned", "docker_logs: over a container's size cap")]
|
||||
|
||||
@@ -142,13 +141,13 @@ async def _run_metrics_retention(session, now: datetime) -> dict:
|
||||
|
||||
|
||||
async def _run_docker_retention(session, now: datetime) -> dict | None:
|
||||
"""Drive the docker plugin's rollup + prune and its log ring via its
|
||||
"""Drive the docker plugin's event prune and its log ring via its
|
||||
capabilities, if loaded.
|
||||
|
||||
Windows are read fresh from settings each run (rule 25 — a change in the
|
||||
Settings UI takes effect on the next hourly cleanup, no restart). Each runs
|
||||
in its own transaction, so a docker-side failure can't roll back the generic
|
||||
prune above, and the log ring's deletes don't wait on the metrics rollup
|
||||
prune above, and the log ring's deletes don't wait on the event prune
|
||||
(#5449). No-op when the docker plugin is disabled (capability absent).
|
||||
"""
|
||||
from steward.core.capabilities import has_capability, invoke_capability
|
||||
@@ -160,13 +159,10 @@ async def _run_docker_retention(session, now: datetime) -> dict | None:
|
||||
# Reads + rollup/prune share one transaction — get_setting's SELECT would
|
||||
# otherwise autobegin one, making a later session.begin() raise.
|
||||
async with session.begin():
|
||||
raw_days = int(await get_setting(session, "docker.retention.metrics_raw_days") or 7)
|
||||
rollup_days = int(await get_setting(session, "docker.retention.metrics_rollup_days") or 90)
|
||||
events_days = int(await get_setting(session, "docker.retention.events_days") or 30)
|
||||
counts = await invoke_capability(
|
||||
"docker.run_retention", UserRole.viewer, session,
|
||||
events_days=events_days, metrics_raw_days=raw_days,
|
||||
metrics_rollup_days=rollup_days, now=now,
|
||||
events_days=events_days, now=now,
|
||||
)
|
||||
async with session.begin():
|
||||
logs_days = int(await get_setting(session, "docker.logs.retention_days") or 3)
|
||||
|
||||
@@ -311,6 +311,7 @@ METRICS: dict[str, tuple[str, str]] = {
|
||||
"total_clients": ("Connected clients", "devices on the network"),
|
||||
"cpu_pct": ("CPU", "CPU in use; for a container, 100% is one core"),
|
||||
"mem_pct": ("Memory (% of limit)", "memory in use, against the container's limit or the host's RAM"),
|
||||
"mem_usage_bytes": ("Memory used", "a container's memory in use, in bytes"),
|
||||
"restart_count": ("Restarts", "times the container has restarted, in total"),
|
||||
"disk_total_bytes": ("Docker disk use", "everything Docker keeps on the host (resource: the host)"),
|
||||
"disk_images_bytes": ("Docker images", "space Docker's images take on the host"),
|
||||
|
||||
@@ -117,11 +117,8 @@ DEFAULTS: dict[str, Any] = {
|
||||
# Days an ended metric anomaly waits to be reviewed before it lapses:
|
||||
# it leaves the to-review list and is kept as not reviewed (#5740).
|
||||
"anomalies.review_days": 14,
|
||||
# Docker time-series retention (rule 25 — tunable, no restart). Raw 30s
|
||||
# samples are heavy, so keep a short raw window then roll up to hourly
|
||||
# averages kept much longer; lifecycle events are light, keep a month.
|
||||
"docker.retention.metrics_raw_days": 7,
|
||||
"docker.retention.metrics_rollup_days": 90,
|
||||
# Docker lifecycle events are light; keep a month (rule 25 — tunable, no
|
||||
# restart). Container CPU and memory follow metrics.retention.* (#5752).
|
||||
"docker.retention.events_days": 30,
|
||||
# Container logs (m79): on by default for every container (operator
|
||||
# preference). `exclude` names containers the server drops on ingest; the
|
||||
|
||||
@@ -149,8 +149,6 @@ _THRESHOLD_FIELDS = [
|
||||
# Docker time-series retention windows (days). Read fresh by the cleanup task,
|
||||
# so a save takes effect on the next hourly run — no app.config wiring needed.
|
||||
_RETENTION_FIELDS = [
|
||||
("docker_metrics_raw_days", "docker.retention.metrics_raw_days"),
|
||||
("docker_metrics_rollup_days", "docker.retention.metrics_rollup_days"),
|
||||
("docker_events_days", "docker.retention.events_days"),
|
||||
("docker_logs_retention_days", "docker.logs.retention_days"),
|
||||
("metrics_raw_days", "metrics.retention.raw_days"),
|
||||
|
||||
@@ -104,18 +104,13 @@
|
||||
</div>
|
||||
|
||||
<div class="card" style="max-width:640px;margin-top:1.5rem;">
|
||||
<h2 class="section-title" style="margin-bottom:0.5rem;">Docker data retention</h2>
|
||||
<h2 class="section-title" style="margin-bottom:0.5rem;">Docker lifecycle events</h2>
|
||||
<p style="font-size:0.82rem;color:var(--text-muted);margin-bottom:1.25rem;">
|
||||
Bounds how much Docker history is stored. Raw per-sample container metrics are
|
||||
kept for the raw window, then rolled up into hourly averages kept for the
|
||||
rollup window; lifecycle events are kept for the events window. Applied by the
|
||||
hourly cleanup task — a change takes effect on its next run.
|
||||
How long container start, stop, die and health changes are kept. Container CPU
|
||||
and memory are kept with the other metrics, under Metric retention below.
|
||||
Applied by the hourly cleanup task — a change takes effect on its next run.
|
||||
</p>
|
||||
|
||||
{{ days("Raw metrics", "docker_metrics_raw_days", "docker.retention.metrics_raw_days",
|
||||
"Keep per-sample container CPU/memory points this long, then roll up to hourly.") }}
|
||||
{{ days("Rolled-up metrics", "docker_metrics_rollup_days", "docker.retention.metrics_rollup_days",
|
||||
"Keep the hourly-averaged series this long for multi-day history.") }}
|
||||
{{ days("Lifecycle events", "docker_events_days", "docker.retention.events_days",
|
||||
"Keep container start/stop/die/health history this long.") }}
|
||||
</div>
|
||||
@@ -169,16 +164,18 @@
|
||||
</div>
|
||||
|
||||
<div class="card" style="max-width:640px;margin-top:1rem;">
|
||||
<h2 class="section-title" style="margin-bottom:0.5rem;">Host metrics retention</h2>
|
||||
<h2 class="section-title" style="margin-bottom:0.5rem;">Metric retention</h2>
|
||||
<p style="font-size:0.82rem;color:var(--text-muted);margin-bottom:1.25rem;">
|
||||
Bounds how much host-agent metric history is stored (CPU, memory, disk, network,
|
||||
temps, …). Raw per-sample points are kept for the raw window, then rolled up
|
||||
into hourly averages kept for the rollup window. Host charts read raw for the
|
||||
recent part of a range and hourly for older data. Applied by the hourly cleanup.
|
||||
Bounds how much metric history is stored: hosts (CPU, memory, disk, network,
|
||||
temps, …), containers (CPU and memory), monitors and the other sources. Raw
|
||||
per-sample points are kept for the raw window, then rolled up into hourly
|
||||
figures (average, peak, and the level held) kept for the rollup window. Charts
|
||||
read raw for the recent part of a range and hourly for older data. Applied by
|
||||
the hourly cleanup.
|
||||
</p>
|
||||
|
||||
{{ days("Raw metrics", "metrics_raw_days", "metrics.retention.raw_days",
|
||||
"Keep per-sample host metrics this long, then roll up to hourly averages.") }}
|
||||
"Keep per-sample metrics this long, then roll up to hourly.") }}
|
||||
{{ days("Rolled-up metrics", "metrics_rollup_days", "metrics.retention.rollup_days",
|
||||
"Keep the hourly-averaged series this long for multi-week history.") }}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user