{% extends "base.html" %} {% block title %}Kubernetes{% endblock %} {% block content %} {% from '_limit.html' import limit, paused, locked %} {% if not plan.kubernetes %}{{ locked('Kubernetes monitoring is on Pro (3 clusters) and Enterprise, or the Observability module. Nodes keep reporting basic metrics.') }}{% endif %}

Kubernetes

Every node runs the Vigil agent as a DaemonSet and reports its pods, container states and node conditions. {{ totals.clusters }} cluster{{ 's' if totals.clusters != 1 }} · {{ totals.nodes }} node{{ 's' if totals.nodes != 1 }} · {{ totals.pods }} pods.

Servers

Install on a cluster

{% if manifest %}

Creates namespace vigil, a ServiceAccount with read access to pods/nodes, a Secret with the token and a DaemonSet (python:3.12-slim, hostNetwork, host filesystem mounted read-only at /host). Each node shows up as a server named <cluster>/<node>. Change cluster= to name the cluster; the token below belongs to {{ token_server.name }} (any server token in this workspace works; nodes register themselves).

kubectl apply -f "{{ manifest }}"
{% if not manifest_ready %}
The manifest file is not available on this Vigil build yet; the URL returns 404 until it ships. Meanwhile install the agent per node with the shell command on a server's Install tab.
{% endif %}

Verify: kubectl -n vigil get pods -o wide · logs: kubectl -n vigil logs ds/vigil-agent · remove: kubectl delete ns vigil (plus the ClusterRole/Binding).

{% else %}

Add a server first (Servers → Add server) to get a token, then come back for the kubectl apply one-liner.

{% endif %}
{% if not groups %}

No Kubernetes nodes reporting yet

Apply the DaemonSet above. Nodes appear here within a minute, grouped by cluster, with pods, restarts, CrashLoopBackOff / OOMKilled containers and node pressure conditions.

{% endif %} {% for g in groups %}

Cluster {{ g.name }} {% if g.problems %}{{ g.problems|length }} problem{{ 's' if g.problems|length != 1 }}{% else %}healthy{% endif %}

{{ g.nodes|length }} nodes · {{ g.totals.pods }} pods ({{ g.totals.running }} running{% if g.totals.pending %}, {{ g.totals.pending }} pending{% endif %}{% if g.totals.failed %}, {{ g.totals.failed }} failed{% endif %}) · {{ g.totals.restarts_1h }} restarts in the last hour
{% for n in g.nodes %}{% set s = n.server %}
{% if s.online %}{% elif s.online is sameas false %}{% else %}{% endif %}{{ n.node }}{% if n.role == 'control-plane' %}control-plane {% endif %}{% if s.online is sameas false %}offline{% elif n.ready %}Ready{% else %}NotReady{% endif %}
{{ n.pods }} pods · {{ n.running }} running{% if n.problems %} · {{ n.problems }} with problems{% endif %}{% if n.restarts_1h %} · {{ n.restarts_1h }} restarts/1h{% endif %}
{% for p in n.pressure %}{{ p }} {% endfor %}{% set smp = s.last_sample or {} %}cpu {{ '%.0f'|format((smp.get('cpu') or {}).get('pct') or 0) }}% · mem {{ '%.0f'|format((smp.get('mem') or {}).get('pct') or 0) }}%
{% endfor %}
{% if g.problems %}

Problems

{% for p in g.problems %}{% endfor %}
{{ p.kind }}{% if p.kind == 'pod' %}{{ p.namespace }}/{{ p.name }}{% else %}{{ p.node }}{% endif %}{{ p.what }}{% if p.kind == 'pod' %}on {{ p.node }}{% endif %}node →
{% endif %}
{% if namespace or phase or only_problems or cluster %}Clear{% endif %} {{ g.filtered|length }} of {{ g.pods|length }} pods
{% for p in g.filtered %} {% else %}{% endfor %}
NamespacePodNodePhaseReadyRestartsContainersCPUMemoryAge
{{ p.namespace }}{{ p.name }}{% if p.owner %}
{{ p.owner }}
{% endif %}
{{ p.node }} {{ p.phase or '?' }}{% for pr in p.problems %}
{{ pr }}
{% endfor %}
{{ p.ready }}/{{ p.total }}{{ p.restarts or 0 }} {% for c in p.containers or [] %}
{{ c.name }} {{ c.state or '' }}{% if c.reason %} {{ c.reason }}{% endif %}
{% endfor %}
{% if p.cpu_millicores is not none %}{{ p.cpu_millicores|int }}m{% else %}—{% endif %}{% if p.mem_bytes is not none %}{{ fmt_bytes(p.mem_bytes) }}{% else %}—{% endif %}{{ fmt_uptime(p.age_s) }}
No pods match.
{% endfor %} {% endblock %}