From 81466aebacc5d43280332ff4996ef24d1f2fce63 Mon Sep 17 00:00:00 2001 From: Benji Shohet <97973081+benjisho@users.noreply.github.com> Date: Wed, 16 Jul 2025 03:21:23 +0000 Subject: [PATCH 1/5] Add Docker Compose setup for NGINX Prometheus Exporter - Add docker-compose.yml with nginx-exporter, nginx, app, prometheus, and grafana services - Add docker-compose.test.yml for minimal testing setup - Configure NGINX with stub_status endpoint on port 8081 - Add sample web application with custom HTML content - Configure Prometheus to scrape nginx-exporter metrics - Add Grafana with pre-configured datasources and dashboards - Set up proper networking and service dependencies - Use examples/docker-compose/ directory structure for all configuration files --- docker-compose.test.yml | 50 ++ docker-compose.yml | 93 +++ examples/docker-compose/app/html/index.html | 190 ++++++ examples/docker-compose/app/nginx.conf | 37 ++ .../grafana/dashboards/nginx-dashboard.json | 567 ++++++++++++++++++ .../provisioning/dashboards/dashboards.yml | 12 + .../provisioning/datasources/prometheus.yml | 9 + examples/docker-compose/nginx/nginx.conf | 55 ++ .../docker-compose/prometheus/prometheus.yml | 28 + 9 files changed, 1041 insertions(+) create mode 100644 docker-compose.test.yml create mode 100644 docker-compose.yml create mode 100644 examples/docker-compose/app/html/index.html create mode 100644 examples/docker-compose/app/nginx.conf create mode 100644 examples/docker-compose/grafana/dashboards/nginx-dashboard.json create mode 100644 examples/docker-compose/grafana/provisioning/dashboards/dashboards.yml create mode 100644 examples/docker-compose/grafana/provisioning/datasources/prometheus.yml create mode 100644 examples/docker-compose/nginx/nginx.conf create mode 100644 examples/docker-compose/prometheus/prometheus.yml diff --git a/docker-compose.test.yml b/docker-compose.test.yml new file mode 100644 index 00000000..568ee59b --- /dev/null +++ b/docker-compose.test.yml @@ -0,0 +1,50 @@ +version: '3.8' + +services: + # Sample web application + app: + image: nginx:alpine + container_name: sample-app + volumes: + - ./docker/app/nginx.conf:/etc/nginx/nginx.conf + - ./docker/app/html:/usr/share/nginx/html + ports: + - "8080:80" + networks: + - nginx-monitoring + restart: unless-stopped + + # NGINX with stub_status enabled + nginx: + image: nginx:alpine + container_name: nginx-server + depends_on: + - app + volumes: + - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf + - ./docker/nginx/conf.d:/etc/nginx/conf.d + ports: + - "80:80" + - "8081:8081" # stub_status port + networks: + - nginx-monitoring + restart: unless-stopped + + # NGINX Prometheus Exporter + nginx-exporter: + image: nginx/nginx-prometheus-exporter:latest + container_name: nginx-prometheus-exporter + depends_on: + - nginx + command: + - --nginx.scrape-uri=http://nginx:8081/stub_status + - --web.listen-address=0.0.0.0:9113 + ports: + - "9113:9113" + networks: + - nginx-monitoring + restart: unless-stopped + +networks: + nginx-monitoring: + driver: bridge diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..6028b038 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,93 @@ +services: + # Sample web application + app: + image: nginx:alpine + container_name: sample-app + volumes: + - ./examples/docker-compose/app/nginx.conf:/etc/nginx/nginx.conf + - ./examples/docker-compose/app/html:/usr/share/nginx/html + ports: + - "8080:80" + networks: + - nginx-monitoring + restart: unless-stopped + + # NGINX with stub_status enabled + nginx: + image: nginx:alpine + container_name: nginx-server + depends_on: + - app + volumes: + - ./examples/docker-compose/nginx/nginx.conf:/etc/nginx/nginx.conf + - ./examples/docker-compose/nginx/conf.d:/etc/nginx/conf.d + ports: + - "80:80" + - "8081:8081" # stub_status port + networks: + - nginx-monitoring + restart: unless-stopped + + # NGINX Prometheus Exporter + nginx-exporter: + image: nginx/nginx-prometheus-exporter:latest + container_name: nginx-prometheus-exporter + depends_on: + - nginx + command: + - --nginx.scrape-uri=http://nginx:8081/stub_status + - --web.listen-address=0.0.0.0:9113 + ports: + - "9113:9113" + networks: + - nginx-monitoring + restart: unless-stopped + + # Prometheus + prometheus: + image: prom/prometheus:latest + container_name: prometheus + depends_on: + - nginx-exporter + volumes: + - ./examples/docker-compose/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus_data:/prometheus + ports: + - "9090:9090" + networks: + - nginx-monitoring + restart: unless-stopped + command: + - --config.file=/etc/prometheus/prometheus.yml + - --storage.tsdb.path=/prometheus + - --web.console.libraries=/etc/prometheus/console_libraries + - --web.console.templates=/etc/prometheus/consoles + - --storage.tsdb.retention.time=200h + - --web.enable-lifecycle + + # Grafana + grafana: + image: grafana/grafana:latest + container_name: grafana + depends_on: + - prometheus + volumes: + - grafana_data:/var/lib/grafana + - ./examples/docker-compose/grafana/provisioning:/etc/grafana/provisioning + - ./examples/docker-compose/grafana/dashboards:/var/lib/grafana/dashboards + ports: + - "3000:3000" + networks: + - nginx-monitoring + restart: unless-stopped + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin123 + - GF_USERS_ALLOW_SIGN_UP=false + +volumes: + prometheus_data: + grafana_data: + +networks: + nginx-monitoring: + driver: bridge diff --git a/examples/docker-compose/app/html/index.html b/examples/docker-compose/app/html/index.html new file mode 100644 index 00000000..c0380fd7 --- /dev/null +++ b/examples/docker-compose/app/html/index.html @@ -0,0 +1,190 @@ + + + + + + NGINX Prometheus Exporter Demo + + + +
+

