├── backend2 ├── Dockerfile └── index.js ├── backend1 ├── Dockerfile ├── package.json └── index.js ├── .gitignore ├── prometheus.yml ├── admin-frontend └── index.html ├── app-frontend └── index.html ├── README.md ├── prometheus_queries.md └── docker-compose.yml /backend2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine 2 | WORKDIR /app 3 | COPY index.js . 4 | EXPOSE 3000 5 | CMD ["node", "index.js"] -------------------------------------------------------------------------------- /backend1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine 2 | WORKDIR /app 3 | COPY package.json . 4 | RUN npm install 5 | COPY index.js . 6 | EXPOSE 3000 7 | CMD ["node", "index.js"] -------------------------------------------------------------------------------- /backend1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend1", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "dependencies": { 6 | "prom-client": "^14.2.0" 7 | } 8 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node.js 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | *.log 7 | .env* 8 | 9 | # Build artifacts 10 | dist/ 11 | build/ 12 | 13 | # OS generated files 14 | .DS_Store 15 | Thumbs.db 16 | 17 | # IDE specific 18 | .vscode/ 19 | -------------------------------------------------------------------------------- /backend2/index.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | 3 | const server = http.createServer((req, res) => { 4 | res.writeHead(200, { 'Content-Type': 'application/json' }); 5 | res.end(JSON.stringify({ 6 | service: 'Backend 2', 7 | message: 'This is the second backend service', 8 | path: req.url 9 | })); 10 | }); 11 | 12 | server.listen(3000, () => { 13 | console.log('Backend 2 running on port 3000'); 14 | }); -------------------------------------------------------------------------------- /prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 15s 3 | evaluation_interval: 15s 4 | 5 | scrape_configs: 6 | - job_name: 'traefik' 7 | static_configs: 8 | - targets: ['traefik:8080'] 9 | 10 | - job_name: 'backend1' 11 | static_configs: 12 | - targets: ['backend1:3000'] 13 | metrics_path: '/metrics' 14 | 15 | - job_name: 'backend2' 16 | static_configs: 17 | - targets: ['backend2:3000'] 18 | metrics_path: '/metrics' -------------------------------------------------------------------------------- /admin-frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |This is the admin panel running on admin.local.com
20 | 21 | -------------------------------------------------------------------------------- /app-frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |This is the main application frontend running on app.local.com
20 | 21 | -------------------------------------------------------------------------------- /backend1/index.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const client = require('prom-client'); 3 | 4 | // Create a Registry to register metrics 5 | const register = new client.Registry(); 6 | 7 | // Add default metrics (CPU, memory, etc.) 8 | client.collectDefaultMetrics({ 9 | register, 10 | prefix: 'backend1_' 11 | }); 12 | 13 | // Create a custom counter for requests 14 | const requestCounter = new client.Counter({ 15 | name: 'backend1_requests_total', 16 | help: 'Total number of requests to backend1', 17 | labelNames: ['path'] 18 | }); 19 | 20 | // Register the custom counter 21 | register.registerMetric(requestCounter); 22 | 23 | const server = http.createServer(async (req, res) => { 24 | // Increment the request counter 25 | requestCounter.inc({ path: req.url }); 26 | 27 | // Handle metrics endpoint 28 | if (req.url === '/metrics') { 29 | res.setHeader('Content-Type', register.contentType); 30 | res.end(await register.metrics()); 31 | return; 32 | } 33 | 34 | res.writeHead(200, { 'Content-Type': 'application/json' }); 35 | res.end(JSON.stringify({ 36 | service: 'Backend 1', 37 | message: 'This is the first backend service', 38 | path: req.url 39 | })); 40 | }); 41 | 42 | server.listen(3000, () => { 43 | console.log('Backend 1 running on port 3000'); 44 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Traefik Demo Stack 2 | 3 | This is a simple demonstration of Traefik as a reverse proxy with multiple services. 4 | 5 | ## Prerequisites 6 | 7 | - Docker 8 | - Docker Compose 9 | 10 | ## Setup 11 | 12 | 1. **DNS Configuration:** Ensure you have the following DNS records pointing to your server's IP address: 13 | - `app.yourdomain.com` 14 | - `admin.yourdomain.com` 15 | - `api.yourdomain.com` 16 | - `traefik.yourdomain.com` 17 | - `prometheus.yourdomain.com` 18 | 19 | 2. Start the stack: 20 | ```bash 21 | docker-compose up -d 22 | ``` 23 | 24 | ## Accessing the Services 25 | 26 | - Traefik Dashboard: https://traefik.yourdomain.com 27 | - Frontend: 28 | - App Frontend: https://app.yourdomain.com 29 | - Admin Frontend: https://admin.yourdomain.com 30 | - Backend: 31 | - Backend 1: https://api.yourdomain.com/backend1 32 | - Backend 2: https://api.yourdomain.com/backend2 33 | - Metrics Endpoints: 34 | - Traefik Metrics: https://traefik.yourdomain.com/metrics 35 | - Prometheus: https://prometheus.yourdomain.com 36 | 37 | ## Docker Compose Configuration 38 | 39 | The application utilizes Docker Compose for defining and managing multi-container Docker applications. Key aspects of the configuration include: 40 | 41 | ### Networks 42 | 43 | The following networks are defined: 44 | 45 | - `app-network`: A bridge network for the application's services. 46 | - `monitoring-network`: A bridge network for monitoring-related services. 47 | 48 | These networks enable communication between the services. `app-network` connects all the services and `monitoring-network` is used to connect traefik and prometheus. 49 | 50 | ### Volumes 51 | 52 | The following volumes are defined: 53 | 54 | - `prometheus_data`: A local volume to store Prometheus data. 55 | - `letsencrypt`: A local volume to store Let's Encrypt certificates for secure communication. 56 | 57 | These volumes persist data across container restarts. 58 | 59 | ## Stopping the Stack 60 | 61 | ```bash 62 | docker-compose down 63 | ``` -------------------------------------------------------------------------------- /prometheus_queries.md: -------------------------------------------------------------------------------- 1 | # Prometheus Queries for Traefik Demo 2 | 3 | ## Backend1 Service Metrics 4 | 5 | ### Request Rate 6 | ```promql 7 | rate(backend1_requests_total[5m]) 8 | ``` 9 | Shows the request rate per second over the last 5 minutes. 10 | 11 | ### Total Requests by Path 12 | ```promql 13 | sum(backend1_requests_total) by (path) 14 | ``` 15 | Shows the total number of requests grouped by path. 16 | 17 | ### Error Rate (if you add error tracking) 18 | ```promql 19 | rate(backend1_errors_total[5m]) 20 | ``` 21 | 22 | ## System Metrics 23 | 24 | ### CPU Usage 25 | ```promql 26 | rate(process_cpu_seconds_total[5m]) * 100 27 | ``` 28 | Shows CPU usage percentage. 29 | 30 | ### Memory Usage 31 | ```promql 32 | process_resident_memory_bytes 33 | ``` 34 | Shows the resident memory usage in bytes. 35 | 36 | ### Node.js Event Loop Lag 37 | ```promql 38 | nodejs_eventloop_lag_seconds 39 | ``` 40 | Shows the event loop lag in seconds. 41 | 42 | ## Traefik Metrics 43 | 44 | ### Request Rate by Service 45 | ```promql 46 | rate(traefik_service_requests_total[5m]) 47 | ``` 48 | Shows the request rate per service. 49 | 50 | ### Average Response Time 51 | ```promql 52 | rate(traefik_service_request_duration_seconds_sum[5m]) / rate(traefik_service_request_duration_seconds_count[5m]) 53 | ``` 54 | Shows the average response time in seconds. 55 | 56 | ### Active Connections 57 | ```promql 58 | traefik_entrypoint_open_connections 59 | ``` 60 | Shows the number of currently open connections. 61 | 62 | ### HTTP Status Codes 63 | ```promql 64 | sum(rate(traefik_service_requests_total{code=~"2.."}[5m])) by (service) 65 | ``` 66 | Shows successful requests (2xx) rate. 67 | 68 | ```promql 69 | sum(rate(traefik_service_requests_total{code=~"4.."}[5m])) by (service) 70 | ``` 71 | Shows client error requests (4xx) rate. 72 | 73 | ```promql 74 | sum(rate(traefik_service_requests_total{code=~"5.."}[5m])) by (service) 75 | ``` 76 | Shows server error requests (5xx) rate. 77 | 78 | ## Combined Metrics 79 | 80 | ### Request Distribution 81 | ```promql 82 | sum(rate(traefik_service_requests_total[5m])) by (service) 83 | ``` 84 | Shows the distribution of requests across all services. 85 | 86 | ### Error Rate Percentage 87 | ```promql 88 | (sum(rate(traefik_service_requests_total{code=~"5.."}[5m])) by (service) / sum(rate(traefik_service_requests_total[5m])) by (service)) * 100 89 | ``` 90 | Shows the percentage of 5xx errors per service. 91 | 92 | ### Service Health 93 | ```promql 94 | up 95 | ``` 96 | Shows which services are up (1) or down (0). 97 | 98 | ## Alerting Rules (for future use) 99 | 100 | ```yaml 101 | groups: 102 | - name: example 103 | rules: 104 | - alert: HighErrorRate 105 | expr: sum(rate(traefik_service_requests_total{code=~"5.."}[5m])) by (service) / sum(rate(traefik_service_requests_total[5m])) by (service) > 0.05 106 | for: 5m 107 | labels: 108 | severity: warning 109 | annotations: 110 | summary: High error rate on {{ $labels.service }} 111 | description: "Error rate is {{ $value }}" 112 | 113 | - alert: HighLatency 114 | expr: rate(traefik_service_request_duration_seconds_sum[5m]) / rate(traefik_service_request_duration_seconds_count[5m]) > 1 115 | for: 5m 116 | labels: 117 | severity: warning 118 | annotations: 119 | summary: High latency on {{ $labels.service }} 120 | description: "Latency is {{ $value }} seconds" 121 | ``` -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | networks: 2 | app-network: 3 | driver: bridge 4 | monitoring-network: 5 | driver: bridge 6 | 7 | volumes: 8 | prometheus_data: 9 | driver: local 10 | letsencrypt: # Volume to store Let's Encrypt certificates 11 | 12 | services: 13 | traefik: 14 | container_name: traefik 15 | image: traefik:v3.3.5 16 | command: 17 | # Base config 18 | - "--providers.docker=true" 19 | - "--providers.docker.exposedbydefault=false" 20 | - "--api.dashboard=true" 21 | # Entrypoints 22 | - "--entrypoints.web.address=:80" 23 | - "--entrypoints.websecure.address=:443" 24 | # Logging 25 | - "--log.level=INFO" # Optional: Set log level 26 | - "--accesslog=true" # Enable access logs 27 | # Let's Encrypt configuration 28 | - "--certificatesresolvers.myresolver.acme.email=hello@yourdomain.com" 29 | - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" 30 | - "--certificatesresolvers.myresolver.acme.httpchallenge=true" 31 | - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web" # Use HTTP challenge on port 80 32 | # Prometheus Metrics 33 | - "--metrics.prometheus=true" 34 | # - "--metrics.prometheus.entryPoint=metrics" # Removed this line 35 | ports: 36 | - "80:80" # HTTP 37 | - "443:443" # HTTPS 38 | - "8080:8080" # Traefik Dashboard (optional, for direct access if API insecure is true) 39 | volumes: 40 | - /var/run/docker.sock:/var/run/docker.sock:ro 41 | - letsencrypt:/letsencrypt # Mount the volume for certificates 42 | networks: 43 | - app-network 44 | - monitoring-network 45 | deploy: 46 | resources: 47 | limits: 48 | cpus: '0.50' 49 | memory: 512M 50 | labels: 51 | # Enable Traefik for itself (dashboard) 52 | - "traefik.enable=true" 53 | # --- HTTP to HTTPS Redirect --- 54 | # 1. Define the middleware 55 | - "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https" 56 | # 2. Create a router that captures all HTTP traffic 57 | - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)" 58 | - "traefik.http.routers.http-catchall.entrypoints=web" 59 | - "traefik.http.routers.http-catchall.middlewares=https-redirect" 60 | # --- Traefik Dashboard Router (HTTPS) --- 61 | - "traefik.http.routers.dashboard.rule=Host(`traefik.yourdomain.com`)" 62 | - "traefik.http.routers.dashboard.service=api@internal" 63 | - "traefik.http.routers.dashboard.entrypoints=websecure" # Use HTTPS entrypoint 64 | - "traefik.http.routers.dashboard.tls=true" # Enable TLS 65 | - "traefik.http.routers.dashboard.tls.certresolver=myresolver" # Specify Let's Encrypt resolver 66 | # --- Traefik Metrics Router (HTTPS) --- 67 | - "traefik.http.routers.metrics.rule=Host(`traefik.yourdomain.com`) && Path(`/metrics`)" 68 | - "traefik.http.routers.metrics.service=prometheus@internal" # Route to internal prometheus service 69 | - "traefik.http.routers.metrics.entrypoints=websecure" # Use HTTPS entrypoint 70 | - "traefik.http.routers.metrics.tls=true" # Enable TLS 71 | - "traefik.http.routers.metrics.tls.certresolver=myresolver" # Specify Let's Encrypt resolver 72 | 73 | prometheus: 74 | container_name: prometheus 75 | image: prom/prometheus:latest 76 | volumes: 77 | - prometheus_data:/prometheus 78 | - ./prometheus.yml:/etc/prometheus/prometheus.yml 79 | command: 80 | - '--config.file=/etc/prometheus/prometheus.yml' 81 | ports: 82 | - "9090:9090" # Still expose for direct access if needed 83 | networks: 84 | - monitoring-network 85 | deploy: 86 | resources: 87 | limits: 88 | cpus: '0.50' 89 | memory: 512M 90 | labels: 91 | - "traefik.enable=true" 92 | - "traefik.http.routers.prometheus.rule=Host(`prometheus.yourdomain.com`)" 93 | - "traefik.http.routers.prometheus.entrypoints=websecure" # Use HTTPS entrypoint 94 | - "traefik.http.routers.prometheus.tls=true" # Enable TLS 95 | - "traefik.http.routers.prometheus.tls.certresolver=myresolver" # Specify Let's Encrypt resolver 96 | - "traefik.http.services.prometheus.loadbalancer.server.port=9090" # Tell Traefik which port Prometheus uses 97 | 98 | app-frontend: 99 | container_name: app-frontend 100 | image: nginx:alpine 101 | volumes: 102 | - ./app-frontend:/usr/share/nginx/html 103 | networks: 104 | - app-network 105 | deploy: 106 | resources: 107 | limits: 108 | cpus: '0.25' 109 | memory: 256M 110 | labels: 111 | - "traefik.enable=true" 112 | - "traefik.http.routers.app-frontend.rule=Host(`app.yourdomain.com`)" 113 | - "traefik.http.routers.app-frontend.entrypoints=websecure" # Use HTTPS entrypoint 114 | - "traefik.http.routers.app-frontend.tls=true" # Enable TLS 115 | - "traefik.http.routers.app-frontend.tls.certresolver=myresolver" # Specify Let's Encrypt resolver 116 | - "traefik.http.services.app-frontend.loadbalancer.server.port=80" # Nginx default port 117 | 118 | admin-frontend: 119 | container_name: admin-frontend 120 | image: nginx:alpine 121 | volumes: 122 | - ./admin-frontend:/usr/share/nginx/html 123 | networks: 124 | - app-network 125 | deploy: 126 | resources: 127 | limits: 128 | cpus: '0.25' 129 | memory: 256M 130 | labels: 131 | - "traefik.enable=true" 132 | - "traefik.http.routers.admin-frontend.rule=Host(`admin.yourdomain.com`)" 133 | - "traefik.http.routers.admin-frontend.entrypoints=websecure" # Use HTTPS entrypoint 134 | - "traefik.http.routers.admin-frontend.tls=true" # Enable TLS 135 | - "traefik.http.routers.admin-frontend.tls.certresolver=myresolver" # Specify Let's Encrypt resolver 136 | - "traefik.http.services.admin-frontend.loadbalancer.server.port=80" # Nginx default port 137 | 138 | backend1: 139 | build: ./backend1 140 | deploy: 141 | replicas: 2 142 | resources: 143 | limits: 144 | cpus: '0.50' 145 | memory: 512M 146 | networks: 147 | - app-network 148 | labels: 149 | - "traefik.enable=true" 150 | - "traefik.http.routers.backend1.rule=Host(`api.yourdomain.com`) && PathPrefix(`/backend1`)" 151 | - "traefik.http.routers.backend1.entrypoints=websecure" # Use HTTPS entrypoint 152 | - "traefik.http.routers.backend1.tls=true" # Enable TLS 153 | - "traefik.http.routers.backend1.tls.certresolver=myresolver" # Specify Let's Encrypt resolver 154 | - "traefik.http.services.backend1.loadbalancer.server.port=3000" 155 | - "traefik.http.services.backend1.loadbalancer.sticky=true" 156 | 157 | backend2: 158 | container_name: backend2 159 | build: ./backend2 160 | networks: 161 | - app-network 162 | deploy: 163 | resources: 164 | limits: 165 | cpus: '0.50' 166 | memory: 512M 167 | labels: 168 | - "traefik.enable=true" 169 | - "traefik.http.routers.backend2.rule=Host(`api.yourdomain.com`) && PathPrefix(`/backend2`)" 170 | - "traefik.http.routers.backend2.entrypoints=websecure" # Use HTTPS entrypoint 171 | - "traefik.http.routers.backend2.tls=true" # Enable TLS 172 | - "traefik.http.routers.backend2.tls.certresolver=myresolver" # Specify Let's Encrypt resolver 173 | - "traefik.http.services.backend2.loadbalancer.server.port=3000" 174 | --------------------------------------------------------------------------------