feat(docker): Docker disk is recorded over time per host, the Disk page shows each host's growth and folds images under their repository, and the overview has one disk line per host (#5304–#5306)
- #5304 "Docker disk totals recorded over time per host, with a growth chart and alertable metrics" - #5305 "Disk page: images grouped by repository, versions folded under it" - #5306 "Docker overview: one disk line per host linking to the Disk page" Each `docker system df` report is now also recorded as metrics, with the host as the resource: disk_total_bytes, its parts, and disk_reclaimable_bytes. An alert rule can watch them. The Disk page starts with the hosts side by side, showing how much each grew over the selected range with a line of it, then gives each host its own section. An image's versions fold under its repository, which is charged only for the layers its versions don't share. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -611,7 +611,8 @@ def test_disk_usage_persisted(app):
|
||||
async with s.begin():
|
||||
await s.execute(text("DELETE FROM docker_images"))
|
||||
await s.execute(text("DELETE FROM docker_disk_usage"))
|
||||
h = Host(id=str(uuid.uuid4()), name="dsk", address="10.6.6.6")
|
||||
hid = str(uuid.uuid4())
|
||||
h = Host(id=hid, name=f"dsk-{hid[:8]}", address="10.6.6.6")
|
||||
s.add(h)
|
||||
await s.flush()
|
||||
hid = h.id
|
||||
@@ -631,9 +632,17 @@ def test_disk_usage_persisted(app):
|
||||
reclaimable2 = (await s.execute(text(
|
||||
"SELECT images_reclaimable FROM docker_disk_usage WHERE host_id=:h"),
|
||||
{"h": hid})).scalar()
|
||||
return summary, img_count, [r[0] for r in after], reclaimable2
|
||||
# Each report is recorded as metrics too, resource = the host (#5304).
|
||||
metrics = (await s.execute(text(
|
||||
"SELECT metric_name, value FROM plugin_metrics WHERE source_module = 'docker' "
|
||||
"AND resource_name = :r"), {"r": f"dsk-{hid[:8]}"})).all()
|
||||
return summary, img_count, [r[0] for r in after], reclaimable2, metrics
|
||||
|
||||
summary, img_count, after_ids, reclaimable2 = asyncio.run(_go())
|
||||
summary, img_count, after_ids, reclaimable2, metrics = asyncio.run(_go())
|
||||
assert {v for m, v in metrics if m == "disk_total_bytes"} == {1155.0} # 1000 + 100 + 40 + 15
|
||||
assert sorted(v for m, v in metrics if m == "disk_reclaimable_bytes") == [0.0, 700.0]
|
||||
assert {m for m, _ in metrics} == {"disk_images_bytes", "disk_containers_bytes", "disk_volumes_bytes",
|
||||
"disk_build_cache_bytes", "disk_total_bytes", "disk_reclaimable_bytes"}
|
||||
assert summary[0] == 700 and summary[1] == 2 and summary[2] == 15
|
||||
assert img_count == 2
|
||||
assert after_ids == ["aaaaaaaaaaaa"] # the unreferenced image pruned on re-report
|
||||
|
||||
Reference in New Issue
Block a user