Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
README.md
Observability #
Central observability node for devICT infrastructure: the Grafana LGTM stack (Loki, Mimir, Tempo + Grafana), with Grafana Alloy as the collector on every monitored host.
- Terraform provisions the droplet (
devict-observability) + cloud firewall. - Ansible installs Docker + renders one compose stack per role under
/opt/devict/<role>/, each wrapped in a systemd unit. - Caddy terminates TLS for
grafana.devict.org->grafana:3000.
Architecture #
- Alloy runs on every host (this one included) and pushes: host metrics (embedded node_exporter) + its own metrics -> Mimir, systemd journal + docker container logs -> Loki. Push model: monitored hosts need no inbound ports, only outbound to this node.
- Apps send OTLP traces directly to Tempo (:4317). Alloy is not in the trace path.
- Backends run unauthenticated (
auth_enabled: false). All telemetry crosses a WireGuard mesh (hub-and-spoke, this node is the hub,10.77.0.0/24; sharedwireguardrole in../shared/roles). The ingest ports are not opened in the cloud firewall at all — the tunnel is the only path in, and every peer is authenticated by key. Note: docker-published ports bypass ufw and wg0 traffic bypasses the cloud firewall (it only sees UDP 51820 on the NIC), so the mesh is the enforcement point. - Alerting is Grafana-managed, provisioned as code
(
roles/svc-grafana/templates/alerting-*.yaml.j2) via Grafana's embedded Alertmanager. If critical alerting ever needs to survive Grafana being down, the upgrade path is Prometheus-style rule files evaluated by Mimir's/Loki's ruler + a standalone Alertmanager. - Dashboards are provisioned as code — JSON committed under
roles/svc-grafana/files/dashboards/boards/, loaded read-only from theInfrastructurefolder. - Why WireGuard over VPC + firewall: VPC rules are IP-based,
unencrypted, region-locked, and DO-only. The mesh adds encryption in
transit and per-node key identity, and works for any future host
anywhere. Private keys live in 1Password (
WG_PRIVATE_KEY_*inenv-devict-infra), injected per host via each service dir's.env.local.op; public keys are committed in the inventories.
Prerequisites #
Create observability/.env.local.op (1Password-backed, not committed) with:
TF_VAR_do_token=op://Private/env-devict-infra/DIGITALOCEAN_TOKEN
TF_VAR_ssh_public_key=op://Private/env-devict-infra/SSH_PUBLIC_KEY
CADDY_EMAIL=op://Private/env-devict-infra/CADDY_EMAIL
GRAFANA_ADMIN_PASSWORD=op://Private/env-devict-infra/GRAFANA_ADMIN_PASSWORD
GRAFANA_ALERT_EMAIL=op://Private/env-devict-infra/GRAFANA_ALERT_EMAIL
WG_PRIVATE_KEY=op://Private/env-devict-infra/WG_PRIVATE_KEY_OBSERVABILITY
# Optional: SMTP for alert email delivery (alerts are UI-only without it)
# GRAFANA_SMTP_HOST=...
# GRAFANA_SMTP_USER=...
# GRAFANA_SMTP_PASSWORD=...
# GRAFANA_SMTP_FROM_ADDRESS=...
DIGITALOCEAN_TOKEN / SSH_PUBLIC_KEY are reused from the other services;
create the rest in 1Password. GRAFANA_HOSTNAME defaults to
grafana.devict.org.
DNS (out of band): grafana.devict.org -> droplet IP.
Usage #
Prefix commands with op run --env-file=.env.local.op -- to inject secrets:
just init # terraform init
just plan # terraform plan
just apply # terraform apply
just setup # ansible-playbook site.yaml (full setup)
just deploy # re-render service templates + restart (no base setup)
just ssh # ssh devict@<ip>
Adding monitoring to another host #
The wireguard and alloy roles live in ../shared/roles (on every
dir's roles_path). On the target host:
- Generate a keypair (
wg genkey), store the private key in 1Password, addWG_PRIVATE_KEY=op://...to that dir's.env.local.op. - Pick the next free
10.77.0.x/24address. Add the host as a peer in this dir'sinventory.yaml(hub side) and add the hub as a peer in the host's inventory (spoke side, withendpoint+persistent_keepalive). - Add the
wireguard+alloyroles to the host's playbook with:
alloy_docker_network: ""
alloy_loki_url: "http://10.77.0.1:3100/loki/api/v1/push"
alloy_mimir_url: "http://10.77.0.1:9009/api/v1/push"
Open questions / TODO #
- Alert destination is email for now; consider Slack/Discord webhook.
- Dashboards are provisioned as code (see below); new ones: add JSON under
roles/svc-grafana/files/dashboards/boards/, validate queries against Mimir/Loki, thenjust deploy. - Consider pinning image tags to exact versions once validated.
- Sizing:
s-1vcpu-2gbto start; bumpsizeif Mimir gets OOM-happy.