feat(traffic): a host's top senders, on its page and as get_host_senders (#6115)
The host page gains a "Who's sending" panel, and the MCP a tool, that put the host's outbound (or inbound) traffic down to its containers and NFS mounts: read in shared time buckets, so the host's busiest bucket says who was sending in it. A swarm service's tasks add up under its name; what none of them accounts for is the host itself. Also fixes CI 9488: per-container net_rx_bps / net_tx_bps join the docker alert catalog, so a rule can catch a container flooding the uplink. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
{# Who sent the host's traffic over the range (#6115): its containers and NFS
|
||||
mounts at the host's busiest moment, biggest first. Lazy-loaded below the
|
||||
charts and refreshed with them. #}
|
||||
<div class="section-title" style="margin-bottom:0.5rem;">Who's sending — last {{ range_key }}</div>
|
||||
{% if not got.host %}
|
||||
<p class="field-hint" style="margin:0;">No outbound traffic recorded for this host in the last {{ range_key }}.</p>
|
||||
{% else %}
|
||||
{% set top = got.senders[0] if got.senders else None %}
|
||||
<p style="margin:0 0 0.6rem;">
|
||||
Busiest at {{ got.host.peak_at|when }}: <strong>{{ got.host.peak_bps|rate }}</strong> out (average {{ got.host.avg_bps|rate }}).
|
||||
{% if top and top.at_host_peak_bps >= got.unaccounted_at_peak_bps %}Most of it from <strong>{{ top.name }}</strong> ({{ top.at_host_peak_bps|rate }}).
|
||||
{% else %}Most of it from <strong>no container or NFS mount</strong>: the host itself.{% endif %}
|
||||
</p>
|
||||
<table class="table">
|
||||
<thead><tr><th>Sender</th><th>At the peak</th><th>Average</th><th>Its own peak</th></tr></thead>
|
||||
<tbody>
|
||||
{% for r in got.senders %}
|
||||
<tr>
|
||||
<td><span title="{{ r.name }}">{{ "NFS " if r.kind == "nfs" }}{{ r.name }}</span></td>
|
||||
<td style="font-variant-numeric:tabular-nums;">{{ r.at_host_peak_bps|rate }}</td>
|
||||
<td style="font-variant-numeric:tabular-nums;">{{ r.avg_bps|rate }}</td>
|
||||
<td style="font-variant-numeric:tabular-nums;">{{ r.peak_bps|rate }} · {{ r.peak_at|when }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td style="color:var(--text-muted);">The host itself, or none of the above</td>
|
||||
<td style="font-variant-numeric:tabular-nums;">{{ got.unaccounted_at_peak_bps|rate }}</td>
|
||||
<td style="font-variant-numeric:tabular-nums;">{{ got.unaccounted_avg_bps|rate }}</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="field-hint" style="margin:0.5rem 0 0;">
|
||||
Read in {{ (got.bucket_s / 60)|round|int }}-minute steps; a shorter range shows a spike sharper.
|
||||
{% if got.more %}{{ got.more }} more sent less. {% endif %}
|
||||
A container's traffic to another on this host counts for it without leaving the host.
|
||||
</p>
|
||||
{% endif %}
|
||||
@@ -42,6 +42,14 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="hm-senders" class="card" style="margin-top:1rem;margin-bottom:0;"
|
||||
hx-get="/plugins/host_agent/{{ host.id }}/senders"
|
||||
hx-trigger="load, every {{ chart_poll_seconds }}s, rangeChange from:body"
|
||||
hx-include="#time-range"
|
||||
hx-swap="innerHTML">
|
||||
<div class="section-title" style="margin-bottom:0.5rem;">Who's sending</div>
|
||||
<p class="field-hint" style="margin:0;">Loading…</p>
|
||||
</div>
|
||||
{% if "syslog" in enabled_plugins %}<p id="hm-marks-note" class="field-hint" hidden style="margin:0.5rem 0 0;"></p>{% endif %}
|
||||
{# Hidden poller: fetches the history series and applies it to the charts above
|
||||
(no visible swap). Lazy on load → never blocks paint; refreshed slowly + on
|
||||
|
||||
Reference in New Issue
Block a user