Phase 13 build: logs, metrics, alerting and a deadman on a diag. host

Six containers, all loopback-published, reachable only through the gated diag. host:
  victorialogs      30d retention, -http.pathPrefix=/logs so Caddy passes the prefix through unchanged
  vector            docker_logs + journald -> VictoriaLogs (elasticsearch bulk, the documented path)
  victoriametrics   90d, scrapes the four taps that were already live plus the new services
  vmalert-metrics   11 metric rules: disk, load, memory, RAID, restart loops, a core container gone
  vmalert-logs      5 LogsQL rules: sweep failures, publish-deny bursts, failed bot deploys, cert trouble
  alertmanager      groups and inhibits, posts to Discord, webhook read from a 0600 file not the config

The deadman (bin/vanguard-deadman.sh, host timer, 5 min) is the part that does not trust any of the above:
curl only, it checks each component answers, that the always-firing Watchdog alert is still arriving through
vmalert -> Alertmanager -> dumpd /heartbeat, and the disks. It reports on change and announces recovery once.
Verified here: failures listed, silence when nothing changes, disk threshold, recovery fired once.

Collection uses the Docker socket (Phase 13 option A, chosen explicitly). That is root-equivalent on this host
and is recorded as such in vector.yaml with the two upgrade paths.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-11-03 15:45:58 -06:00
parent 09882833a0
commit 8ae43905dc
13 changed files with 695 additions and 63 deletions

View File

@@ -345,10 +345,135 @@ services:
privileged: true
restart: unless-stopped
# ================================================================ observability (Phase 13)
# Logs: Vector -> VictoriaLogs. Metrics: VictoriaMetrics scrapes the four taps.
# Alerting: a vmalert per datasource -> Alertmanager -> Discord. All loopback; published via diag.HOST.
# The deadman (bin/vanguard-deadman.sh, a host timer) watches THIS stack, because nothing in it can.
victorialogs:
logging: *vglog
labels: { vanguard.service: victorialogs, vanguard.category: infra }
image: docker.io/victoriametrics/victoria-logs:latest
container_name: victorialogs
user: "1000:1000"
command:
- -storageDataPath=/data
- -retentionPeriod=30d
- -httpListenAddr=:9428
- -http.pathPrefix=/logs # its own UI then emits /logs/... so Caddy must NOT strip the prefix
ports:
- "127.0.0.1:9428:9428"
volumes:
- /srv/appdata/victorialogs:/data
restart: unless-stopped
vector:
logging: *vglog
labels: { vanguard.service: vector, vanguard.category: infra }
image: docker.io/timberio/vector:latest-debian
container_name: vector
environment:
VECTOR_CONFIG: /etc/vector/vector.yaml
volumes:
- ./observability/vector.yaml:/etc/vector/vector.yaml:ro
- /var/run/docker.sock:/var/run/docker.sock # Phase 13 decision "A": container logs need the Docker
# API, so this is root-equivalent on the host. Upgrade
# paths: journald log driver, or a read-only socket proxy.
- /var/log/journal:/var/log/journal:ro # our own systemd units
- /etc/machine-id:/etc/machine-id:ro # journald needs it to find the right journal
- vector_data:/var/lib/vector
depends_on:
- victorialogs
restart: unless-stopped
victoriametrics:
logging: *vglog
labels: { vanguard.service: victoriametrics, vanguard.category: infra }
image: docker.io/victoriametrics/victoria-metrics:latest
container_name: victoriametrics
user: "1000:1000"
command:
- -storageDataPath=/data
- -retentionPeriod=90d
- -httpListenAddr=:8428
- -promscrape.config=/etc/vm/scrape.yml
- -promscrape.config.strictParse=false
ports:
- "127.0.0.1:8428:8428"
volumes:
- /srv/appdata/victoriametrics:/data
- ./observability/scrape.yml:/etc/vm/scrape.yml:ro
extra_hosts:
- "host.docker.internal:host-gateway" # the four taps bind the host's loopback
restart: unless-stopped
vmalert-metrics:
logging: *vglog
labels: { vanguard.service: vmalert-metrics, vanguard.category: infra }
image: docker.io/victoriametrics/vmalert:latest
container_name: vmalert-metrics
command:
- -datasource.url=http://victoriametrics:8428
- -notifier.url=http://alertmanager:9093
- -remoteWrite.url=http://victoriametrics:8428
- -rule=/etc/alerts/alerts-metrics.yml
- -httpListenAddr=:8880
- -evaluationInterval=1m
ports:
- "127.0.0.1:8880:8880"
volumes:
- ./observability/alerts-metrics.yml:/etc/alerts/alerts-metrics.yml:ro
depends_on:
- victoriametrics
- alertmanager
restart: unless-stopped
vmalert-logs:
logging: *vglog
labels: { vanguard.service: vmalert-logs, vanguard.category: infra }
image: docker.io/victoriametrics/vmalert:latest
container_name: vmalert-logs
# A vmalert instance has one datasource, so log rules need their own: this one queries VictoriaLogs'
# LogsQL stats endpoints, which answer in the Prometheus format vmalert expects.
command:
- -datasource.url=http://victorialogs:9428/logs
- -notifier.url=http://alertmanager:9093
- -remoteWrite.url=http://victoriametrics:8428
- -rule=/etc/alerts/alerts-logs.yml
- -httpListenAddr=:8881
- -evaluationInterval=2m
ports:
- "127.0.0.1:8881:8881"
volumes:
- ./observability/alerts-logs.yml:/etc/alerts/alerts-logs.yml:ro
depends_on:
- victorialogs
- alertmanager
restart: unless-stopped
alertmanager:
logging: *vglog
labels: { vanguard.service: alertmanager, vanguard.category: infra }
image: docker.io/prom/alertmanager:latest
container_name: alertmanager
command:
- --config.file=/etc/alertmanager/alertmanager.yml
- --storage.path=/data
- --web.listen-address=:9093
ports:
- "127.0.0.1:9093:9093"
volumes:
- ./observability/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
- /etc/vanguard/alert-webhook:/etc/vanguard/alert-webhook:ro # written by configure.py, mode 0600
- /srv/appdata/alertmanager:/data
extra_hosts:
- "host.docker.internal:host-gateway" # the Watchdog heartbeat goes to dumpd on the bridge port
restart: unless-stopped
networks:
botnet: # isolates discord-bot from the shared default network (see above)
volumes:
copyparty_hist:
vector_data:
caddy_data:
caddy_config: