The agent sent Docker's working set (usage less inactive_file), which still counts recently used file cache. A file-heavy container with no limit read 191 GB while its processes held 2.2 GB, and the host's own figure, which already leaves cache out, disagreed with it. Agent 1.23.0 sends mem_used_bytes / mem_used_pct (anon + shmem, so Postgres's shared buffers count; v1: rss + shmem) and mem_cache_bytes apart. The new names keep the two meanings apart in history and in learned normals: docker_013 renames the columns, clears the old values and adds mem_cache_bytes. The Docker page, swarm by-node sums, Busiest Containers, container detail, normals and the MCP tools all read the new figure. File cache is shown only on the container's page and in get_container. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
134 lines
7.5 KiB
HTML
134 lines
7.5 KiB
HTML
{# docker/container_detail.html — one container (#5310), shaped like a
|
|
service's page: a header that says whether it is well and what it is; why
|
|
it stopped, only while it has; what it moved since it started; its CPU and
|
|
memory over time (the one home for what it uses now), with its metric
|
|
anomalies above when it has any; its lifecycle.
|
|
A swarm task is titled by its service and slot, the raw name under it. #}
|
|
{% extends "base.html" %}
|
|
{% from "_macros.html" import crumbs, status_badge %}
|
|
{% block title %}{{ label or name }} — Docker — Steward{% endblock %}
|
|
{% block breadcrumb %}
|
|
{%- if container is not none and container.service_name -%}
|
|
{{ crumbs([("Docker", "/plugins/docker/"), (container.service_name, "/plugins/docker/service/" ~ container.service_name), (label, "")]) }}
|
|
{%- else -%}
|
|
{{ crumbs([("Docker", "/plugins/docker/"), (label or name, "")]) }}
|
|
{%- endif -%}
|
|
{% endblock %}
|
|
{% block content %}
|
|
|
|
{% if container is none %}
|
|
<div class="card empty-state">
|
|
<strong>Container not found</strong>
|
|
<span>No container named <code>{{ name }}</code> is reported for this host now. It may have been
|
|
removed, or the host agent has not reported recently. <a href="/plugins/docker/">Back to Docker</a></span>
|
|
</div>
|
|
{% else %}
|
|
{% set c = container %}
|
|
|
|
{# ── Header: the verdict, then what it is ────────────────────────────────── #}
|
|
<div class="page-header">
|
|
<div style="min-width:0;">
|
|
<h1 class="page-title" style="overflow-wrap:anywhere;">{{ label }}</h1>
|
|
<p class="page-subtitle">
|
|
{% if c.status == 'running' %}{{ status_badge('ok', 'Running') }}
|
|
{% elif c.status == 'restarting' %}{{ status_badge('crit', 'Restarting') }}
|
|
{% elif c.status == 'paused' %}{{ status_badge('warn', 'Paused') }}
|
|
{% elif exit_failed %}{{ status_badge('crit', c.status|capitalize) }}
|
|
{% else %}{{ status_badge('dim', c.status|capitalize) }}{% endif %}
|
|
{%- if c.health == 'unhealthy' %} {{ status_badge('crit', 'Unhealthy') }}
|
|
{%- elif c.health == 'starting' %} {{ status_badge('pending', 'Starting') }}
|
|
{%- elif c.health == 'healthy' %} {{ status_badge('ok', 'Healthy') }}{% endif %}
|
|
{%- if uptime %} · up {{ uptime }}{% endif %}
|
|
{%- if host %} · on <a href="/hosts/{{ host.id }}">{{ host.name }}</a>{% endif %}
|
|
{%- if c.service_name %} · task of <a href="/plugins/docker/service/{{ c.service_name }}">{{ c.service_name }}</a>
|
|
{%- elif c.compose_project %} · compose <span class="chip">{{ c.compose_project }}</span>{% endif %}
|
|
{%- if c.image %} · <span class="mono" style="overflow-wrap:anywhere;" title="{{ c.image }}">{{ c.image.split("@")[0] }}</span>{% endif %}
|
|
</p>
|
|
<p class="page-subtitle tone-dim" style="margin-top:0.2rem;">
|
|
{%- if label != c.name %}<span class="mono" style="overflow-wrap:anywhere;">{{ c.name }}</span> · {% endif -%}
|
|
{%- if c.scraped_at %}reported {{ c.scraped_at|ago }}{% endif -%}
|
|
</p>
|
|
</div>
|
|
<div class="page-actions"><a class="btn btn-sm btn-ghost" href="/plugins/docker/container/{{ host_id }}/{{ name }}/logs">Logs</a></div>
|
|
</div>
|
|
|
|
{# ── Why it stopped, only while it has ───────────────────────────────────── #}
|
|
{% if c.status != 'running' and exit_failed %}
|
|
<section class="card problem-card" aria-labelledby="ctr-exit">
|
|
<h2 id="ctr-exit" class="section-title" style="margin-bottom:0.4rem;">Last exit</h2>
|
|
<p style="margin:0;">
|
|
Exited <span class="tone-crit">{{ c.exit_code }}</span>{% if c.oom_killed %}: killed for running out of memory{% endif %}
|
|
{%- if died_at %} · {{ died_at|ago }}{% endif %}
|
|
· <a href="/plugins/docker/container/{{ host_id }}/{{ name }}/logs">its logs →</a>
|
|
</p>
|
|
</section>
|
|
{% endif %}
|
|
|
|
{# ── Its CPU or memory out of its normal for the hour, and past anomalies (#5742) #}
|
|
{% with back=anomaly_back, base_resource=anomaly_resource %}{% include "_anomaly_card.html" %}{% endwith %}
|
|
|
|
{# ── What it is set up with, and what it moved since it started ─────────── #}
|
|
<div class="card">
|
|
<dl class="kv">
|
|
{% if not c.service_name %}
|
|
{#- Swarm never restarts a task's container: it replaces the task, so a
|
|
task's restart count is always 0; its service's Tasks table is the
|
|
count that means something. #}
|
|
<dt>Restarts</dt><dd>{% if c.restart_count %}<span class="tone-warn">{{ c.restart_count }}</span>{% else %}0{% endif %}
|
|
{%- if c.exit_code is not none and c.status == 'running' and c.restart_count %} <span class="tone-dim">· last exit {{ c.exit_code }}</span>{% endif %}</dd>
|
|
{% endif %}
|
|
<dt>Memory limit</dt><dd>{% if usage.mem_limit %}{{ usage.mem_limit|bytes }}{% else %}<span class="tone-dim">none set</span>{% endif %}</dd>
|
|
{#- Its memory figure is what its processes hold; the file cache charged to
|
|
it is shown here only, since the kernel gives it back when the host
|
|
needs it (#6030). #}
|
|
<dt>File cache</dt><dd>{% if c.mem_cache_bytes is not none %}{{ c.mem_cache_bytes|bytes }} <span class="tone-dim">files it read or wrote, kept in memory until the host needs it back; not counted in its memory</span>{% else %}<span class="tone-dim">—</span>{% endif %}</dd>
|
|
{% if ports %}
|
|
<dt>Ports</dt><dd class="mono">{% for p in ports %}{{ p.host_port }}:{{ p.container_port }}/{{ p.protocol }}{% if not loop.last %}, {% endif %}{% endfor %}</dd>
|
|
{% endif %}
|
|
{% if c.node_id %}
|
|
<dt>Swarm node</dt><dd title="node id {{ c.node_id }}">{{ node_name or c.node_id[:12] }}</dd>
|
|
{% endif %}
|
|
<dt>Network</dt><dd>{{ net_rx }} in · {{ net_tx }} out <span class="tone-dim">since it started</span></dd>
|
|
<dt>Disk</dt><dd>{{ blk_read }} read · {{ blk_write }} written <span class="tone-dim">since it started</span></dd>
|
|
</dl>
|
|
</div>
|
|
|
|
{# ── Resource history: the one home for what it uses ────────────────────── #}
|
|
<section class="card" aria-labelledby="ctr-history">
|
|
<div style="display:flex;align-items:center;justify-content:space-between;gap:1rem;margin-bottom:0.75rem;flex-wrap:wrap;">
|
|
<h2 id="ctr-history" class="section-title" style="margin:0;">Resource history</h2>
|
|
{% include "_time_range.html" %}
|
|
</div>
|
|
<div id="container-history"
|
|
hx-get="/plugins/docker/container/{{ host_id }}/{{ name }}/history"
|
|
hx-trigger="load, rangeChange from:body"
|
|
hx-include="#time-range"
|
|
hx-swap="innerHTML">
|
|
<p class="field-hint">Loading…</p>
|
|
</div>
|
|
</section>
|
|
|
|
{# ── Lifecycle, newest first ─────────────────────────────────────────────── #}
|
|
<section class="card-flush" aria-labelledby="ctr-lifecycle">
|
|
<h2 id="ctr-lifecycle" class="table-caption">Lifecycle <span class="tone-muted">newest first</span></h2>
|
|
{% if events %}
|
|
<table class="table">
|
|
<thead><tr><th>Event</th><th>Detail</th><th class="num">When</th></tr></thead>
|
|
<tbody>
|
|
{% for e in events %}
|
|
<tr>
|
|
<td><span class="tone-{{ e.tone }}" aria-hidden="true">{{ e.glyph }}</span> {{ e.event|replace("_", " ") }}</td>
|
|
<td class="tone-muted" style="overflow-wrap:anywhere;">{{ e.detail or "" }}</td>
|
|
<td class="num">{{ e.at|ago }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% else %}
|
|
<p class="tone-muted" style="margin:0;padding:1rem;">No lifecycle events recorded yet. Starts, stops and health changes appear here as the agent reports them.</p>
|
|
{% endif %}
|
|
</section>
|
|
|
|
{% endif %}
|
|
{% endblock %}
|