Docs follow the Elastic swap, and apply removes orphaned containers

README 5h, Phase 13, FRESH-INSTALL 7b and appendix M, and STATUS all describe four Elastic containers with
Kibana as the single pane instead of the Victoria shape. Phase 13 keeps the wrong turn on the record: the
first build had no dashboards, which was the point of the exercise, and Grafana was rejected for this purpose
because it is a UI only and its stack is the same piece count with more parts.

configure.py apply now passes --remove-orphans, which is how the eight containers the compose file no longer
lists actually go away rather than lingering.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-11-03 16:58:08 -06:00
parent 6f7fb29ae0
commit ef357c6d82
5 changed files with 95 additions and 85 deletions

View File

@@ -320,45 +320,31 @@ permanent reference is needed for long sessions, not just a start burst.
export at 2560×1440)
- rejected: LTC audio (SRT multitrack + per-PC generator); clock inside the picture; cropping at remux (re-encode)
### Phase 13 — `diag.` host: logs, metrics, alerting, deadman (built; needs DNS + a webhook)
### Phase 13 — `diag.` host: Kibana (logs + metrics + alerting) (built; needs a webhook + two Kibana rules)
**Decided 2026-11-03: VictoriaLogs**, on `diag.BASE`, log UI at `/logs`. Rejected: ELK (a JVM heap and a
growing index on a box that spends its nights running x264, to search one host's logs) and Grafana + Loki (two
services and a dashboard tool we would only use as a log viewer). The framework everything feeds is
`LOGGING.md`; as-built detail lives there and in README §5h.
**Decided 2026-11-03, after one wrong turn.** First build was VictoriaLogs + Vector + VictoriaMetrics + two
vmalert instances + Alertmanager, eight containers counting the exporters, and it produced **no dashboards** —
five vendor UIs behind one hostname and an index page of links. The requirement was one tool with views, and
the honest answers to that were Netdata (one container, weakest log search) or Elastic (four containers, one
UI, strongest log search). Elastic was chosen. Grafana was considered and rejected *for this purpose*: it is a
UI only, so its stack is Loki + Prometheus + Alloy + Grafana, which is the same piece count with more parts.
Six containers, all loopback, published only through the gated `diag.` host:
`victorialogs` (30 d) · `vector` · `victoriametrics` (90 d) · `vmalert-metrics` · `vmalert-logs` · `alertmanager`.
Four containers: `elasticsearch` (both logs and metrics) · `kibana` (the whole `diag.` host, at the root) ·
`filebeat` · `metricbeat` (which also retires node_exporter and cAdvisor). Detail in README §5h and LOGGING.md.
- [x] `configure.py`: `diag` host role, `DIAG_REQUIRE_GROUP` (default `media-admins`), `ALERT_WEBHOOK_URL`
asked by `--full`, written to `/etc/vanguard/alert-webhook` (0640 root:65534)
- [x] `install.sh`: the three data dirs with the right owners, `/var/lib/vanguard`, the webhook file (compose
bind-mounts it, so without it Docker would create a *directory* and alertmanager would not start),
the deadman script and timer
- [x] alerting: vmalert per datasource → Alertmanager → Discord. 11 metric rules, 5 LogsQL rules, inhibits so
a critical disk does not also report as filling
- [x] deadman (`bin/vanguard-deadman.sh`, host timer, 5 min): component health, the `Watchdog` heartbeat
through vmalert → Alertmanager → dumpd `/heartbeat`, and the disks. Reports on change, announces recovery
once, degrades to journal-only with no webhook and exits non-zero so the unit state is the signal
- [x] collection = **option A**: vector holds the Docker socket. Root-equivalent on this host, recorded in
`observability/vector.yaml`. Upgrades: journald log driver everywhere, or a read-only socket proxy
- [ ] CNAME `diag` → the main host
- [ ] a Discord webhook for alerts (its own channel, not the live notices) → `ALERT_WEBHOOK_URL` in `.env`
- [ ] `./configure.py --hosts AUTH WATCH GIT BOT LIVE FILES SYNC CLIPS DIAG` — **not `--apply`**: apply reads
the hostnames back out of the Caddyfile, so it cannot introduce a host that is not in there yet and will
leave the new name without a site block or a certificate without reporting anything
(HOSTNAME-CHANGE.md §D). Pulls ~500 MB of images on the first run.
- [ ] verify: all six answer, `/logs` shows events from several services, `/alerts` lists `Watchdog` as firing,
`journalctl -u vanguard-deadman` is quiet
- [x] first-run corrections (2026-11-03): `current_boot_only: false` is rejected on systemd 257; the
ingestion path is under `-http.pathPrefix` too (`/logs/insert/elasticsearch/`); every proxied UI needs
its own prefix or it renders broken; a Caddy `respond` body eats `{...}` so the index is a file
- [ ] still to confirm with real traffic: Vector's journald field names (our timers showing the right
`service` label) and the LogsQL rule syntax (`vmalert-logs` logging no errors)
- [ ] later: ops controls on the same host (restart buttons). Design note so it is not built the easy wrong
way — allowlisted dumpd endpoints, authorised from the Authentik headers like everything else, every
invocation logged as a logfmt event so the action lands beside the logs it is diagnosing. Never a web
page with Docker access, never a generic command runner.
- [x] compose: the four, all loopback, published only through the gated `diag.` host
- [x] pinned: 2 GB heap, `vm.max_map_count`, security off (loopback + Authentik), fixed Kibana encryption key
- [x] both Beats emit the same correlation fields, so one dashboard spans logs and metrics
- [x] `configure.py`: `diag` role → Kibana at the root; `--remove-orphans` on apply so the old eight go away
- [x] `install.sh`: the ES data dir, the sysctl, the deadman
- [x] deadman repointed at Elastic, including the two failures a process check misses: a Beat that runs without
shipping, and indices flipped read-only by the watermark (which never clears itself)
- [ ] a Discord webhook → `ALERT_WEBHOOK_URL`
- [ ] in Kibana: the retention delete phase; the always-firing heartbeat rule → dumpd `/heartbeat`; a webhook
connector for alerts; then rules for the conditions the old vmalert files listed (disk, load, restart
loops, sweep failures, publish-deny bursts, failed bot releases, certificate trouble)
- [ ] later: ops controls on this host (restart buttons) — allowlisted dumpd endpoints, authorised from the
Authentik headers, every invocation logged as a logfmt event. Never a page with Docker access.
### Phase 14 — push-to-deploy (built; OFF — `DEPLOY_AUTO=0`, CI workflow in draft-workflows/)
- server folder = git clone (`switchToClone.sh`); generated files gitignored and preserved