feat(docker): a swarm service's metrics stay under one key across its tasks (#6718)
#6718 "A swarm container's metrics, normal and anomalies are keyed by its task name, so they start over whenever the task moves". The operator: "the metrics should be keys to the service name not the task name... as that's what's consistent between updates." CPU, memory, restart count and health now go under "swarm/<service>", so a normal, its anomalies and an alert rule carry across a redeploy or a move between nodes. Network rates stay "<host>/<service>", the task id gone, as they answer who on a host is sending. Tasks of a multi-task service share the key, so its level reads as an average across them (of 51 live services, only authentik-worker and portainer_agent run more than one task). History under task names moves in a background task, 5000 raw samples every 15 s, not a migration: the raw table holds about 2M such rows and the app would wait on it at boot. Hourly rows combine where two tasks share an hour, and rows still to review merge to one per metric. A service's anomalies no longer reach a host's page, as it has no one host; get_anomaly reads what happened around one from the service's own events. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -112,7 +112,8 @@ async def resource_url_for(db, source: str, resource: str) -> str | None:
|
||||
"""Where a (source, resource) of the metric store is shown, or None.
|
||||
|
||||
host_agent resources are the host's name, or "<host>:<mount|nic|core>";
|
||||
docker's are "<host>/<container>"; a monitor's is its name under its type.
|
||||
docker's are "<host>/<container>" or "swarm/<service>"; a monitor's is its
|
||||
name under its type.
|
||||
"""
|
||||
from steward.models.hosts import Host
|
||||
if source == "host_agent":
|
||||
@@ -125,6 +126,8 @@ async def resource_url_for(db, source: str, resource: str) -> str | None:
|
||||
select(Monitor.id).where(Monitor.type == source, Monitor.name == resource).limit(1)
|
||||
)).scalar_one_or_none()
|
||||
return f"/monitors/{monitor_id}" if monitor_id else None
|
||||
if source == "docker" and resource.startswith("swarm/"):
|
||||
return f"/plugins/docker/service/{resource.split('/', 1)[1]}"
|
||||
if source == "docker" and "/" in resource:
|
||||
host_name, container = resource.split("/", 1)
|
||||
host_id = (await db.execute(
|
||||
|
||||
Reference in New Issue
Block a user