diff --git a/.github/workflows/test-docker-compose-ci.yml b/.github/workflows/test-docker-compose-ci.yml new file mode 100644 index 00000000..b94a4b57 --- /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 diff --git a/README.md b/README.md index 2d868186..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) @@ -155,6 +156,31 @@ 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/docker-compose.test.yml b/docker-compose.test.yml new file mode 100644 index 00000000..b5a15bc4 --- /dev/null +++ b/docker-compose.test.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/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..b5a15bc4 --- /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/README.md b/examples/docker-compose/README.md new file mode 100644 index 00000000..ebab8127 --- /dev/null +++ b/examples/docker-compose/README.md @@ -0,0 +1,168 @@ +# 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**: + - **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 + +## 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 diff --git a/examples/docker-compose/app/html/index.html b/examples/docker-compose/app/html/index.html new file mode 100644 index 00000000..e4e8f1aa --- /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..7f66a2e0 --- /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..a0c1bb1c --- /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