diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 0a49213..7df1c02 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -34,3 +34,22 @@ jobs: run: | docker run --rm -v "$PWD/config.alloy:/config.alloy:ro" \ grafana/alloy:v1.10.0 fmt /config.alloy > /dev/null + + - name: Validate Alloy config semantics (parse + load components) + # `fmt` is syntax-only; this catches bad component refs, wrong arg + # types, and unknown component types by actually loading the graph. + # Components log scrape errors against the dummy URLs but alloy + # itself stays running, so liveness after sleep == config valid. + run: | + docker run --rm -d --name alloy-test \ + -e ALLOY_HOSTNAME -e REMOTECFG_URL -e REMOTECFG_ID -e REMOTECFG_USER \ + -e PROM_URL -e PROM_USER -e LOKI_URL -e LOKI_USER -e GRAFANA_TOKEN \ + -v "$PWD/config.alloy:/etc/alloy/config.alloy:ro" \ + grafana/alloy:v1.10.0 \ + run --server.http.listen-addr=127.0.0.1:0 /etc/alloy/config.alloy + sleep 8 + if [ "$(docker inspect -f '{{.State.Running}}' alloy-test 2>/dev/null)" != "true" ]; then + docker logs alloy-test + exit 1 + fi + docker stop alloy-test >/dev/null diff --git a/README.md b/README.md index 30da07e..60207dc 100644 --- a/README.md +++ b/README.md @@ -45,20 +45,20 @@ Same compose file on every host — change `ALLOY_HOSTNAME` and `REMOTECFG_ID` p ## Security note -Runs as `privileged: true` (matching the upstream Grafana Cloud docker integration). This is required for cadvisor to read cgroups via `/sys` and for `/dev/kmsg` access. If you need least-privilege, see the upstream Alloy docker integration docs and tighten capabilities. +Runs `privileged: true` + `network_mode: host`, matching the upstream Grafana Cloud docker integration. `network_mode: host` is required so `prometheus.exporter.unix` reports the host's real network interfaces (eth0…) instead of the alloy container's veth pair. If you need least-privilege, see the upstream Alloy docker integration docs and tighten capabilities. ## Mounts | Mount | Why | |---|---| -| `/proc:/rootproc:ro` | node-exporter cpu/mem/load | +| `/proc:/rootproc:ro` | node-exporter cpu/mem/load (referenced via `procfs_path`) | | `/sys:/sys:ro` | node-exporter + cadvisor cgroups | -| `/:/rootfs:ro` | filesystem collector | -| `/dev/disk/:/dev/disk:ro` | diskstats device labels | -| `/var/run/docker.sock` | docker discovery + log streaming | +| `/:/rootfs:ro` | filesystem collector (referenced via `rootfs_path`) | +| `/dev/disk/:/dev/disk:ro` | node-exporter diskstats device labels | +| `/var/run/docker.sock` | `discovery.docker` + `loki.source.docker` | | `/var/lib/docker:ro` | cadvisor container metadata | | `/var/log/journal:ro` | `loki.source.journal` | -| `/dev/kmsg` (device) | cadvisor OOM detection | +| `/etc/machine-id:ro` | stable host id for the journal reader | | `alloy-data` (named volume) | WAL + remotecfg cache | ## License diff --git a/docker-compose.yml b/docker-compose.yml index 1c01834..19fac33 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,7 @@ services: restart: unless-stopped hostname: ${ALLOY_HOSTNAME:?required} privileged: true + network_mode: host # node_exporter netdev/netstat sees real host interfaces (eth0…) not veth environment: ALLOY_DEPLOY_MODE: docker REMOTECFG_URL: ${REMOTECFG_URL:?required} @@ -29,10 +30,6 @@ services: - /var/lib/docker/:/var/lib/docker:ro - /var/log/journal:/var/log/journal:ro - /etc/machine-id:/etc/machine-id:ro # stable host id for systemd journal reader - extra_hosts: - - 'host.docker.internal:host-gateway' - devices: - - /dev/kmsg configs: - { source: alloy_config, target: /etc/alloy/config.alloy } command: 'run --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy' @@ -151,6 +148,7 @@ configs: loki.source.journal "default" { max_age = "12h0m0s" + path = "/var/log/journal" forward_to = [loki.process.default.receiver] relabel_rules = loki.relabel.default.rules } @@ -201,7 +199,7 @@ configs: rule { source_labels = ["__name__"] - regex = "container_cpu_usage_seconds_total|container_fs_reads_total|container_fs_usage_bytes|container_fs_writes_total|container_last_seen|container_memory_usage_bytes|container_network_receive_bytes_total|container_network_receive_errors_total|container_network_receive_packets_dropped_total|container_network_transmit_bytes_total|container_network_transmit_errors_total|container_network_transmit_packets_dropped_total|container_spec_memory_reservation_limit_bytes|machine_memory_bytes|machine_scrape_error|up" + regex = "container_cpu_usage_seconds_total|container_fs_reads_total|container_fs_usage_bytes|container_fs_writes_total|container_last_seen|container_memory_usage_bytes|container_memory_working_set_bytes|container_network_receive_bytes_total|container_network_receive_errors_total|container_network_receive_packets_dropped_total|container_network_transmit_bytes_total|container_network_transmit_errors_total|container_network_transmit_packets_dropped_total|container_spec_memory_reservation_limit_bytes|machine_memory_bytes|machine_scrape_error|up" action = "keep" } }