diff --git a/README.md b/README.md new file mode 100644 index 0000000..2e1639c --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# alloy-docker-compose + +One-file [Grafana Alloy](https://grafana.com/docs/alloy/latest/) setup that ships host and container telemetry to Grafana Cloud — sized for the Free tier. + +- **Single container, single file** — no `remotecfg`, no fleet-management, no split `alloy` + `alloy-docker` deployments. +- **Env-driven** — six shell variables, no `.env` file. +- **Least-privilege** — `cap_drop: [ALL]`, no `privileged`, no `pid: host`, UI not published. +- **Free-tier safe** — `keep`-filter on metric names holds a typical 15-container host to ~1,600 of the 10,000 active-series cap. + +## What it collects + +| Source | Component | Notes | +|---|---|---| +| Host metrics | `prometheus.exporter.unix` | CPU, memory (+MemFree/Available), load, disk I/O + IOPS + saturation, filesystem (size/avail/readonly/inodes), network (bytes/errs/drop), uname, boot time | +| Container metrics | `prometheus.exporter.cadvisor` | CPU, memory (usage + working_set), fs usage/limit, network, `last_seen` | +| Container logs | `loki.source.docker` | all running containers, labeled with `container`, `stream`, `instance` | +| System logs | `loki.source.journal` | systemd journal with `unit` + `level` labels | + +60-second scrape interval. Alloy's own logs are *not* excluded — useful for debugging the agent itself. + +## Quick start + +```bash +export ALLOY_HOSTNAME= # e.g. miti-jp +export PROM_URL=https://prometheus-prod-XX-.grafana.net/api/prom/push +export PROM_USER= +export LOKI_URL=https://logs-prod-XXX.grafana.net/loki/api/v1/push +export LOKI_USER= +export GRAFANA_TOKEN=glc_... # Cloud Access Policy, scopes: metrics:write + logs:write + +docker compose up -d +``` + +Find the `PROM_*` / `LOKI_*` values under Grafana Cloud → your stack → **Details** on the Prometheus and Loki data sources. Create one Cloud Access Policy with both `metrics:write` and `logs:write` scopes, then generate a token from it and reuse for both writes. + +Any unset required variable makes `docker compose up` fail fast with a clear error. + +## Multi-host + +The same file works on every host — just export a different `ALLOY_HOSTNAME`. Each instance independently ships to the same Grafana Cloud stack; filter/compare in Grafana with `instance=~"..."`. + +## UI access + +The UI (port `12345`) is **not** published to the host. To reach it: + +```bash +# from another container on the same Docker network: +curl http://alloy:12345/graph + +# or expose temporarily for local debugging: +docker compose exec alloy wget -qO- http://localhost:12345/-/ready +``` + +## Security posture + +- Runs as `user: "0:0"` (root) inside the container — required to read `/var/run/docker.sock` (0660 root:docker) and `/var/log/journal/*` (0640 root:systemd-journal). +- All Linux capabilities dropped (`cap_drop: [ALL]`). Root without caps still reads files (DAC bypass) but cannot do privileged syscalls. +- `no-new-privileges:true` on seccomp. +- All host mounts are read-only. Only the `alloy-data` named volume is writable (WAL/positions). +- The UI listens on `0.0.0.0:12345` *inside* the container network only; no `ports:` mapping to the host. + +## Free-tier cardinality budget (Grafana Cloud) + +- **Metrics**: 10,000 active series hard cap. This config emits ~1,500–1,800 per host (cAdvisor dominates at ~150/container). Two hosts ≈ 32% utilisation. +- **Logs**: 50 GB/month ingestion. `batch_wait=5s`, `batch_size=1MiB` minimise push overhead. Typical home server stays well under. +- **Retention**: 14 days (set by the Free plan, not configurable here). + +If you add a third host or scrape extra applications, re-check cardinality before declaring victory. + +## Troubleshooting + +- **`hostname: required` error from Compose** — you didn't `export ALLOY_HOSTNAME` in the current shell. +- **No system logs in Loki** — host has no persistent journal. Enable with `sudo mkdir -p /var/log/journal && sudo systemd-tmpfiles --create --prefix /var/log/journal && sudo systemctl restart systemd-journald`. +- **Empty panels on "Node Exporter Full" dashboard** — the `keep`-filter trims rarely-used metrics (Shmem, Slab, Active/Inactive memory breakdowns). Add names to the regex in `prometheus.relabel "filter"` to restore specific panels. +- **Docker logs missing for recently-started containers** — discovery refreshes every 5s; wait a few seconds. + +## License + +MIT — do whatever you want.