Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 7e460ec

Browse files
Add a dockerfile for running a set of Synapse worker processes (#9162)
This PR adds a Dockerfile and some supporting files to the `docker/` directory. The Dockerfile's intention is to spin up a container with: * A Synapse main process. * Any desired worker processes, defined by a `SYNAPSE_WORKERS` environment variable supplied at runtime. * A redis for worker communication. * A nginx for routing traffic. * A supervisord to start all worker processes and monitor them if any go down. Note that **this is not currently intended to be used in production**. If you'd like to use Synapse workers with Docker, instead make use of the official image, with one worker per container. The purpose of this dockerfile is currently to allow testing Synapse in worker mode with the [Complement](https://github.com/matrix-org/complement/) test suite. `configure_workers_and_start.py` is where most of the magic happens in this PR. It reads from environment variables (documented in the file) and creates all necessary config files for the processes. It is the entrypoint of the Dockerfile, and thus is run any time the docker container is spun up, recreating all config files in case you want to use a different set of workers. One can specify which workers they'd like to use by setting the `SYNAPSE_WORKERS` environment variable (as a comma-separated list of arbitrary worker names) or by setting it to `*` for all worker processes. We will be using the latter in CI. Huge thanks to @MatMaul for helping get this all working 🎉 This PR is paired with its equivalent on the Complement side: matrix-org/complement#62. Note, for the purpose of testing this PR before it's merged: You'll need to (re)build the base Synapse docker image for everything to work (`matrixdotorg/synapse:latest`). Then build the worker-based docker image on top (`matrixdotorg/synapse:workers`).
1 parent f16c6cf commit 7e460ec

File tree

11 files changed

+867
-6
lines changed

11 files changed

+867
-6
lines changed

changelog.d/9162.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add a dockerfile for running Synapse in worker-mode under Complement.

docker/Dockerfile-workers

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Inherit from the official Synapse docker image
2+
FROM matrixdotorg/synapse
3+
4+
# Install deps
5+
RUN apt-get update
6+
RUN apt-get install -y supervisor redis nginx
7+
8+
# Remove the default nginx sites
9+
RUN rm /etc/nginx/sites-enabled/default
10+
11+
# Copy Synapse worker, nginx and supervisord configuration template files
12+
COPY ./docker/conf-workers/* /conf/
13+
14+
# Expose nginx listener port
15+
EXPOSE 8080/tcp
16+
17+
# Volume for user-editable config files, logs etc.
18+
VOLUME ["/data"]
19+
20+
# A script to read environment variables and create the necessary
21+
# files to run the desired worker configuration. Will start supervisord.
22+
COPY ./docker/configure_workers_and_start.py /configure_workers_and_start.py
23+
ENTRYPOINT ["/configure_workers_and_start.py"]

docker/README-testing.md

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Running tests against a dockerised Synapse
2+
3+
It's possible to run integration tests against Synapse
4+
using [Complement](https://github.com/matrix-org/complement). Complement is a Matrix Spec
5+
compliance test suite for homeservers, and supports any homeserver docker image configured
6+
to listen on ports 8008/8448. This document contains instructions for building Synapse
7+
docker images that can be run inside Complement for testing purposes.
8+
9+
Note that running Synapse's unit tests from within the docker image is not supported.
10+
11+
## Testing with SQLite and single-process Synapse
12+
13+
> Note that `scripts-dev/complement.sh` is a script that will automatically build
14+
> and run an SQLite-based, single-process of Synapse against Complement.
15+
16+
The instructions below will set up Complement testing for a single-process,
17+
SQLite-based Synapse deployment.
18+
19+
Start by building the base Synapse docker image. If you wish to run tests with the latest
20+
release of Synapse, instead of your current checkout, you can skip this step. From the
21+
root of the repository:
22+
23+
```sh
24+
docker build -t matrixdotorg/synapse -f docker/Dockerfile .
25+
```
26+
27+
This will build an image with the tag `matrixdotorg/synapse`.
28+
29+
Next, build the Synapse image for Complement. You will need a local checkout
30+
of Complement. Change to the root of your Complement checkout and run:
31+
32+
```sh
33+
docker build -t complement-synapse -f "dockerfiles/Synapse.Dockerfile" dockerfiles
34+
```
35+
36+
This will build an image with the tag `complement-synapse`, which can be handed to
37+
Complement for testing via the `COMPLEMENT_BASE_IMAGE` environment variable. Refer to
38+
[Complement's documentation](https://github.com/matrix-org/complement/#running) for
39+
how to run the tests, as well as the various available command line flags.
40+
41+
## Testing with PostgreSQL and single or multi-process Synapse
42+
43+
The above docker image only supports running Synapse with SQLite and in a
44+
single-process topology. The following instructions are used to build a Synapse image for
45+
Complement that supports either single or multi-process topology with a PostgreSQL
46+
database backend.
47+
48+
As with the single-process image, build the base Synapse docker image. If you wish to run
49+
tests with the latest release of Synapse, instead of your current checkout, you can skip
50+
this step. From the root of the repository:
51+
52+
```sh
53+
docker build -t matrixdotorg/synapse -f docker/Dockerfile .
54+
```
55+
56+
This will build an image with the tag `matrixdotorg/synapse`.
57+
58+
Next, we build a new image with worker support based on `matrixdotorg/synapse:latest`.
59+
Again, from the root of the repository:
60+
61+
```sh
62+
docker build -t matrixdotorg/synapse-workers -f docker/Dockerfile-workers .
63+
```
64+
65+
This will build an image with the tag` matrixdotorg/synapse-workers`.
66+
67+
It's worth noting at this point that this image is fully functional, and
68+
can be used for testing against locally. See instructions for using the container
69+
under
70+
[Running the Dockerfile-worker image standalone](#running-the-dockerfile-worker-image-standalone)
71+
below.
72+
73+
Finally, build the Synapse image for Complement, which is based on
74+
`matrixdotorg/synapse-workers`. You will need a local checkout of Complement. Change to
75+
the root of your Complement checkout and run:
76+
77+
```sh
78+
docker build -t matrixdotorg/complement-synapse-workers -f dockerfiles/SynapseWorkers.Dockerfile dockerfiles
79+
```
80+
81+
This will build an image with the tag `complement-synapse`, which can be handed to
82+
Complement for testing via the `COMPLEMENT_BASE_IMAGE` environment variable. Refer to
83+
[Complement's documentation](https://github.com/matrix-org/complement/#running) for
84+
how to run the tests, as well as the various available command line flags.
85+
86+
## Running the Dockerfile-worker image standalone
87+
88+
For manual testing of a multi-process Synapse instance in Docker,
89+
[Dockerfile-workers](Dockerfile-workers) is a Dockerfile that will produce an image
90+
bundling all necessary components together for a workerised homeserver instance.
91+
92+
This includes any desired Synapse worker processes, a nginx to route traffic accordingly,
93+
a redis for worker communication and a supervisord instance to start up and monitor all
94+
processes. You will need to provide your own postgres container to connect to, and TLS
95+
is not handled by the container.
96+
97+
Once you've built the image using the above instructions, you can run it. Be sure
98+
you've set up a volume according to the [usual Synapse docker instructions](README.md).
99+
Then run something along the lines of:
100+
101+
```
102+
docker run -d --name synapse \
103+
--mount type=volume,src=synapse-data,dst=/data \
104+
-p 8008:8008 \
105+
-e SYNAPSE_SERVER_NAME=my.matrix.host \
106+
-e SYNAPSE_REPORT_STATS=no \
107+
-e POSTGRES_HOST=postgres \
108+
-e POSTGRES_USER=postgres \
109+
-e POSTGRES_PASSWORD=somesecret \
110+
-e SYNAPSE_WORKER_TYPES=synchrotron,media_repository,user_dir \
111+
-e SYNAPSE_WORKERS_WRITE_LOGS_TO_DISK=1 \
112+
matrixdotorg/synapse-workers
113+
```
114+
115+
...substituting `POSTGRES*` variables for those that match a postgres host you have
116+
available (usually a running postgres docker container).
117+
118+
The `SYNAPSE_WORKER_TYPES` environment variable is a comma-separated list of workers to
119+
use when running the container. All possible worker names are defined by the keys of the
120+
`WORKERS_CONFIG` variable in [this script](configure_workers_and_start.py), which the
121+
Dockerfile makes use of to generate appropriate worker, nginx and supervisord config
122+
files.
123+
124+
Sharding is supported for a subset of workers, in line with the
125+
[worker documentation](../docs/workers.md). To run multiple instances of a given worker
126+
type, simply specify the type multiple times in `SYNAPSE_WORKER_TYPES`
127+
(e.g `SYNAPSE_WORKER_TYPES=event_creator,event_creator...`).
128+
129+
Otherwise, `SYNAPSE_WORKER_TYPES` can either be left empty or unset to spawn no workers
130+
(leaving only the main process). The container is configured to use redis-based worker
131+
mode.
132+
133+
Logs for workers and the main process are logged to stdout and can be viewed with
134+
standard `docker logs` tooling. Worker logs contain their worker name
135+
after the timestamp.
136+
137+
Setting `SYNAPSE_WORKERS_WRITE_LOGS_TO_DISK=1` will cause worker logs to be written to
138+
`<data_dir>/logs/<worker_name>.log`. Logs are kept for 1 week and rotate every day at 00:
139+
00, according to the container's clock. Logging for the main process must still be
140+
configured by modifying the homeserver's log config in your Synapse data volume.

docker/README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,19 @@ is not supported by this image.
1111

1212
## Volumes
1313

14-
By default, the image expects a single volume, located at ``/data``, that will hold:
14+
By default, the image expects a single volume, located at `/data`, that will hold:
1515

1616
* configuration files;
1717
* uploaded media and thumbnails;
1818
* the SQLite database if you do not configure postgres;
1919
* the appservices configuration.
2020

2121
You are free to use separate volumes depending on storage endpoints at your
22-
disposal. For instance, ``/data/media`` could be stored on a large but low
22+
disposal. For instance, `/data/media` could be stored on a large but low
2323
performance hdd storage while other files could be stored on high performance
2424
endpoints.
2525

26-
In order to setup an application service, simply create an ``appservices``
26+
In order to setup an application service, simply create an `appservices`
2727
directory in the data volume and write the application service Yaml
2828
configuration file there. Multiple application services are supported.
2929

@@ -56,6 +56,8 @@ The following environment variables are supported in `generate` mode:
5656
* `SYNAPSE_SERVER_NAME` (mandatory): the server public hostname.
5757
* `SYNAPSE_REPORT_STATS` (mandatory, `yes` or `no`): whether to enable
5858
anonymous statistics reporting.
59+
* `SYNAPSE_HTTP_PORT`: the port Synapse should listen on for http traffic.
60+
Defaults to `8008`.
5961
* `SYNAPSE_CONFIG_DIR`: where additional config files (such as the log config
6062
and event signing key) will be stored. Defaults to `/data`.
6163
* `SYNAPSE_CONFIG_PATH`: path to the file to be generated. Defaults to
@@ -76,6 +78,8 @@ docker run -d --name synapse \
7678
matrixdotorg/synapse:latest
7779
```
7880

81+
(assuming 8008 is the port Synapse is configured to listen on for http traffic.)
82+
7983
You can then check that it has started correctly with:
8084

8185
```
@@ -211,4 +215,4 @@ healthcheck:
211215
## Using jemalloc
212216

213217
Jemalloc is embedded in the image and will be used instead of the default allocator.
214-
You can read about jemalloc by reading the Synapse [README](../README.md)
218+
You can read about jemalloc by reading the Synapse [README](../README.md).

docker/conf-workers/nginx.conf.j2

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# This file contains the base config for the reverse proxy, as part of ../Dockerfile-workers.
2+
# configure_workers_and_start.py uses and amends to this file depending on the workers
3+
# that have been selected.
4+
5+
{{ upstream_directives }}
6+
7+
server {
8+
# Listen on an unoccupied port number
9+
listen 8008;
10+
listen [::]:8008;
11+
12+
server_name localhost;
13+
14+
# Nginx by default only allows file uploads up to 1M in size
15+
# Increase client_max_body_size to match max_upload_size defined in homeserver.yaml
16+
client_max_body_size 100M;
17+
18+
{{ worker_locations }}
19+
20+
# Send all other traffic to the main process
21+
location ~* ^(\\/_matrix|\\/_synapse) {
22+
proxy_pass http://localhost:8080;
23+
proxy_set_header X-Forwarded-For $remote_addr;
24+
proxy_set_header X-Forwarded-Proto $scheme;
25+
proxy_set_header Host $host;
26+
}
27+
}

docker/conf-workers/shared.yaml.j2

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# This file contains the base for the shared homeserver config file between Synapse workers,
2+
# as part of ./Dockerfile-workers.
3+
# configure_workers_and_start.py uses and amends to this file depending on the workers
4+
# that have been selected.
5+
6+
redis:
7+
enabled: true
8+
9+
{{ shared_worker_config }}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# This file contains the base config for supervisord, as part of ../Dockerfile-workers.
2+
# configure_workers_and_start.py uses and amends to this file depending on the workers
3+
# that have been selected.
4+
[supervisord]
5+
nodaemon=true
6+
user=root
7+
8+
[program:nginx]
9+
command=/usr/sbin/nginx -g "daemon off;"
10+
priority=500
11+
stdout_logfile=/dev/stdout
12+
stdout_logfile_maxbytes=0
13+
stderr_logfile=/dev/stderr
14+
stderr_logfile_maxbytes=0
15+
username=www-data
16+
autorestart=true
17+
18+
[program:redis]
19+
command=/usr/bin/redis-server /etc/redis/redis.conf --daemonize no
20+
priority=1
21+
stdout_logfile=/dev/stdout
22+
stdout_logfile_maxbytes=0
23+
stderr_logfile=/dev/stderr
24+
stderr_logfile_maxbytes=0
25+
username=redis
26+
autorestart=true
27+
28+
[program:synapse_main]
29+
command=/usr/local/bin/python -m synapse.app.homeserver --config-path="{{ main_config_path }}" --config-path=/conf/workers/shared.yaml
30+
priority=10
31+
# Log startup failures to supervisord's stdout/err
32+
# Regular synapse logs will still go in the configured data directory
33+
stdout_logfile=/dev/stdout
34+
stdout_logfile_maxbytes=0
35+
stderr_logfile=/dev/stderr
36+
stderr_logfile_maxbytes=0
37+
autorestart=unexpected
38+
exitcodes=0
39+
40+
# Additional process blocks
41+
{{ worker_config }}

docker/conf-workers/worker.yaml.j2

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# This is a configuration template for a single worker instance, and is
2+
# used by Dockerfile-workers.
3+
# Values will be change depending on whichever workers are selected when
4+
# running that image.
5+
6+
worker_app: "{{ app }}"
7+
worker_name: "{{ name }}"
8+
9+
# The replication listener on the main synapse process.
10+
worker_replication_host: 127.0.0.1
11+
worker_replication_http_port: 9093
12+
13+
worker_listeners:
14+
- type: http
15+
port: {{ port }}
16+
{% if listener_resources %}
17+
resources:
18+
- names:
19+
{%- for resource in listener_resources %}
20+
- {{ resource }}
21+
{%- endfor %}
22+
{% endif %}
23+
24+
worker_log_config: {{ worker_log_config_filepath }}
25+
26+
{{ worker_extra_conf }}

docker/conf/homeserver.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ listeners:
4040
compress: false
4141
{% endif %}
4242

43-
- port: 8008
43+
# Allow configuring in case we want to reverse proxy 8008
44+
# using another process in the same container
45+
- port: {{ SYNAPSE_HTTP_PORT or 8008 }}
4446
tls: false
4547
bind_addresses: ['::']
4648
type: http

docker/conf/log.config

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,34 @@ version: 1
22

33
formatters:
44
precise:
5-
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
5+
{% if worker_name %}
6+
format: '%(asctime)s - worker:{{ worker_name }} - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
7+
{% else %}
8+
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
9+
{% endif %}
610

711
handlers:
12+
file:
13+
class: logging.handlers.TimedRotatingFileHandler
14+
formatter: precise
15+
filename: {{ LOG_FILE_PATH or "homeserver.log" }}
16+
when: "midnight"
17+
backupCount: 6 # Does not include the current log file.
18+
encoding: utf8
19+
20+
# Default to buffering writes to log file for efficiency. This means that
21+
# there will be a delay for INFO/DEBUG logs to get written, but WARNING/ERROR
22+
# logs will still be flushed immediately.
23+
buffer:
24+
class: logging.handlers.MemoryHandler
25+
target: file
26+
# The capacity is the number of log lines that are buffered before
27+
# being written to disk. Increasing this will lead to better
28+
# performance, at the expensive of it taking longer for log lines to
29+
# be written to disk.
30+
capacity: 10
31+
flushLevel: 30 # Flush for WARNING logs as well
32+
833
console:
934
class: logging.StreamHandler
1035
formatter: precise
@@ -17,6 +42,11 @@ loggers:
1742

1843
root:
1944
level: {{ SYNAPSE_LOG_LEVEL or "INFO" }}
45+
46+
{% if LOG_FILE_PATH %}
47+
handlers: [console, buffer]
48+
{% else %}
2049
handlers: [console]
50+
{% endif %}
2151

2252
disable_existing_loggers: false

0 commit comments

Comments
 (0)