From 1cf54fb254a59cc7938047d76234138b44ba114f Mon Sep 17 00:00:00 2001 From: Eric Gullickson <16152721+ericgullickson@users.noreply.github.com> Date: Wed, 4 Feb 2026 20:16:53 -0600 Subject: [PATCH 1/3] feat: add Promtail, Loki, and Grafana log aggregation stack (refs #86) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Promtail for Docker log scraping with container discovery - Add Loki for log storage with 30-day retention - Add Grafana with Loki datasource auto-provisioned - Add IP whitelist middleware restricting Grafana to RFC1918 ranges - Container count: 6 → 9 Co-Authored-By: Claude Opus 4.5 --- config/grafana/datasources/loki.yml | 9 ++++ config/loki/config.yml | 32 +++++++++++ config/promtail/config.yml | 21 ++++++++ config/traefik/dynamic/grafana.yml | 8 +++ docker-compose.yml | 82 +++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 config/grafana/datasources/loki.yml create mode 100644 config/loki/config.yml create mode 100644 config/promtail/config.yml create mode 100644 config/traefik/dynamic/grafana.yml diff --git a/config/grafana/datasources/loki.yml b/config/grafana/datasources/loki.yml new file mode 100644 index 0000000..7154896 --- /dev/null +++ b/config/grafana/datasources/loki.yml @@ -0,0 +1,9 @@ +apiVersion: 1 + +datasources: + - name: Loki + type: loki + access: proxy + url: http://mvp-loki:3100 + isDefault: true + editable: false diff --git a/config/loki/config.yml b/config/loki/config.yml new file mode 100644 index 0000000..4e6c22e --- /dev/null +++ b/config/loki/config.yml @@ -0,0 +1,32 @@ +auth_enabled: false + +server: + http_listen_port: 3100 + +ingester: + lifecycler: + ring: + kvstore: + store: inmemory + replication_factor: 1 + +schema_config: + configs: + - from: 2020-01-01 + store: boltdb-shipper + object_store: filesystem + schema: v11 + index: + prefix: index_ + period: 24h + +storage_config: + boltdb_shipper: + active_index_directory: /loki/boltdb-shipper-active + cache_location: /loki/boltdb-shipper-cache + shared_store: filesystem + filesystem: + directory: /loki/chunks + +limits_config: + retention_period: 720h # 30 days diff --git a/config/promtail/config.yml b/config/promtail/config.yml new file mode 100644 index 0000000..48bf9ab --- /dev/null +++ b/config/promtail/config.yml @@ -0,0 +1,21 @@ +server: + http_listen_port: 9080 + grpc_listen_port: 0 + +positions: + filename: /tmp/positions.yaml + +clients: + - url: http://mvp-loki:3100/loki/api/v1/push + +scrape_configs: + - job_name: containers + docker_sd_configs: + - host: unix:///var/run/docker.sock + refresh_interval: 5s + relabel_configs: + - source_labels: ['__meta_docker_container_name'] + regex: '/(.*)' + target_label: 'container' + - source_labels: ['__meta_docker_container_label_com_docker_compose_service'] + target_label: 'service' diff --git a/config/traefik/dynamic/grafana.yml b/config/traefik/dynamic/grafana.yml new file mode 100644 index 0000000..6d296d4 --- /dev/null +++ b/config/traefik/dynamic/grafana.yml @@ -0,0 +1,8 @@ +http: + middlewares: + grafana-ipwhitelist: + ipWhiteList: + sourceRange: + - "10.0.0.0/8" + - "172.16.0.0/12" + - "192.168.0.0/16" diff --git a/docker-compose.yml b/docker-compose.yml index 9be6de6..8a796a8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -264,6 +264,84 @@ services: max-size: "10m" max-file: "3" + # Log Aggregation - Loki + mvp-loki: + image: grafana/loki:2.9.0 + container_name: mvp-loki + restart: unless-stopped + volumes: + - ./config/loki/config.yml:/etc/loki/config.yml:ro + - mvp_loki_data:/loki + command: -config.file=/etc/loki/config.yml + networks: + - backend + healthcheck: + test: ["CMD-SHELL", "wget -q --spider http://localhost:3100/ready || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" + + # Log Aggregation - Promtail + mvp-promtail: + image: grafana/promtail:2.9.0 + container_name: mvp-promtail + restart: unless-stopped + volumes: + - ./config/promtail/config.yml:/etc/promtail/config.yml:ro + - /var/lib/docker/containers:/var/lib/docker/containers:ro + - /var/run/docker.sock:/var/run/docker.sock:ro + command: -config.file=/etc/promtail/config.yml + networks: + - backend + depends_on: + - mvp-loki + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" + + # Log Aggregation - Grafana + mvp-grafana: + image: grafana/grafana:10.0.0 + container_name: mvp-grafana + restart: unless-stopped + environment: + GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-admin} + GF_USERS_ALLOW_SIGN_UP: "false" + volumes: + - ./config/grafana/datasources:/etc/grafana/provisioning/datasources:ro + - mvp_grafana_data:/var/lib/grafana + networks: + - backend + - frontend + depends_on: + - mvp-loki + healthcheck: + test: ["CMD-SHELL", "wget -q --spider http://localhost:3000/api/health || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + labels: + - "traefik.enable=true" + - "traefik.docker.network=motovaultpro_frontend" + - "traefik.http.routers.grafana.rule=Host(`logs.motovaultpro.com`)" + - "traefik.http.routers.grafana.entrypoints=websecure" + - "traefik.http.routers.grafana.tls=true" + - "traefik.http.routers.grafana.tls.certresolver=letsencrypt" + - "traefik.http.routers.grafana.middlewares=grafana-ipwhitelist@file" + - "traefik.http.services.grafana.loadbalancer.server.port=3000" + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" + # Network Definition networks: frontend: @@ -302,3 +380,7 @@ volumes: name: mvp_postgres_data mvp_redis_data: name: mvp_redis_data + mvp_loki_data: + name: mvp_loki_data + mvp_grafana_data: + name: mvp_grafana_data -- 2.49.1 From e83385d7291a8c8c4f476e1557c001759a9c578c Mon Sep 17 00:00:00 2001 From: Eric Gullickson <16152721+ericgullickson@users.noreply.github.com> Date: Wed, 4 Feb 2026 20:19:22 -0600 Subject: [PATCH 2/3] chore: use mirrored registry for logging stack images (refs #86) - Update Loki, Promtail, Grafana to use REGISTRY_MIRRORS - Add grafana/loki, grafana/promtail, grafana/grafana to mirror script Co-Authored-By: Claude Opus 4.5 --- docker-compose.yml | 6 +++--- scripts/ci/mirror-base-images.sh | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 8a796a8..f7debc2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -266,7 +266,7 @@ services: # Log Aggregation - Loki mvp-loki: - image: grafana/loki:2.9.0 + image: ${REGISTRY_MIRRORS:-git.motovaultpro.com/egullickson/mirrors}/loki:2.9.0 container_name: mvp-loki restart: unless-stopped volumes: @@ -288,7 +288,7 @@ services: # Log Aggregation - Promtail mvp-promtail: - image: grafana/promtail:2.9.0 + image: ${REGISTRY_MIRRORS:-git.motovaultpro.com/egullickson/mirrors}/promtail:2.9.0 container_name: mvp-promtail restart: unless-stopped volumes: @@ -308,7 +308,7 @@ services: # Log Aggregation - Grafana mvp-grafana: - image: grafana/grafana:10.0.0 + image: ${REGISTRY_MIRRORS:-git.motovaultpro.com/egullickson/mirrors}/grafana:10.0.0 container_name: mvp-grafana restart: unless-stopped environment: diff --git a/scripts/ci/mirror-base-images.sh b/scripts/ci/mirror-base-images.sh index 50fd486..2e26d50 100755 --- a/scripts/ci/mirror-base-images.sh +++ b/scripts/ci/mirror-base-images.sh @@ -17,6 +17,9 @@ IMAGES=( "python:3.11-slim" "docker:24.0" "docker:24.0-dind" + "grafana/loki:2.9.0" + "grafana/promtail:2.9.0" + "grafana/grafana:10.0.0" ) echo "========================================" -- 2.49.1 From 9aa1ad954fc1ea81a6912d4b388f4b574b722f82 Mon Sep 17 00:00:00 2001 From: Eric Gullickson <16152721+ericgullickson@users.noreply.github.com> Date: Wed, 4 Feb 2026 20:40:23 -0600 Subject: [PATCH 3/3] fix: use correct grafana/ namespace in mirrored image paths (refs #86) Co-Authored-By: Claude Opus 4.5 --- docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index f7debc2..814a76f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -266,7 +266,7 @@ services: # Log Aggregation - Loki mvp-loki: - image: ${REGISTRY_MIRRORS:-git.motovaultpro.com/egullickson/mirrors}/loki:2.9.0 + image: ${REGISTRY_MIRRORS:-git.motovaultpro.com/egullickson/mirrors}/grafana/loki:2.9.0 container_name: mvp-loki restart: unless-stopped volumes: @@ -288,7 +288,7 @@ services: # Log Aggregation - Promtail mvp-promtail: - image: ${REGISTRY_MIRRORS:-git.motovaultpro.com/egullickson/mirrors}/promtail:2.9.0 + image: ${REGISTRY_MIRRORS:-git.motovaultpro.com/egullickson/mirrors}/grafana/promtail:2.9.0 container_name: mvp-promtail restart: unless-stopped volumes: @@ -308,7 +308,7 @@ services: # Log Aggregation - Grafana mvp-grafana: - image: ${REGISTRY_MIRRORS:-git.motovaultpro.com/egullickson/mirrors}/grafana:10.0.0 + image: ${REGISTRY_MIRRORS:-git.motovaultpro.com/egullickson/mirrors}/grafana/grafana:10.0.0 container_name: mvp-grafana restart: unless-stopped environment: -- 2.49.1