🔧 NGINX Prometheus Exporter Demo

+

This is a demo environment showcasing NGINX monitoring with Prometheus and Grafana

+
+ +
+
+

📊 Prometheus

+

Time-series database collecting metrics from NGINX

+ Open Prometheus → +
+ +
+

📈 Grafana

+

Visualization dashboard for NGINX metrics

+

Login: admin / admin123

+ Open Grafana → +
+ +
+

🔍 NGINX Exporter

+

Prometheus exporter for NGINX metrics

+ View Metrics → +
+ +
+

⚙️ NGINX Status

+

Raw NGINX stub_status information

+ View Status → +
+
+ +
+

🚀 Load Testing

+

Generate some traffic to see metrics in action:

+ + + +
+
+ + + + + + diff --git a/examples/docker-compose/app/nginx.conf b/examples/docker-compose/app/nginx.conf new file mode 100644 index 00000000..3fa3e06d --- /dev/null +++ b/examples/docker-compose/app/nginx.conf @@ -0,0 +1,37 @@ +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + error_log /var/log/nginx/error.log; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ =404; + } + + location /api/ { + return 200 '{"status": "ok", "message": "API endpoint"}'; + add_header Content-Type application/json; + } + } +} diff --git a/examples/docker-compose/grafana/dashboards/nginx-dashboard.json b/examples/docker-compose/grafana/dashboards/nginx-dashboard.json new file mode 100644 index 00000000..4adfb200 --- /dev/null +++ b/examples/docker-compose/grafana/dashboards/nginx-dashboard.json @@ -0,0 +1,567 @@ +{ + "__inputs": [ + { + "description": "", + "label": "Prometheus", + "name": "DS_PROMETHEUS", + "pluginId": "prometheus", + "pluginName": "Prometheus", + "type": "datasource" + } + ], + "__requires": [ + { + "id": "grafana", + "name": "Grafana", + "type": "grafana", + "version": "5.0.0" + }, + { + "id": "graph", + "name": "Graph", + "type": "panel", + "version": "" + }, + { + "id": "prometheus", + "name": "Prometheus", + "type": "datasource", + "version": "1.0.0" + }, + { + "id": "singlestat", + "name": "Singlestat", + "type": "panel", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Official dashboard for NGINX Prometheus exporter", + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "iteration": 1562682051068, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": "${DS_PROMETHEUS}", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 4, + "panels": [], + "title": "Status", + "type": "row" + }, + { + "cacheTimeout": null, + "colorBackground": true, + "colorPostfix": false, + "colorPrefix": false, + "colorValue": false, + "colors": [ + "#E02F44", + "#FF9830", + "#299c46" + ], + "datasource": "${DS_PROMETHEUS}", + "decimals": null, + "description": "", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 3, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 8, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "repeat": "instance", + "repeatDirection": "h", + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "nginx_up{instance=~\"$instance\"}", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "1,1", + "timeFrom": null, + "timeShift": null, + "title": "NGINX Status for $instance", + "type": "singlestat", + "valueFontSize": "100%", + "valueMaps": [ + { + "op": "=", + "text": "Down", + "value": "0" + }, + { + "op": "=", + "text": "Up", + "value": "1" + } + ], + "valueName": "current" + }, + { + "collapsed": false, + "datasource": "${DS_PROMETHEUS}", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 4 + }, + "id": 6, + "panels": [], + "title": "Metrics", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "decimals": null, + "description": "", + "fill": 1, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 10, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "hideEmpty": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "irate(nginx_connections_accepted{instance=~\"$instance\"}[5m])", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "legendFormat": "{{instance}} accepted", + "refId": "A" + }, + { + "expr": "irate(nginx_connections_handled{instance=~\"$instance\"}[5m])", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "legendFormat": "{{instance}} handled", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Processed connections", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 1, + "format": "short", + "label": "Connections (rate)", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "decimals": 0, + "fill": 1, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 5 + }, + "id": 12, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "nginx_connections_active{instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{instance}} active", + "refId": "A" + }, + { + "expr": "nginx_connections_reading{instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{instance}} reading", + "refId": "B" + }, + { + "expr": "nginx_connections_waiting{instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{instance}} waiting", + "refId": "C" + }, + { + "expr": "nginx_connections_writing{instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{instance}} writing", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Active Connections", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 0, + "format": "short", + "label": "Connections", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fill": 1, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 15 + }, + "id": 15, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "irate(nginx_http_requests_total{instance=~\"$instance\"}[5m])", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{instance}} total requests", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Total requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 18, + "style": "dark", + "tags": [ + "nginx", + "prometheus", + "nginx prometheus exporter" + ], + "templating": { + "list": [ + { + "current": { + "selected": false, + "tags": [], + "text": "default", + "value": "default" + }, + "hide": 0, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "label_values(nginx_up, instance)", + "hide": 0, + "includeAll": true, + "label": "", + "multi": true, + "name": "instance", + "options": [], + "query": "label_values(nginx_up, instance)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "", + "title": "NGINX", + "uid": "MsjffzSZz", + "version": 1 +} diff --git a/examples/docker-compose/grafana/provisioning/dashboards/dashboards.yml b/examples/docker-compose/grafana/provisioning/dashboards/dashboards.yml new file mode 100644 index 00000000..7435f09d --- /dev/null +++ b/examples/docker-compose/grafana/provisioning/dashboards/dashboards.yml @@ -0,0 +1,12 @@ +apiVersion: 1 + +providers: + - name: 'default' + orgId: 1 + folder: '' + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /var/lib/grafana/dashboards diff --git a/examples/docker-compose/grafana/provisioning/datasources/prometheus.yml b/examples/docker-compose/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..1a57b69c --- /dev/null +++ b/examples/docker-compose/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,9 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: true diff --git a/examples/docker-compose/nginx/nginx.conf b/examples/docker-compose/nginx/nginx.conf new file mode 100644 index 00000000..78791539 --- /dev/null +++ b/examples/docker-compose/nginx/nginx.conf @@ -0,0 +1,55 @@ +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + error_log /var/log/nginx/error.log; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + # Include additional configuration files + include /etc/nginx/conf.d/*.conf; + + # Main server + server { + listen 80; + server_name localhost; + + # Proxy to sample app + location / { + proxy_pass http://app:80; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } + + # Status server for metrics + server { + listen 8081; + server_name localhost; + + location /stub_status { + stub_status; + access_log off; + allow all; + } + + location / { + return 404; + } + } +} diff --git a/examples/docker-compose/prometheus/prometheus.yml b/examples/docker-compose/prometheus/prometheus.yml new file mode 100644 index 00000000..e9b2aca2 --- /dev/null +++ b/examples/docker-compose/prometheus/prometheus.yml @@ -0,0 +1,28 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +rule_files: + # - "first_rules.yml" + # - "second_rules.yml" + +scrape_configs: + # The job name is added as a label `job=` to any timeseries scraped from this config. + - job_name: "prometheus" + static_configs: + - targets: ["localhost:9090"] + + # NGINX Prometheus Exporter + - job_name: "nginx-exporter" + static_configs: + - targets: ["nginx-exporter:9113"] + scrape_interval: 5s + metrics_path: /metrics + scrape_timeout: 5s + + # Optional: Direct monitoring of services + - job_name: "nginx-status" + static_configs: + - targets: ["nginx:8081"] + metrics_path: /stub_status + scrape_interval: 10s From 3d7ff39972ef2d220b58333deca7eb5a9f8e3331 Mon Sep 17 00:00:00 2001 From: Benji Shohet <97973081+benjisho@users.noreply.github.com> Date: Wed, 16 Jul 2025 03:21:38 +0000 Subject: [PATCH 2/5] Add Docker Compose documentation - Add comprehensive Docker Compose documentation in examples/docker-compose/README.md - Include architecture overview, quick start guide, and service descriptions - Add testing instructions and troubleshooting section - Update main README.md to reference Docker Compose example - Focus quick start on nginx-exporter only for existing NGINX setups - Provide separate instructions for complete monitoring stack - Add configuration file references and usage examples --- README.md | 21 +++++ examples/docker-compose/README.md | 148 ++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 examples/docker-compose/README.md diff --git a/README.md b/README.md index 2d868186..422ab37f 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,27 @@ To start the exporter we use the [docker run](https://docs.docker.com/engine/ref follow the example in [examples/systemd](./examples/systemd/README.md). Alternatively, you can run the exporter in a Docker container. +### Docker Compose Setup + +For a complete monitoring stack including NGINX, NGINX Prometheus Exporter, Prometheus, and Grafana, see the [Docker Compose example](./examples/docker-compose/README.md). + +The Docker Compose setup provides: +- NGINX server with stub_status enabled +- NGINX Prometheus Exporter for metrics collection +- Prometheus for time-series storage +- Grafana for visualization with pre-configured dashboards +- Sample web application for testing + +Quick start (nginx-exporter only): +```console +docker-compose up -d nginx-exporter +``` + +For a complete monitoring stack with all services: +```console +docker-compose up -d +``` + ## Usage ### Command-line Arguments diff --git a/examples/docker-compose/README.md b/examples/docker-compose/README.md new file mode 100644 index 00000000..17206d09 --- /dev/null +++ b/examples/docker-compose/README.md @@ -0,0 +1,148 @@ +# Docker Compose Setup for NGINX Prometheus Exporter + +This Docker Compose configuration provides a complete monitoring stack for NGINX using Prometheus and Grafana. + +## Table of Contents + +- [Architecture](#architecture) +- [Quick Start](#quick-start) +- [Services](#services) + - [nginx-exporter](#nginx-exporter) + - [nginx (Main Server)](#nginx-main-server) + - [app (Sample Application)](#app-sample-application) + - [prometheus](#prometheus) + - [grafana](#grafana) +- [Configuration Files](#configuration-files) +- [Testing](#testing) +- [Stopping](#stopping) +- [Troubleshooting](#troubleshooting) +- [Network](#network) +- [Volumes](#volumes) + +## Architecture + +The setup includes: +- **nginx**: Main NGINX server with stub_status enabled (for demo purposes) +- **app**: Sample web application served by NGINX (for demo purposes) +- **nginx-exporter**: NGINX Prometheus Exporter that scrapes metrics from NGINX +- **prometheus**: Prometheus time-series database for metrics collection +- **grafana**: Grafana dashboard for visualization + +**Note**: If you have an existing NGINX setup, you only need to run the `nginx-exporter` service. Make sure your NGINX has stub_status enabled and is accessible at the configured scrape URI. + +## Quick Start + +1. **Navigate to the project root directory**: + ```bash + cd /path/to/nginx-prometheus-exporter + ``` + +2. **For existing NGINX setup - Start only the nginx-exporter**: + ```bash + docker-compose up -d nginx-exporter + ``` + This assumes you already have NGINX running with stub_status enabled at `http://nginx:8081/stub_status`. + +3. **For complete demo setup - Start the full monitoring stack**: + ```bash + docker-compose up -d + ``` + This starts all services including a sample NGINX server, Prometheus, and Grafana. + +4. **Access the services**: + - **Sample App**: http://localhost:8080 + - **NGINX Server**: http://localhost:80 + - **NGINX Metrics**: http://localhost:9113/metrics + - **NGINX Stub Status**: http://localhost:8081/stub_status + - **Prometheus**: http://localhost:9090 + - **Grafana**: http://localhost:3000 (admin/admin123) + +## Services + +### nginx-exporter +- **Image**: `nginx/nginx-prometheus-exporter:latest` +- **Port**: 9113 +- **Function**: Scrapes NGINX stub_status and converts to Prometheus metrics + +### nginx (Main Server) +- **Image**: `nginx:alpine` +- **Ports**: 80, 8081 (stub_status) +- **Function**: Proxies to sample app and provides metrics endpoint + +### app (Sample Application) +- **Image**: `nginx:alpine` +- **Port**: 8080 +- **Function**: Serves sample web content + +### prometheus +- **Image**: `prom/prometheus:latest` +- **Port**: 9090 +- **Function**: Collects and stores metrics from nginx-exporter + +### grafana +- **Image**: `grafana/grafana:latest` +- **Port**: 3000 +- **Function**: Provides visualization dashboards + +## Configuration Files + +- `nginx/nginx.conf`: Main NGINX configuration with stub_status +- `app/nginx.conf`: Sample application NGINX configuration +- `prometheus/prometheus.yml`: Prometheus scraping configuration +- `grafana/provisioning/`: Grafana datasource and dashboard configuration + +## Testing + +1. **Check nginx-exporter metrics**: + ```bash + curl http://localhost:9113/metrics | grep nginx + ``` + +2. **Check stub_status directly**: + ```bash + curl http://localhost:8081/stub_status + ``` + +3. **Generate traffic**: + ```bash + for i in {1..10}; do curl -s http://localhost:80 > /dev/null; done + ``` + +## Stopping + +Stop all services: +```bash +docker-compose down +``` + +Stop and remove volumes: +```bash +docker-compose down -v +``` + +## Troubleshooting + +1. **Check container logs**: + ```bash + docker-compose logs nginx-exporter + docker-compose logs nginx + ``` + +2. **Verify container status**: + ```bash + docker-compose ps + ``` + +3. **Test internal connectivity**: + ```bash + docker exec nginx-server curl -s http://localhost:8081/stub_status + ``` + +## Network + +All services run on the `nginx-monitoring` bridge network for internal communication. + +## Volumes + +- `prometheus_data`: Persistent storage for Prometheus metrics +- `grafana_data`: Persistent storage for Grafana configurations and dashboards From 54ebea930532cc858aff80ddf4d25e2d9f3b315e Mon Sep 17 00:00:00 2001 From: Benji Shohet <97973081+benjisho@users.noreply.github.com> Date: Wed, 16 Jul 2025 03:21:53 +0000 Subject: [PATCH 3/5] Add CI workflow for Docker Compose testing - Add test-docker-compose-ci.yml workflow to test Docker Compose setup - Test 'docker-compose up -d nginx-exporter' command functionality - Verify nginx-exporter container starts correctly and serves metrics - Test full monitoring stack integration (nginx, prometheus, grafana) - Validate service connectivity and metrics collection - Include proper error handling and cleanup procedures - Add detailed logging for troubleshooting failures - Follow project CI conventions and use consistent action versions --- .github/workflows/test-docker-compose-ci.yml | 286 +++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 .github/workflows/test-docker-compose-ci.yml diff --git a/.github/workflows/test-docker-compose-ci.yml b/.github/workflows/test-docker-compose-ci.yml new file mode 100644 index 00000000..33b1f689 --- /dev/null +++ b/.github/workflows/test-docker-compose-ci.yml @@ -0,0 +1,286 @@ +name: Docker Compose Test + +on: + push: + branches: + - main + paths: + - 'docker-compose.yml' + - 'examples/docker-compose/**' + - '.github/workflows/test-docker-compose-ci.yml' + pull_request: + branches: + - main + paths: + - 'docker-compose.yml' + - 'examples/docker-compose/**' + - '.github/workflows/test-docker-compose-ci.yml' + +defaults: + run: + shell: bash + +concurrency: + group: ${{ github.ref_name }}-test-docker-compose-ci + cancel-in-progress: true + +permissions: + contents: read + +jobs: + docker-compose-nginx-exporter: + name: Docker Compose - NGINX Exporter + runs-on: ubuntu-24.04 + steps: + - name: Checkout Repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + + - name: Validate Docker Compose configuration + run: | + echo "Validating Docker Compose configuration..." + docker-compose config --quiet + echo "✓ Docker Compose configuration is valid" + + - name: Start NGINX Exporter with dependencies + run: | + echo "Starting NGINX Exporter and its dependencies..." + docker-compose up -d nginx-exporter + echo "✓ NGINX Exporter started successfully" + + - name: Wait for services to be ready + run: | + echo "Waiting for services to be ready..." + + # Wait for NGINX to be ready + for i in {1..30}; do + if curl -s http://localhost:8081/stub_status > /dev/null 2>&1; then + echo "✓ NGINX stub_status is ready" + break + fi + echo "Waiting for NGINX stub_status... (attempt $i/30)" + sleep 2 + done + + # Wait for NGINX Exporter to be ready + for i in {1..30}; do + if curl -s http://localhost:9113/metrics > /dev/null 2>&1; then + echo "✓ NGINX Exporter metrics endpoint is ready" + break + fi + echo "Waiting for NGINX Exporter metrics... (attempt $i/30)" + sleep 2 + done + + - name: Verify container status + run: | + echo "Verifying container status..." + docker-compose ps + + # Check if nginx-exporter container is running + if ! docker-compose ps nginx-exporter | grep -q "Up"; then + echo "❌ NGINX Exporter container is not running" + docker-compose logs nginx-exporter + exit 1 + fi + echo "✓ NGINX Exporter container is running" + + # Check if nginx container is running + if ! docker-compose ps nginx | grep -q "Up"; then + echo "❌ NGINX container is not running" + docker-compose logs nginx + exit 1 + fi + echo "✓ NGINX container is running" + + - name: Test NGINX stub_status endpoint + run: | + echo "Testing NGINX stub_status endpoint..." + response=$(curl -s http://localhost:8081/stub_status) + + if [[ -z "$response" ]]; then + echo "❌ NGINX stub_status endpoint returned empty response" + exit 1 + fi + + # Check if response contains expected metrics + if echo "$response" | grep -q "Active connections:"; then + echo "✓ NGINX stub_status endpoint is working correctly" + else + echo "❌ NGINX stub_status endpoint response is invalid" + echo "Response: $response" + exit 1 + fi + + - name: Test NGINX Exporter metrics endpoint + run: | + echo "Testing NGINX Exporter metrics endpoint..." + response=$(curl -s http://localhost:9113/metrics) + + if [[ -z "$response" ]]; then + echo "❌ NGINX Exporter metrics endpoint returned empty response" + exit 1 + fi + + # Check if response contains expected NGINX metrics + if echo "$response" | grep -q "nginx_connections_accepted"; then + echo "✓ NGINX Exporter metrics endpoint is working correctly" + else + echo "❌ NGINX Exporter metrics endpoint response is invalid" + echo "Response: $response" + exit 1 + fi + + - name: Verify metrics collection + run: | + echo "Verifying metrics collection..." + + # Generate some traffic to NGINX + echo "Generating traffic to NGINX..." + for i in {1..5}; do + curl -s http://localhost:80 > /dev/null || true + sleep 1 + done + + # Wait a moment for metrics to be updated + sleep 3 + + # Check if metrics are being collected + metrics=$(curl -s http://localhost:9113/metrics) + + # Verify nginx_up metric exists and is 1 + if echo "$metrics" | grep -q "nginx_up 1"; then + echo "✓ NGINX Exporter is successfully collecting metrics" + else + echo "❌ NGINX Exporter is not collecting metrics correctly" + echo "nginx_up metric:" + echo "$metrics" | grep "nginx_up" || echo "nginx_up metric not found" + exit 1 + fi + + # Verify connection metrics exist + if echo "$metrics" | grep -q "nginx_connections_accepted"; then + echo "✓ Connection metrics are being collected" + else + echo "❌ Connection metrics are not being collected" + exit 1 + fi + + - name: Test service connectivity + run: | + echo "Testing service connectivity..." + + # Test that services can communicate internally + docker exec nginx-prometheus-exporter curl -f http://nginx:8081/stub_status > /dev/null + echo "✓ Services can communicate internally" + + - name: Show container logs on failure + if: failure() + run: | + echo "=== NGINX Exporter Logs ===" + docker-compose logs nginx-exporter + echo "=== NGINX Logs ===" + docker-compose logs nginx + echo "=== App Logs ===" + docker-compose logs app || true + echo "=== Container Status ===" + docker-compose ps + + - name: Cleanup + if: always() + run: | + echo "Cleaning up..." + docker-compose down -v + docker system prune -f + + docker-compose-full-stack: + name: Docker Compose - Full Stack + runs-on: ubuntu-24.04 + steps: + - name: Checkout Repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + + - name: Start full monitoring stack + run: | + echo "Starting full monitoring stack..." + docker-compose up -d + echo "✓ Full monitoring stack started successfully" + + - name: Wait for all services to be ready + run: | + echo "Waiting for all services to be ready..." + + # Wait for all services to be healthy + services=("nginx:8081/stub_status" "nginx-exporter:9113/metrics" "prometheus:9090/-/ready" "grafana:3000/api/health") + + for service in "${services[@]}"; do + IFS=':' read -r host_port path <<< "$service" + + for i in {1..60}; do + if curl -s "http://localhost:${host_port}${path}" > /dev/null 2>&1; then + echo "✓ Service ${host_port} is ready" + break + fi + echo "Waiting for service ${host_port}... (attempt $i/60)" + sleep 2 + done + done + + - name: Verify all containers are running + run: | + echo "Verifying all containers are running..." + docker-compose ps + + # Check that all expected services are up + services=("nginx" "nginx-exporter" "prometheus" "grafana" "app") + + for service in "${services[@]}"; do + if ! docker-compose ps "$service" | grep -q "Up"; then + echo "❌ Service $service is not running" + docker-compose logs "$service" + exit 1 + fi + echo "✓ Service $service is running" + done + + - name: Test full stack integration + run: | + echo "Testing full stack integration..." + + # Test Prometheus can scrape NGINX Exporter + prometheus_targets=$(curl -s http://localhost:9090/api/v1/targets) + if echo "$prometheus_targets" | grep -q "nginx-exporter:9113"; then + echo "✓ Prometheus is configured to scrape NGINX Exporter" + else + echo "❌ Prometheus is not configured to scrape NGINX Exporter" + exit 1 + fi + + # Test Grafana is accessible + grafana_health=$(curl -s http://localhost:3000/api/health) + if echo "$grafana_health" | grep -q "ok"; then + echo "✓ Grafana is healthy" + else + echo "❌ Grafana is not healthy" + exit 1 + fi + + - name: Show container logs on failure + if: failure() + run: | + echo "=== All Container Logs ===" + docker-compose logs + echo "=== Container Status ===" + docker-compose ps + + - name: Cleanup + if: always() + run: | + echo "Cleaning up..." + docker-compose down -v + docker system prune -f From e7452697a3566118c8c92c68ed9d4281e975c7a9 Mon Sep 17 00:00:00 2001 From: Benji Shohet <97973081+benjisho@users.noreply.github.com> Date: Thu, 17 Jul 2025 12:26:39 +0000 Subject: [PATCH 4/5] Fix pre-commit issues: trailing whitespace, line length, YAML, and TOC updates --- .github/workflows/test-docker-compose-ci.yml | 42 +++++++------- README.md | 7 ++- docker-compose.test.yml | 55 +++++++++++++++++-- examples/docker-compose/README.md | 34 +++++++++--- examples/docker-compose/app/html/index.html | 6 +- examples/docker-compose/nginx/nginx.conf | 6 +- .../docker-compose/prometheus/prometheus.yml | 2 +- 7 files changed, 110 insertions(+), 42 deletions(-) diff --git a/.github/workflows/test-docker-compose-ci.yml b/.github/workflows/test-docker-compose-ci.yml index 33b1f689..b94a4b57 100644 --- a/.github/workflows/test-docker-compose-ci.yml +++ b/.github/workflows/test-docker-compose-ci.yml @@ -53,7 +53,7 @@ jobs: - name: Wait for services to be ready run: | echo "Waiting for services to be ready..." - + # Wait for NGINX to be ready for i in {1..30}; do if curl -s http://localhost:8081/stub_status > /dev/null 2>&1; then @@ -63,7 +63,7 @@ jobs: echo "Waiting for NGINX stub_status... (attempt $i/30)" sleep 2 done - + # Wait for NGINX Exporter to be ready for i in {1..30}; do if curl -s http://localhost:9113/metrics > /dev/null 2>&1; then @@ -78,7 +78,7 @@ jobs: run: | echo "Verifying container status..." docker-compose ps - + # Check if nginx-exporter container is running if ! docker-compose ps nginx-exporter | grep -q "Up"; then echo "❌ NGINX Exporter container is not running" @@ -86,7 +86,7 @@ jobs: exit 1 fi echo "✓ NGINX Exporter container is running" - + # Check if nginx container is running if ! docker-compose ps nginx | grep -q "Up"; then echo "❌ NGINX container is not running" @@ -99,12 +99,12 @@ jobs: run: | echo "Testing NGINX stub_status endpoint..." response=$(curl -s http://localhost:8081/stub_status) - + if [[ -z "$response" ]]; then echo "❌ NGINX stub_status endpoint returned empty response" exit 1 fi - + # Check if response contains expected metrics if echo "$response" | grep -q "Active connections:"; then echo "✓ NGINX stub_status endpoint is working correctly" @@ -118,12 +118,12 @@ jobs: run: | echo "Testing NGINX Exporter metrics endpoint..." response=$(curl -s http://localhost:9113/metrics) - + if [[ -z "$response" ]]; then echo "❌ NGINX Exporter metrics endpoint returned empty response" exit 1 fi - + # Check if response contains expected NGINX metrics if echo "$response" | grep -q "nginx_connections_accepted"; then echo "✓ NGINX Exporter metrics endpoint is working correctly" @@ -136,20 +136,20 @@ jobs: - name: Verify metrics collection run: | echo "Verifying metrics collection..." - + # Generate some traffic to NGINX echo "Generating traffic to NGINX..." for i in {1..5}; do curl -s http://localhost:80 > /dev/null || true sleep 1 done - + # Wait a moment for metrics to be updated sleep 3 - + # Check if metrics are being collected metrics=$(curl -s http://localhost:9113/metrics) - + # Verify nginx_up metric exists and is 1 if echo "$metrics" | grep -q "nginx_up 1"; then echo "✓ NGINX Exporter is successfully collecting metrics" @@ -159,7 +159,7 @@ jobs: echo "$metrics" | grep "nginx_up" || echo "nginx_up metric not found" exit 1 fi - + # Verify connection metrics exist if echo "$metrics" | grep -q "nginx_connections_accepted"; then echo "✓ Connection metrics are being collected" @@ -171,7 +171,7 @@ jobs: - name: Test service connectivity run: | echo "Testing service connectivity..." - + # Test that services can communicate internally docker exec nginx-prometheus-exporter curl -f http://nginx:8081/stub_status > /dev/null echo "✓ Services can communicate internally" @@ -214,13 +214,13 @@ jobs: - name: Wait for all services to be ready run: | echo "Waiting for all services to be ready..." - + # Wait for all services to be healthy services=("nginx:8081/stub_status" "nginx-exporter:9113/metrics" "prometheus:9090/-/ready" "grafana:3000/api/health") - + for service in "${services[@]}"; do IFS=':' read -r host_port path <<< "$service" - + for i in {1..60}; do if curl -s "http://localhost:${host_port}${path}" > /dev/null 2>&1; then echo "✓ Service ${host_port} is ready" @@ -235,10 +235,10 @@ jobs: run: | echo "Verifying all containers are running..." docker-compose ps - + # Check that all expected services are up services=("nginx" "nginx-exporter" "prometheus" "grafana" "app") - + for service in "${services[@]}"; do if ! docker-compose ps "$service" | grep -q "Up"; then echo "❌ Service $service is not running" @@ -251,7 +251,7 @@ jobs: - name: Test full stack integration run: | echo "Testing full stack integration..." - + # Test Prometheus can scrape NGINX Exporter prometheus_targets=$(curl -s http://localhost:9090/api/v1/targets) if echo "$prometheus_targets" | grep -q "nginx-exporter:9113"; then @@ -260,7 +260,7 @@ jobs: echo "❌ Prometheus is not configured to scrape NGINX Exporter" exit 1 fi - + # Test Grafana is accessible grafana_health=$(curl -s http://localhost:3000/api/health) if echo "$grafana_health" | grep -q "ok"; then diff --git a/README.md b/README.md index 422ab37f..f4eb9704 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ NGINX Prometheus exporter makes it possible to monitor NGINX or NGINX Plus using - [Prerequisites](#prerequisites) - [Running the Exporter in a Docker Container](#running-the-exporter-in-a-docker-container) - [Running the Exporter Binary](#running-the-exporter-binary) + - [Docker Compose Setup](#docker-compose-setup) - [Usage](#usage) - [Command-line Arguments](#command-line-arguments) - [Exported Metrics](#exported-metrics) @@ -157,9 +158,11 @@ in a Docker container. ### Docker Compose Setup -For a complete monitoring stack including NGINX, NGINX Prometheus Exporter, Prometheus, and Grafana, see the [Docker Compose example](./examples/docker-compose/README.md). +For a complete monitoring stack including NGINX, NGINX Prometheus Exporter, Prometheus, and Grafana, +see the [Docker Compose example](./examples/docker-compose/README.md). The Docker Compose setup provides: + - NGINX server with stub_status enabled - NGINX Prometheus Exporter for metrics collection - Prometheus for time-series storage @@ -167,11 +170,13 @@ The Docker Compose setup provides: - Sample web application for testing Quick start (nginx-exporter only): + ```console docker-compose up -d nginx-exporter ``` For a complete monitoring stack with all services: + ```console docker-compose up -d ``` diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 568ee59b..6028b038 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -1,13 +1,11 @@ -version: '3.8' - services: # Sample web application app: image: nginx:alpine container_name: sample-app volumes: - - ./docker/app/nginx.conf:/etc/nginx/nginx.conf - - ./docker/app/html:/usr/share/nginx/html + - ./examples/docker-compose/app/nginx.conf:/etc/nginx/nginx.conf + - ./examples/docker-compose/app/html:/usr/share/nginx/html ports: - "8080:80" networks: @@ -21,8 +19,8 @@ services: depends_on: - app volumes: - - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf - - ./docker/nginx/conf.d:/etc/nginx/conf.d + - ./examples/docker-compose/nginx/nginx.conf:/etc/nginx/nginx.conf + - ./examples/docker-compose/nginx/conf.d:/etc/nginx/conf.d ports: - "80:80" - "8081:8081" # stub_status port @@ -45,6 +43,51 @@ services: - nginx-monitoring restart: unless-stopped + # Prometheus + prometheus: + image: prom/prometheus:latest + container_name: prometheus + depends_on: + - nginx-exporter + volumes: + - ./examples/docker-compose/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus_data:/prometheus + ports: + - "9090:9090" + networks: + - nginx-monitoring + restart: unless-stopped + command: + - --config.file=/etc/prometheus/prometheus.yml + - --storage.tsdb.path=/prometheus + - --web.console.libraries=/etc/prometheus/console_libraries + - --web.console.templates=/etc/prometheus/consoles + - --storage.tsdb.retention.time=200h + - --web.enable-lifecycle + + # Grafana + grafana: + image: grafana/grafana:latest + container_name: grafana + depends_on: + - prometheus + volumes: + - grafana_data:/var/lib/grafana + - ./examples/docker-compose/grafana/provisioning:/etc/grafana/provisioning + - ./examples/docker-compose/grafana/dashboards:/var/lib/grafana/dashboards + ports: + - "3000:3000" + networks: + - nginx-monitoring + restart: unless-stopped + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin123 + - GF_USERS_ALLOW_SIGN_UP=false + +volumes: + prometheus_data: + grafana_data: + networks: nginx-monitoring: driver: bridge diff --git a/examples/docker-compose/README.md b/examples/docker-compose/README.md index 17206d09..ebab8127 100644 --- a/examples/docker-compose/README.md +++ b/examples/docker-compose/README.md @@ -22,64 +22,76 @@ This Docker Compose configuration provides a complete monitoring stack for NGINX ## Architecture The setup includes: + - **nginx**: Main NGINX server with stub_status enabled (for demo purposes) - **app**: Sample web application served by NGINX (for demo purposes) - **nginx-exporter**: NGINX Prometheus Exporter that scrapes metrics from NGINX - **prometheus**: Prometheus time-series database for metrics collection - **grafana**: Grafana dashboard for visualization -**Note**: If you have an existing NGINX setup, you only need to run the `nginx-exporter` service. Make sure your NGINX has stub_status enabled and is accessible at the configured scrape URI. +**Note**: If you have an existing NGINX setup, you only need to run the `nginx-exporter` service. +Make sure your NGINX has stub_status enabled and is accessible at the configured scrape URI. ## Quick Start 1. **Navigate to the project root directory**: + ```bash cd /path/to/nginx-prometheus-exporter ``` 2. **For existing NGINX setup - Start only the nginx-exporter**: + ```bash docker-compose up -d nginx-exporter ``` + This assumes you already have NGINX running with stub_status enabled at `http://nginx:8081/stub_status`. 3. **For complete demo setup - Start the full monitoring stack**: + ```bash docker-compose up -d ``` + This starts all services including a sample NGINX server, Prometheus, and Grafana. 4. **Access the services**: - - **Sample App**: http://localhost:8080 - - **NGINX Server**: http://localhost:80 - - **NGINX Metrics**: http://localhost:9113/metrics - - **NGINX Stub Status**: http://localhost:8081/stub_status - - **Prometheus**: http://localhost:9090 - - **Grafana**: http://localhost:3000 (admin/admin123) + - **Sample App**: + - **NGINX Server**: + - **NGINX Metrics**: + - **NGINX Stub Status**: + - **Prometheus**: + - **Grafana**: (admin/admin123) ## Services ### nginx-exporter + - **Image**: `nginx/nginx-prometheus-exporter:latest` - **Port**: 9113 - **Function**: Scrapes NGINX stub_status and converts to Prometheus metrics ### nginx (Main Server) + - **Image**: `nginx:alpine` - **Ports**: 80, 8081 (stub_status) - **Function**: Proxies to sample app and provides metrics endpoint ### app (Sample Application) + - **Image**: `nginx:alpine` - **Port**: 8080 - **Function**: Serves sample web content ### prometheus + - **Image**: `prom/prometheus:latest` - **Port**: 9090 - **Function**: Collects and stores metrics from nginx-exporter ### grafana + - **Image**: `grafana/grafana:latest` - **Port**: 3000 - **Function**: Provides visualization dashboards @@ -94,16 +106,19 @@ The setup includes: ## Testing 1. **Check nginx-exporter metrics**: + ```bash curl http://localhost:9113/metrics | grep nginx ``` 2. **Check stub_status directly**: + ```bash curl http://localhost:8081/stub_status ``` 3. **Generate traffic**: + ```bash for i in {1..10}; do curl -s http://localhost:80 > /dev/null; done ``` @@ -111,11 +126,13 @@ The setup includes: ## Stopping Stop all services: + ```bash docker-compose down ``` Stop and remove volumes: + ```bash docker-compose down -v ``` @@ -123,17 +140,20 @@ docker-compose down -v ## Troubleshooting 1. **Check container logs**: + ```bash docker-compose logs nginx-exporter docker-compose logs nginx ``` 2. **Verify container status**: + ```bash docker-compose ps ``` 3. **Test internal connectivity**: + ```bash docker exec nginx-server curl -s http://localhost:8081/stub_status ``` diff --git a/examples/docker-compose/app/html/index.html b/examples/docker-compose/app/html/index.html index c0380fd7..e4e8f1aa 100644 --- a/examples/docker-compose/app/html/index.html +++ b/examples/docker-compose/app/html/index.html @@ -121,7 +121,7 @@

🚀 Load Testing

function sendRequests() { const results = document.getElementById('results'); results.innerHTML = '

Sending 100 requests...

'; - + let completed = 0; for (let i = 0; i < 100; i++) { fetch('/?load-test=' + i) @@ -143,7 +143,7 @@

🚀 Load Testing

function sendSlowRequests() { const results = document.getElementById('results'); results.innerHTML = '

Sending slow requests...

'; - + let completed = 0; for (let i = 0; i < 20; i++) { setTimeout(() => { @@ -167,7 +167,7 @@

🚀 Load Testing

function sendAPIRequests() { const results = document.getElementById('results'); results.innerHTML = '

Sending API requests...

'; - + let completed = 0; for (let i = 0; i < 50; i++) { fetch('/api/test' + i) diff --git a/examples/docker-compose/nginx/nginx.conf b/examples/docker-compose/nginx/nginx.conf index 78791539..7f66a2e0 100644 --- a/examples/docker-compose/nginx/nginx.conf +++ b/examples/docker-compose/nginx/nginx.conf @@ -26,7 +26,7 @@ http { server { listen 80; server_name localhost; - + # Proxy to sample app location / { proxy_pass http://app:80; @@ -41,13 +41,13 @@ http { server { listen 8081; server_name localhost; - + location /stub_status { stub_status; access_log off; allow all; } - + location / { return 404; } diff --git a/examples/docker-compose/prometheus/prometheus.yml b/examples/docker-compose/prometheus/prometheus.yml index e9b2aca2..b5ae9362 100644 --- a/examples/docker-compose/prometheus/prometheus.yml +++ b/examples/docker-compose/prometheus/prometheus.yml @@ -2,7 +2,7 @@ global: scrape_interval: 15s evaluation_interval: 15s -rule_files: +rule_files: [] # - "first_rules.yml" # - "second_rules.yml" From c1e5b237ceb7150bf035783080c6909cea0a356c Mon Sep 17 00:00:00 2001 From: Benji Shohet <97973081+benjisho@users.noreply.github.com> Date: Thu, 17 Jul 2025 12:29:59 +0000 Subject: [PATCH 5/5] fix: address YAML lint issues - Fix comment indentation in prometheus.yml - Fix empty values in docker-compose volumes sections - All pre-commit hooks now pass --- docker-compose.test.yml | 4 ++-- docker-compose.yml | 4 ++-- examples/docker-compose/prometheus/prometheus.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 6028b038..b5a15bc4 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -85,8 +85,8 @@ services: - GF_USERS_ALLOW_SIGN_UP=false volumes: - prometheus_data: - grafana_data: + prometheus_data: {} + grafana_data: {} networks: nginx-monitoring: diff --git a/docker-compose.yml b/docker-compose.yml index 6028b038..b5a15bc4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -85,8 +85,8 @@ services: - GF_USERS_ALLOW_SIGN_UP=false volumes: - prometheus_data: - grafana_data: + prometheus_data: {} + grafana_data: {} networks: nginx-monitoring: diff --git a/examples/docker-compose/prometheus/prometheus.yml b/examples/docker-compose/prometheus/prometheus.yml index b5ae9362..a0c1bb1c 100644 --- a/examples/docker-compose/prometheus/prometheus.yml +++ b/examples/docker-compose/prometheus/prometheus.yml @@ -3,8 +3,8 @@ global: evaluation_interval: 15s rule_files: [] - # - "first_rules.yml" - # - "second_rules.yml" +# - "first_rules.yml" +# - "second_rules.yml" scrape_configs: # The job name is added as a label `job=` to any timeseries scraped from this config.