From e1f00740493496c26cd633536b1efacaf713cc78 Mon Sep 17 00:00:00 2001 From: Markus Reinhold Date: Wed, 22 Mar 2023 23:54:10 +0100 Subject: [PATCH 1/3] Add PHP-FPM dashboard Expose prometheus metrics and add Grafana dashboard. The Metrics endpoint is not yet protected. --- composer.json | 1 + composer.lock | 134 +++++- config/config.yml | 9 + config/routing.yml | 4 + config/web-interface/config.yml | 3 + docker-compose.yml | 1 + docker/grafana/dashboards/php-fpm.json | 568 +++++++++++++++++++++++++ src/Kernel.php | 4 +- 8 files changed, 722 insertions(+), 2 deletions(-) create mode 100644 docker/grafana/dashboards/php-fpm.json diff --git a/composer.json b/composer.json index 0a4e9529..43142b4a 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ "ext-pcntl": "*", "ext-posix": "*", "ext-session": "*", + "artprima/prometheus-metrics-bundle": "^1.16", "doctrine/dbal": "^3.3", "doctrine/doctrine-bundle": "^2.7", "doctrine/doctrine-migrations-bundle": "^3.2", diff --git a/composer.lock b/composer.lock index 7619047d..56309a6f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,73 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "03c2ad0d7ce7a2c9a3fe48803925e738", + "content-hash": "6588528fa6ce6e4b520a1500a144d917", "packages": [ + { + "name": "artprima/prometheus-metrics-bundle", + "version": "1.16.0", + "source": { + "type": "git", + "url": "https://github.com/artprima/prometheus-metrics-bundle.git", + "reference": "65430eef5ab064401426364731b2f386010e4cf0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/artprima/prometheus-metrics-bundle/zipball/65430eef5ab064401426364731b2f386010e4cf0", + "reference": "65430eef5ab064401426364731b2f386010e4cf0", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^8.0", + "promphp/prometheus_client_php": "^2.6", + "symfony/config": "^5.4|^6.2", + "symfony/dependency-injection": "^5.4|^6.2", + "symfony/http-kernel": "^5.4|^6.2" + }, + "require-dev": { + "escapestudios/symfony2-coding-standard": "^3.11", + "friendsofphp/php-cs-fixer": "^2.17", + "phpunit/phpunit": "^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5", + "symfony/browser-kit": "^5.4|^6.2", + "symfony/framework-bundle": "^5.4|^6.2", + "symfony/yaml": "^5.4|^6.2" + }, + "suggest": { + "ext-apcu": "Required if using APCu as prometheus metrics backend", + "ext-redis": "Required if using Redis as prometheus metrics backend", + "symfony/stopwatch": "Required if you want to measure request duration" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Artprima\\PrometheusMetricsBundle\\": "./" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Denis Voytyuk", + "email": "ask@artprima.cz" + } + ], + "description": "Symfony 5 / 6 Prometheus Metrics Bundle", + "keywords": [ + "Metrics", + "prometheus", + "symfony", + "symfony-bundle" + ], + "support": { + "issues": "https://github.com/artprima/prometheus-metrics-bundle/issues", + "source": "https://github.com/artprima/prometheus-metrics-bundle/tree/1.16.0" + }, + "time": "2023-02-22T11:04:16+00:00" + }, { "name": "doctrine/annotations", "version": "2.0.1", @@ -2523,6 +2588,73 @@ ], "time": "2023-03-02T18:32:04+00:00" }, + { + "name": "promphp/prometheus_client_php", + "version": "v2.6.2", + "source": { + "type": "git", + "url": "https://github.com/PromPHP/prometheus_client_php.git", + "reference": "df77bbcc65bd173f2ffaf40ab4e1ca8716da8ce6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PromPHP/prometheus_client_php/zipball/df77bbcc65bd173f2ffaf40ab4e1ca8716da8ce6", + "reference": "df77bbcc65bd173f2ffaf40ab4e1ca8716da8ce6", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.2|^8.0" + }, + "replace": { + "endclothing/prometheus_client_php": "*", + "jimdo/prometheus_client_php": "*", + "lkaemmerling/prometheus_client_php": "*" + }, + "require-dev": { + "guzzlehttp/guzzle": "^6.3|^7.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5.4", + "phpstan/phpstan-phpunit": "^1.1.0", + "phpstan/phpstan-strict-rules": "^1.1.0", + "phpunit/phpunit": "^9.4", + "squizlabs/php_codesniffer": "^3.6", + "symfony/polyfill-apcu": "^1.6" + }, + "suggest": { + "ext-apc": "Required if using APCu.", + "ext-redis": "Required if using Redis.", + "promphp/prometheus_push_gateway_php": "An easy client for using Prometheus PushGateway.", + "symfony/polyfill-apcu": "Required if you use APCu on PHP8.0+" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Prometheus\\": "src/Prometheus/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Lukas Kämmerling", + "email": "kontakt@lukas-kaemmerling.de" + } + ], + "description": "Prometheus instrumentation library for PHP applications.", + "support": { + "issues": "https://github.com/PromPHP/prometheus_client_php/issues", + "source": "https://github.com/PromPHP/prometheus_client_php/tree/v2.6.2" + }, + "time": "2022-06-30T03:46:23+00:00" + }, { "name": "psr/cache", "version": "3.0.0", diff --git a/config/config.yml b/config/config.yml index dc019b44..617df214 100644 --- a/config/config.yml +++ b/config/config.yml @@ -27,6 +27,15 @@ monolog: twig: strict_variables: '%kernel.debug%' +artprima_prometheus_metrics: + namespace: app + ignored_routes: [metrics] + storage: + type: apcng + disable_default_metrics: false + disable_default_promphp_metrics: false + enable_console_metrics: false + services: gaming.schedule-doctrine-connection-heartbeat-middleware: diff --git a/config/routing.yml b/config/routing.yml index b833cbdd..91474415 100644 --- a/config/routing.yml +++ b/config/routing.yml @@ -1,2 +1,6 @@ WebInterface: resource: web-interface/routing.yml + +metrics: + path: /metrics + controller: Artprima\PrometheusMetricsBundle\Controller\MetricsController::prometheus diff --git a/config/web-interface/config.yml b/config/web-interface/config.yml index 21913c7e..bc2e34c5 100644 --- a/config/web-interface/config.yml +++ b/config/web-interface/config.yml @@ -20,6 +20,9 @@ security: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false + metrics: + pattern: ^/metrics + security: false main: pattern: ^/ custom_authenticators: diff --git a/docker-compose.yml b/docker-compose.yml index 12f5a18e..08a4d976 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -147,6 +147,7 @@ services: - "traefik.http.routers.php-fpm.priority=10" - "traefik.http.routers.php-fpm.rule=PathPrefix(`/`)" - "traefik.http.services.php-fpm.loadbalancer.server.port=80" + - "prometheus-job=php-fpm" ############################## # Long running processes # diff --git a/docker/grafana/dashboards/php-fpm.json b/docker/grafana/dashboards/php-fpm.json new file mode 100644 index 00000000..90a89500 --- /dev/null +++ b/docker/grafana/dashboards/php-fpm.json @@ -0,0 +1,568 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 4, + "iteration": 1679525048450, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "expr": "sum(app_http_requests_total{action=\"all\", instance=~\"$instance\"})", + "refId": "A" + } + ], + "title": "Total Requests", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [] + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 13, + "options": { + "legend": { + "displayMode": "table", + "placement": "right", + "values": [ + "value" + ] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(app_http_2xx_responses_total{action=\"all\", instance=~\"$instance\"}[1h]))", + "legendFormat": "2xx", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "increase(app_http_4xx_responses_total{action=\"all\"}[1h])", + "hide": false, + "legendFormat": "4xx", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "increase(app_http_5xx_responses_total{action=\"all\"}[1h])", + "hide": false, + "legendFormat": "5xx", + "range": true, + "refId": "C" + } + ], + "title": "Status Codes Last Hour", + "type": "piechart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 9, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "right", + "sortBy": "Last *", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(app_http_requests_total{action!=\"all\", instance=~\"$instance\"}[1h])) by (action)", + "format": "time_series", + "legendFormat": "{{action}}", + "range": true, + "refId": "A" + } + ], + "title": "Total Requests Last Hour", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 0, + "y": 3 + }, + "id": 11, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "expr": "sum(rate(app_http_requests_total{action=\"all\", instance=~\"$instance\"}[$__rate_interval]))", + "refId": "A" + } + ], + "title": "Requests / s", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "noValue": "0 ms", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 6 + }, + "id": 10, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "right", + "sortBy": "Last *", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "sum(\n rate(app_request_durations_histogram_seconds_sum{action!=\"all\", instance=~\"$instance\"}[1h])\n) by (action)\n/\nsum(\n rate(app_request_durations_histogram_seconds_count{action!=\"all\", instance=~\"$instance\"}[1h])\n) by (action)\n", + "format": "time_series", + "legendFormat": "{{action}}", + "range": true, + "refId": "A" + } + ], + "title": "Average Response Time Last Hour", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "noValue": "0 ms", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 6 + }, + "id": 7, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "right", + "sortBy": "Last *", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "8.5.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "histogram_quantile(\n 0.95,\n sum(\n rate(app_request_durations_histogram_seconds_bucket{action!=\"all\", instance=~\"$instance\"}[1h])\n ) by (action, le)\n)", + "format": "time_series", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Percentile 0.95 Last Hour", + "transformations": [], + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 36, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "definition": "label_values(app_instance_name, instance)", + "hide": 0, + "includeAll": true, + "label": "Instance", + "multi": true, + "name": "instance", + "options": [], + "query": { + "query": "label_values(app_instance_name, instance)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "PHP-FPM Overview", + "uid": "e8jr4ZB4k", + "version": 1, + "weekStart": "" +} diff --git a/src/Kernel.php b/src/Kernel.php index 10cc9421..16037458 100644 --- a/src/Kernel.php +++ b/src/Kernel.php @@ -4,6 +4,7 @@ namespace Gaming; +use Artprima\PrometheusMetricsBundle\ArtprimaPrometheusMetricsBundle; use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; use Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle; use Marein\LockDoctrineMigrationsBundle\MareinLockDoctrineMigrationsBundle; @@ -33,7 +34,8 @@ public function registerBundles(): array new MareinStandardHeadersCsrfBundle(), new TwigBundle(), new MonologBundle(), - new SecurityBundle() + new SecurityBundle(), + new ArtprimaPrometheusMetricsBundle() ]; if ($this->getEnvironment() === 'dev') { From 0af05c03e18000a1489b12736346efd88b6e5709 Mon Sep 17 00:00:00 2001 From: Markus Reinhold Date: Fri, 24 Mar 2023 01:17:35 +0100 Subject: [PATCH 2/3] Secure metrics via ip addresses The environment variable APP_METRICS_ALLOWED_IPS can take a comma-separated list of ip addresses. Additionally, APP_TRUSTED_PROXIES can be configured. This is needed because when run, e.g., in a docker environment, the allowed ips for metrics can cover the whole ip address range of docker. If the load balancer runs within the same range, it would always be allowed to access the metrics endpoint. With trusted proxies, it would forward the client's ip address, which at the end would be rejected. --- .env | 2 ++ config/config.yml | 1 + config/web-interface/config.yml | 5 ++++- docker-compose.production.yml | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.env b/.env index 11d2d905..54f6a35e 100644 --- a/.env +++ b/.env @@ -4,6 +4,8 @@ # dev|prod APP_ENVIRONMENT=dev APP_KERNEL_SECRET=ThisTokenIsNotSoSecretChangeIt +APP_TRUSTED_PROXIES= +APP_METRICS_ALLOWED_IPS=0.0.0.0/0 APP_WAIT_FOR=mysql:3306,redis:6379,rabbit-mq:5672,nchan:81 APP_RABBIT_MQ_DSN=amqp://guest:guest@rabbit-mq:5672?receive_method=basic_consume&qos_prefetch_count=10&heartbeat=60 diff --git a/config/config.yml b/config/config.yml index 617df214..78b19e05 100644 --- a/config/config.yml +++ b/config/config.yml @@ -12,6 +12,7 @@ imports: framework: secret: '%env(APP_KERNEL_SECRET)%' + trusted_proxies: '%env(APP_TRUSTED_PROXIES)%' validation: enabled: true router: diff --git a/config/web-interface/config.yml b/config/web-interface/config.yml index bc2e34c5..df27e210 100644 --- a/config/web-interface/config.yml +++ b/config/web-interface/config.yml @@ -22,8 +22,11 @@ security: security: false metrics: pattern: ^/metrics - security: false + stateless: true main: pattern: ^/ custom_authenticators: - 'web-interface.security.arrival_authenticator' + access_control: + - { path: '^/metrics', roles: PUBLIC_ACCESS, ips: ['%env(APP_METRICS_ALLOWED_IPS)%'] } + - { path: '^/metrics', roles: ROLE_NO_ACCESS } diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 3f1aa35b..c7133345 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -6,6 +6,8 @@ x-php-container: environment: APP_ENVIRONMENT: "prod" APP_KERNEL_SECRET: "ThisTokenIsNotSoSecretChangeIt" + APP_TRUSTED_PROXIES: "172.16.0.0/12" + APP_METRICS_ALLOWED_IPS: "172.16.0.0/12" APP_WAIT_FOR: "mysql:3306,redis:6379,rabbit-mq:5672,nchan:81" APP_RABBIT_MQ_DSN: "amqp://guest:guest@rabbit-mq:5672?receive_method=basic_consume&qos_prefetch_count=10&heartbeat=60" APP_CHAT_DOCTRINE_DBAL_URL: "mysqli://root:password@mysql:3306/chat?persistent=1" From fbf275637de51930aadec9d3a9307d2483e16538 Mon Sep 17 00:00:00 2001 From: Markus Reinhold Date: Sat, 25 Mar 2023 11:06:23 +0100 Subject: [PATCH 3/3] Expose metrics int play-with-docker file Open metrics endpoint to any client for simplicity to not maintain the whole ip range of docker on a non-live system. --- docker-compose.production.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docker-compose.production.yml b/docker-compose.production.yml index c7133345..5c1d7221 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -6,8 +6,8 @@ x-php-container: environment: APP_ENVIRONMENT: "prod" APP_KERNEL_SECRET: "ThisTokenIsNotSoSecretChangeIt" - APP_TRUSTED_PROXIES: "172.16.0.0/12" - APP_METRICS_ALLOWED_IPS: "172.16.0.0/12" + APP_TRUSTED_PROXIES: "" + APP_METRICS_ALLOWED_IPS: "0.0.0.0/0" APP_WAIT_FOR: "mysql:3306,redis:6379,rabbit-mq:5672,nchan:81" APP_RABBIT_MQ_DSN: "amqp://guest:guest@rabbit-mq:5672?receive_method=basic_consume&qos_prefetch_count=10&heartbeat=60" APP_CHAT_DOCTRINE_DBAL_URL: "mysqli://root:password@mysql:3306/chat?persistent=1" @@ -133,6 +133,7 @@ services: - "traefik.http.routers.php-fpm.priority=10" - "traefik.http.routers.php-fpm.rule=PathPrefix(`/`)" - "traefik.http.services.php-fpm.loadbalancer.server.port=80" + - "prometheus-job=php-fpm" ############################## # Long running processes #