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

Add a dockerfile for running a set of Synapse worker processes #9162

Merged
merged 44 commits into from
Apr 14, 2021
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
31e9579
Add docker image for Synapse with Workers
anoadragon453 Dec 14, 2020
d61dc37
Prevent an empty resources: list if no listener_resources defined
anoadragon453 Jan 20, 2021
3523080
Add documentation for the dockerfile
anoadragon453 Jan 19, 2021
e66f9e7
Changelog
anoadragon453 Jan 19, 2021
2e3134b
Missing media path for media worker
MatMaul Jan 22, 2021
f6571ee
Actually launch redis through supervisord
MatMaul Jan 22, 2021
3f1b649
Add worker_extra_conf for media repo
MatMaul Jan 22, 2021
8b41fdf
Keep redis in foreground
MatMaul Jan 23, 2021
2d6af88
Reacts on keyboard signals
MatMaul Jan 23, 2021
cb0b272
Move nginx to 8008 and master to 8080
MatMaul Jan 23, 2021
842dc50
lint
anoadragon453 Jan 23, 2021
31cac05
Axe healthcheck TODO
anoadragon453 Feb 1, 2021
fb40f20
Switch to templates instead of inline for process configs
anoadragon453 Feb 2, 2021
e363197
Rename SYNAPSE_PORT to SYNAPSE_HTTP_PORT, explain its use
anoadragon453 Mar 3, 2021
fb83854
Improve wording of single-container worker docker setup
anoadragon453 Mar 3, 2021
4779845
Remove support for '*' to specify all workers
anoadragon453 Mar 3, 2021
b265083
Add support for sharded worker instances
anoadragon453 Mar 4, 2021
e99007d
Add support for a background_worker worker type
anoadragon453 Mar 4, 2021
48b7faf
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/syn…
anoadragon453 Mar 5, 2021
cc9d243
lint
anoadragon453 Mar 5, 2021
04dd483
Explicitly set worker listener resources
anoadragon453 Mar 5, 2021
278579a
Stream writers need a replication listener configured
anoadragon453 Mar 5, 2021
7f5a8ee
Add frontend_proxy, event_creator support
anoadragon453 Mar 8, 2021
609f1c1
Drop whitelist of shardable worker types.
anoadragon453 Mar 8, 2021
f9d7e28
Prefix worker log lines with worker name
anoadragon453 Mar 9, 2021
3ae6108
Explicitly set supervisord to be root, instead of it guessing we want…
anoadragon453 Mar 10, 2021
f39a25b
Add load-balancing support; support sharding media_repo
anoadragon453 Mar 22, 2021
21b3f22
Bail out from configure script if it is run multiple times
anoadragon453 Mar 24, 2021
1711d34
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/syn…
anoadragon453 Mar 25, 2021
c54801a
Add env var SYNAPSE_WORKERS_WRITE_LOGS_TO_DISK
anoadragon453 Mar 25, 2021
0bd42a3
Update documentation for new env var behaviour
anoadragon453 Mar 29, 2021
ea31dc8
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/syn…
anoadragon453 Mar 29, 2021
5a77614
Clarify this is intended for testing purposes
anoadragon453 Apr 6, 2021
69a6399
Move worker setup-specific config template files to a separate dir
anoadragon453 Apr 6, 2021
e3fbd62
Apply suggestions from code review
anoadragon453 Apr 6, 2021
d844d97
Update worker docker image name to matrixdotorg/synapse-workers
anoadragon453 Apr 6, 2021
f218ae9
Move config block section templates outside of 'generate_worker_files
anoadragon453 Apr 6, 2021
a7666d1
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/syn…
anoadragon453 Apr 6, 2021
486d5d1
Move worker docker documentation to README-testing, document Compleme…
anoadragon453 Apr 9, 2021
e47b390
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/syn…
anoadragon453 Apr 13, 2021
700c203
SYNAPSE_WORKERS -> SYNAPSE_WORKER_TYPES
anoadragon453 Apr 13, 2021
314473f
Apply suggestions from code review
anoadragon453 Apr 14, 2021
030b1f4
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/syn…
anoadragon453 Apr 14, 2021
1a37f25
Remove python text encoding delcaration
anoadragon453 Apr 14, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/9162.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add a dockerfile for running Synapse in worker-mode under Complement.
23 changes: 23 additions & 0 deletions docker/Dockerfile-workers
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Inherit from the official Synapse docker image
FROM matrixdotorg/synapse

