Add Prometheus [HTTP service discovery](https://prometheus.io/docs/prometheus/latest/http_sd/) endpoint for easy discovery of all workers in Docker image. Follow-up to https://github.com/element-hq/synapse/pull/19324 Spawning from wanting to [run a load test](https://github.com/element-hq/synapse-rust-apps/pull/397) against the Complement Docker image of Synapse and see metrics from the homeserver. `GET http://<synapse_container>:9469/metrics/service_discovery` ```json5 [ { "targets": [ "<host>", ... ], "labels": { "<labelname>": "<labelvalue>", ... } }, ... ] ``` The metrics from each worker can also be accessed via `http://<synapse_container>:9469/metrics/worker/<worker_name>` which is what the service discovery response points to behind the scenes. This way, you only need to expose a single port (9469) to access all metrics. <details> <summary>Real HTTP service discovery response</summary> ```json5 [ { "targets": [ "localhost:9469" ], "labels": { "job": "event_persister", "index": "1", "__metrics_path__": "/metrics/worker/event_persister1" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "event_persister", "index": "2", "__metrics_path__": "/metrics/worker/event_persister2" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "background_worker", "index": "1", "__metrics_path__": "/metrics/worker/background_worker1" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "event_creator", "index": "1", "__metrics_path__": "/metrics/worker/event_creator1" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "user_dir", "index": "1", "__metrics_path__": "/metrics/worker/user_dir1" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "media_repository", "index": "1", "__metrics_path__": "/metrics/worker/media_repository1" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "federation_inbound", "index": "1", "__metrics_path__": "/metrics/worker/federation_inbound1" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "federation_reader", "index": "1", "__metrics_path__": "/metrics/worker/federation_reader1" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "federation_sender", "index": "1", "__metrics_path__": "/metrics/worker/federation_sender1" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "synchrotron", "index": "1", "__metrics_path__": "/metrics/worker/synchrotron1" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "client_reader", "index": "1", "__metrics_path__": "/metrics/worker/client_reader1" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "appservice", "index": "1", "__metrics_path__": "/metrics/worker/appservice1" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "pusher", "index": "1", "__metrics_path__": "/metrics/worker/pusher1" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "device_lists", "index": "1", "__metrics_path__": "/metrics/worker/device_lists1" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "device_lists", "index": "2", "__metrics_path__": "/metrics/worker/device_lists2" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "stream_writers", "index": "1", "__metrics_path__": "/metrics/worker/stream_writers1" } }, { "targets": [ "localhost:9469" ], "labels": { "job": "main", "index": "1", "__metrics_path__": "/metrics/worker/main" } } ] ``` </details> And how it ends up as targets in Prometheus (http://localhost:9090/targets): (image) ### Testing strategy 1. Make sure your firewall allows the Docker containers to communicate to the host (`host.docker.internal`) so they can access exposed ports of other Docker containers. We want to allow Synapse to access the Prometheus container and Grafana to access to the Prometheus container. - `sudo ufw allow in on docker0 comment "Allow traffic from the default Docker network to the host machine (host.docker.internal)"` - `sudo ufw allow in on br-+ comment "(from Matrix Complement testing) Allow traffic from custom Docker networks to the host machine (host.docker.internal)"` - [Complement firewall docs](ee6acd9154/README.md (potential-conflict-with-firewall-software)) 1. Build the Docker image for Synapse: `docker build -t matrixdotorg/synapse -f docker/Dockerfile . && docker build -t matrixdotorg/synapse-workers -f docker/Dockerfile-workers .` ([docs](7a24fafbc3/docker/README-testing.md (building-and-running-the-images-manually))) 1. Start Synapse: ``` docker run -d --name synapse \ --mount type=volume,src=synapse-data,dst=/data \ -e SYNAPSE_SERVER_NAME=my.docker.synapse.server \ -e SYNAPSE_REPORT_STATS=no \ -e SYNAPSE_ENABLE_METRICS=1 \ -p 8008:8008 \ -p 9469:9469 \ matrixdotorg/synapse-workers:latest ``` - Also try with workers: ``` docker run -d --name synapse \ --mount type=volume,src=synapse-data,dst=/data \ -e SYNAPSE_SERVER_NAME=my.docker.synapse.server \ -e SYNAPSE_REPORT_STATS=no \ -e SYNAPSE_ENABLE_METRICS=1 \ -e SYNAPSE_WORKER_TYPES="\ event_persister:2, \ background_worker, \ event_creator, \ user_dir, \ media_repository, \ federation_inbound, \ federation_reader, \ federation_sender, \ synchrotron, \ client_reader, \ appservice, \ pusher, \ device_lists:2, \ stream_writers=account_data+presence+receipts+to_device+typing" \ -p 8008:8008 \ -p 9469:9469 \ matrixdotorg/synapse-workers:latest ``` 1. You should be able to see Prometheus service discovery endpoint at http://localhost:9469/metrics/service_discovery 1. Create a Prometheus config (`prometheus.yml`) ```yaml global: scrape_interval: 15s scrape_timeout: 15s evaluation_interval: 15s scrape_configs: - job_name: synapse scrape_interval: 15s metrics_path: /_synapse/metrics scheme: http # We set `honor_labels` so that each service can set their own `job` label # # > honor_labels controls how Prometheus handles conflicts between labels that are # > already present in scraped data and labels that Prometheus would attach # > server-side ("job" and "instance" labels, manually configured target # > labels, and labels generated by service discovery implementations). # > # > *-- https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config* honor_labels: true # Use HTTP service discovery # # Reference: # - https://prometheus.io/docs/prometheus/latest/http_sd/ # - https://prometheus.io/docs/prometheus/latest/configuration/configuration/#http_sd_config http_sd_configs: - url: 'http://localhost:9469/metrics/service_discovery' ``` 1. Start Prometheus (update the volume bind mount to the config you just saved somewhere): ``` docker run \ --detach \ --name=prometheus \ --add-host host.docker.internal:host-gateway \ -p 9090:9090 \ -v ~/Documents/code/random/prometheus-config/prometheus.yml:/etc/prometheus/prometheus.yml \ prom/prometheus ``` 1. Make sure you're seeing some data in Prometheus. On http://localhost:9090/query, search for `synapse_build_info` 1. Start [Grafana](https://hub.docker.com/r/grafana/grafana) ``` docker run -d --name=grafana --add-host host.docker.internal:host-gateway -p 3000:3000 grafana/grafana ``` 1. Visit the Grafana dashboard, http://localhost:3000/ (Credentials: `admin`/`admin`) 1. **Connections** -> **Data Sources** -> **Add data source** -> **Prometheus** - Prometheus server URL: `http://host.docker.internal:9090` 1. Import the Synapse dashboard: https://github.com/element-hq/synapse/blob/develop/contrib/grafana/synapse.json
184 lines
7.6 KiB
Markdown
184 lines
7.6 KiB
Markdown
# Running tests against a dockerised Synapse
|
|
|
|
It's possible to run integration tests against Synapse
|
|
using [Complement](https://github.com/matrix-org/complement). Complement is a Matrix Spec
|
|
compliance test suite for homeservers, and supports any homeserver docker image configured
|
|
to listen on ports 8008/8448. This document contains instructions for building Synapse
|
|
docker images that can be run inside Complement for testing purposes.
|
|
|
|
Note that running Synapse's unit tests from within the docker image is not supported.
|
|
|
|
## Using the Complement launch script
|
|
|
|
`scripts-dev/complement.sh` is a script that will automatically build
|
|
and run Synapse against Complement.
|
|
Consult the [contributing guide][guideComplementSh] for instructions on how to use it.
|
|
|
|
|
|
[guideComplementSh]: https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-integration-tests-complement
|
|
|
|
## Building and running the images manually
|
|
|
|
Under some circumstances, you may wish to build the images manually.
|
|
The instructions below will lead you to doing that.
|
|
|
|
Note that these images can only be built using [BuildKit](https://docs.docker.com/develop/develop-images/build_enhancements/),
|
|
therefore BuildKit needs to be enabled when calling `docker build`. This can be done by
|
|
setting `DOCKER_BUILDKIT=1` in your environment.
|
|
|
|
Start by building the base Synapse docker image. If you wish to run tests with the latest
|
|
release of Synapse, instead of your current checkout, you can skip this step. From the
|
|
root of the repository:
|
|
|
|
```sh
|
|
docker build -t matrixdotorg/synapse -f docker/Dockerfile .
|
|
```
|
|
|
|
Next, build the workerised Synapse docker image, which is a layer over the base
|
|
image.
|
|
|
|
```sh
|
|
docker build -t matrixdotorg/synapse-workers -f docker/Dockerfile-workers .
|
|
```
|
|
|
|
Finally, build the multi-purpose image for Complement, which is a layer over the workers image.
|
|
|
|
```sh
|
|
docker build -t complement-synapse -f docker/complement/Dockerfile docker/complement
|
|
```
|
|
|
|
This will build an image with the tag `complement-synapse`, which can be handed to
|
|
Complement for testing via the `COMPLEMENT_BASE_IMAGE` environment variable. Refer to
|
|
[Complement's documentation](https://github.com/matrix-org/complement/#running) for
|
|
how to run the tests, as well as the various available command line flags.
|
|
|
|
See [the Complement image README](./complement/README.md) for information about the
|
|
expected environment variables.
|
|
|
|
|
|
## Running the Dockerfile-worker image standalone
|
|
|
|
For manual testing of a multi-process Synapse instance in Docker,
|
|
[Dockerfile-workers](Dockerfile-workers) is a Dockerfile that will produce an image
|
|
bundling all necessary components together for a workerised homeserver instance.
|
|
|
|
This includes any desired Synapse worker processes, a nginx to route traffic accordingly,
|
|
a redis for worker communication and a supervisord instance to start up and monitor all
|
|
processes. You will need to provide your own postgres container to connect to, and TLS
|
|
is not handled by the container.
|
|
|
|
Once you've built the image using the above instructions, you can run it. Be sure
|
|
you've set up a volume according to the [usual Synapse docker instructions](README.md).
|
|
Then run something along the lines of:
|
|
|
|
```
|
|
docker run -d --name synapse \
|
|
--mount type=volume,src=synapse-data,dst=/data \
|
|
-p 8008:8008 \
|
|
-e SYNAPSE_SERVER_NAME=my.matrix.host \
|
|
-e SYNAPSE_REPORT_STATS=no \
|
|
-e POSTGRES_HOST=postgres \
|
|
-e POSTGRES_USER=postgres \
|
|
-e POSTGRES_PASSWORD=somesecret \
|
|
-e SYNAPSE_WORKER_TYPES=synchrotron,media_repository,user_dir \
|
|
-e SYNAPSE_WORKERS_WRITE_LOGS_TO_DISK=1 \
|
|
matrixdotorg/synapse-workers
|
|
```
|
|
|
|
...substituting `POSTGRES*` variables for those that match a postgres host you have
|
|
available (usually a running postgres docker container).
|
|
|
|
|
|
### Workers
|
|
|
|
The `SYNAPSE_WORKER_TYPES` environment variable is a comma-separated list of workers to
|
|
use when running the container. All possible worker names are defined by the keys of the
|
|
`WORKERS_CONFIG` variable in [this script](configure_workers_and_start.py), which the
|
|
Dockerfile makes use of to generate appropriate worker, nginx and supervisord config
|
|
files.
|
|
|
|
Sharding is supported for a subset of workers, in line with the
|
|
[worker documentation](../docs/workers.md). To run multiple instances of a given worker
|
|
type, simply specify the type multiple times in `SYNAPSE_WORKER_TYPES`
|
|
(e.g `SYNAPSE_WORKER_TYPES=event_creator,event_creator...`).
|
|
|
|
Otherwise, `SYNAPSE_WORKER_TYPES` can either be left empty or unset to spawn no workers
|
|
(leaving only the main process).
|
|
The container will only be configured to use Redis-based worker mode if there are
|
|
workers enabled.
|
|
|
|
### Logging
|
|
|
|
Logs for workers and the main process are logged to stdout and can be viewed with
|
|
standard `docker logs` tooling. Worker logs contain their worker name
|
|
after the timestamp.
|
|
|
|
Setting `SYNAPSE_WORKERS_WRITE_LOGS_TO_DISK=1` will cause worker logs to be written to
|
|
`<data_dir>/logs/<worker_name>.log`. Logs are kept for 1 week and rotate every day at 00:
|
|
00, according to the container's clock. Logging for the main process must still be
|
|
configured by modifying the homeserver's log config in your Synapse data volume.
|
|
|
|
|
|
### Application Services
|
|
|
|
Setting the `SYNAPSE_AS_REGISTRATION_DIR` environment variable to the path of
|
|
a directory (within the container) will cause the configuration script to scan
|
|
that directory for `.yaml`/`.yml` registration files.
|
|
Synapse will be configured to load these configuration files.
|
|
|
|
|
|
### TLS Termination
|
|
|
|
Nginx is present in the image to route requests to the appropriate workers,
|
|
but it does not serve TLS by default.
|
|
|
|
You can configure `SYNAPSE_TLS_CERT` and `SYNAPSE_TLS_KEY` to point to a
|
|
TLS certificate and key (respectively), both in PEM (textual) format.
|
|
In this case, Nginx will additionally serve using HTTPS on port 8448.
|
|
|
|
|
|
### Metrics
|
|
|
|
Set `SYNAPSE_ENABLE_METRICS=1` to configure `enable_metrics: true` and setup the
|
|
`metrics` listener on the main and worker processes. Defaults to `0` (disabled). The
|
|
main process will listen on port `19090` and workers on port `19091 + <worker index>`.
|
|
|
|
When using `docker/Dockerfile-workers`, to ease the complexity with the metrics setup,
|
|
we also have a [Prometheus HTTP service
|
|
discovery](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#http_sd_config)
|
|
endpoint available at `http://<synapse_container>:9469/metrics/service_discovery`.
|
|
|
|
The metrics from each worker can also be accessed via
|
|
`http://<synapse_container>:9469/metrics/worker/<worker_name>` which is what the service
|
|
discovery response points to behind the scenes. This way, you only need to expose a
|
|
single port (9469) to access all metrics.
|
|
|
|
```yaml
|
|
global:
|
|
scrape_interval: 15s
|
|
scrape_timeout: 15s
|
|
evaluation_interval: 15s
|
|
|
|
scrape_configs:
|
|
- job_name: synapse
|
|
scrape_interval: 15s
|
|
metrics_path: /_synapse/metrics
|
|
scheme: http
|
|
# We set `honor_labels` so that each service can set their own `job`/`instance` label
|
|
#
|
|
# > honor_labels controls how Prometheus handles conflicts between labels that are
|
|
# > already present in scraped data and labels that Prometheus would attach
|
|
# > server-side ("job" and "instance" labels, manually configured target
|
|
# > labels, and labels generated by service discovery implementations).
|
|
# >
|
|
# > *-- https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config*
|
|
honor_labels: true
|
|
# Use HTTP service discovery
|
|
#
|
|
# Reference:
|
|
# - https://prometheus.io/docs/prometheus/latest/http_sd/
|
|
# - https://prometheus.io/docs/prometheus/latest/configuration/configuration/#http_sd_config
|
|
http_sd_configs:
|
|
- url: 'http://localhost:9469/metrics/service_discovery'
|
|
```
|