# Install deps
RUN apt-get update
RUN apt-get install -y supervisor redis nginx

# Remove the default nginx sites
RUN rm /etc/nginx/sites-enabled/default

# Copy Synapse worker, nginx and supervisord configuration template files
COPY ./docker/conf-workers/* /conf/

# Expose nginx listener port
EXPOSE 8080/tcp

# Volume for user-editable config files, logs etc.
VOLUME ["/data"]

# A script to read environment variables and create the necessary
# files to run the desired worker configuration. Will start supervisord.
COPY ./docker/configure_workers_and_start.py /configure_workers_and_start.py
ENTRYPOINT ["/configure_workers_and_start.py"]
136 changes: 136 additions & 0 deletions docker/README-testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# 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.

## Testing with SQLite and single-process Synapse

> Note that `scripts-dev/complement.sh` is a script that will automatically build
> and run an SQLite-based, single-process of Synapse against Complement.

The instructions below will set up Complement testing for a single-process,
SQLite-based Synapse deployment.

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 .
```

This will build an image with the tag `matrixdotorg/synapse`.

Next, build the Synapse image for Complement. You will need a local checkout
of Complement. Change to the root of your Complement checkout and run:

```sh
docker build -t complement-synapse -f "dockerfiles/Synapse.Dockerfile" dockerfiles
```

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.

## Testing with PostgreSQL and single or multi-process Synapse

The above docker image only supports running Synapse with SQLite and in a
single-process topology. The following instructions are used to build a Synapse image for
Complement that supports either single or multi-process topology with a PostgreSQL
database backend.

As with the single-process image, build 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 .
```

This will build an image with the tag `matrixdotorg/synapse`.

Next, we build a new image with worker support based on `matrixdotorg/synapse:latest`.
Again, from the root of the repository:

```sh
docker build -t matrixdotorg/synapse-workers -f docker/Dockerfile-workers .
```

This will build an image with the tag` matrixdotorg/synapse-workers`.

It's worth noting at this point that this image is fully functional, and
can be used for testing against locally. See instructions for using the container
under [Running the Dockerfile-worker image](#Running the Dockerfile-worker image) below.
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

Finally, build the Synapse image for Complement, which is based on
`matrixdotorg/synapse-workers`. You will need a local checkout of Complement. Change to
the root of your Complement checkout and run:

```sh
docker build -t matrixdotorg/complement-synapse-workers -f dockerfiles/SynapseWorkers.Dockerfile dockerfiles
```

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.

## 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 POSTGRES_PASSWORD=somesecret \
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
-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
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
available (usually a running postgres docker container).

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
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
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 is configured to use redis-based worker
mode.

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.
11 changes: 7 additions & 4 deletions docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ The image also does *not* provide a TURN server.

## Volumes

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

* configuration files;
* uploaded media and thumbnails;
* the SQLite database if you do not configure postgres;
* the appservices configuration.

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

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

Expand Down Expand Up @@ -53,6 +53,7 @@ The following environment variables are supported in `generate` mode:
* `SYNAPSE_SERVER_NAME` (mandatory): the server public hostname.
* `SYNAPSE_REPORT_STATS` (mandatory, `yes` or `no`): whether to enable
anonymous statistics reporting.
* `SYNAPSE_HTTP_PORT`: the port Synapse should listen on for http traffic.
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
* `SYNAPSE_CONFIG_DIR`: where additional config files (such as the log config
and event signing key) will be stored. Defaults to `/data`.
* `SYNAPSE_CONFIG_PATH`: path to the file to be generated. Defaults to
Expand All @@ -73,6 +74,8 @@ docker run -d --name synapse \
matrixdotorg/synapse:latest
```

assuming 8008 is the port Synapse is configured to listen on for http traffic.
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

You can then check that it has started correctly with:

```
Expand Down Expand Up @@ -208,4 +211,4 @@ healthcheck:
## Using jemalloc

Jemalloc is embedded in the image and will be used instead of the default allocator.
You can read about jemalloc by reading the Synapse [README](../README.md)
You can read about jemalloc by reading the Synapse [README](../README.md).
27 changes: 27 additions & 0 deletions docker/conf-workers/nginx.conf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# This file contains the base config for the reverse proxy, as part of ../Dockerfile-workers.
# configure_workers_and_start.py uses and amends to this file depending on the workers
# that have been selected.

{{ upstream_directives }}

server {
# Listen on an unoccupied port number
listen 8008;
listen [::]:8008;

server_name localhost;

# Nginx by default only allows file uploads up to 1M in size
# Increase client_max_body_size to match max_upload_size defined in homeserver.yaml
client_max_body_size 100M;

{{ worker_locations }}

# Send all other traffic to the main process
location ~* ^(\\/_matrix|\\/_synapse) {
proxy_pass http://localhost:8080;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
}
}
9 changes: 9 additions & 0 deletions docker/conf-workers/shared.yaml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# This file contains the base for the shared homeserver config file between Synapse workers,
# as part of ./Dockerfile-workers.
# configure_workers_and_start.py uses and amends to this file depending on the workers
# that have been selected.

redis:
enabled: true

{{ shared_worker_config }}
41 changes: 41 additions & 0 deletions docker/conf-workers/supervisord.conf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# This file contains the base config for supervisord, as part of ../Dockerfile-workers.
# configure_workers_and_start.py uses and amends to this file depending on the workers
# that have been selected.
[supervisord]
nodaemon=true
user=root

[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
priority=500
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
username=www-data
autorestart=true

[program:redis]
command=/usr/bin/redis-server /etc/redis/redis.conf --daemonize no
priority=1
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
username=redis
autorestart=true

[program:synapse_main]
command=/usr/local/bin/python -m synapse.app.homeserver --config-path="{{ main_config_path }}" --config-path=/conf/workers/shared.yaml
priority=10
# Log startup failures to supervisord's stdout/err
# Regular synapse logs will still go in the configured data directory
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=unexpected
exitcodes=0

# Additional process blocks
{{ worker_config }}
26 changes: 26 additions & 0 deletions docker/conf-workers/worker.yaml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# This is a configuration template for a single worker instance, and is
# used by Dockerfile-workers.
# Values will be change depending on whichever workers are selected when
# running that image.

worker_app: "{{ app }}"
worker_name: "{{ name }}"

# The replication listener on the main synapse process.
worker_replication_host: 127.0.0.1
worker_replication_http_port: 9093

worker_listeners:
- type: http
port: {{ port }}
{% if listener_resources %}
resources:
- names:
{%- for resource in listener_resources %}
- {{ resource }}
{%- endfor %}
{% endif %}

worker_log_config: {{ worker_log_config_filepath }}

{{ worker_extra_conf }}
4 changes: 3 additions & 1 deletion docker/conf/homeserver.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ listeners:
compress: false
{% endif %}

- port: 8008
# Allow configuring in case we want to reverse proxy 8008
# using another process in the same container
- port: {{ SYNAPSE_HTTP_PORT or 8008 }}
tls: false
bind_addresses: ['::']
type: http
Expand Down
32 changes: 31 additions & 1 deletion docker/conf/log.config
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,34 @@ version: 1

formatters:
precise:
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
{% if worker_name %}
format: '%(asctime)s - worker:{{ worker_name }} - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
{% else %}
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
{% endif %}

handlers:
file:
class: logging.handlers.TimedRotatingFileHandler
formatter: precise
filename: {{ LOG_FILE_PATH or "homeserver.log" }}
when: "midnight"
backupCount: 6 # Does not include the current log file.
encoding: utf8

# Default to buffering writes to log file for efficiency. This means that
# there will be a delay for INFO/DEBUG logs to get written, but WARNING/ERROR
# logs will still be flushed immediately.
buffer:
class: logging.handlers.MemoryHandler
target: file
# The capacity is the number of log lines that are buffered before
# being written to disk. Increasing this will lead to better
# performance, at the expensive of it taking longer for log lines to
# be written to disk.
capacity: 10
flushLevel: 30 # Flush for WARNING logs as well

console:
class: logging.StreamHandler
formatter: precise
Expand All @@ -17,6 +42,11 @@ loggers:

root:
level: {{ SYNAPSE_LOG_LEVEL or "INFO" }}

{% if LOG_FILE_PATH %}
handlers: [console, buffer]
{% else %}
handlers: [console]
{% endif %}

disable_existing_loggers: false
Loading