From 3c3f02d55791e830acb26411bc644ad39db4bbb1 Mon Sep 17 00:00:00 2001 From: Pietro Brenna Date: Wed, 13 May 2020 12:36:47 +0200 Subject: [PATCH] Aggiunto build system docker --- .dockerignore | 3 + Dockerfile | 70 + Jenkinsfile | 15 + docker/.env | 8 + docker/configs/app/config.js | 84 ++ docker/configs/redis/redis.conf | 1372 ++++++++++++++++++++ docker/configs/server/config.js | 158 +++ docker/deployment/.env | 8 + docker/deployment/README.md | 63 + docker/deployment/configs/app/config.js | 84 ++ docker/deployment/configs/redis/redis.conf | 1372 ++++++++++++++++++++ docker/deployment/configs/server/config.js | 158 +++ docker/deployment/docker-compose.yml | 49 + docker/deployment/images/background.jpg | Bin 0 -> 50043 bytes docker/deployment/images/logo.svg | 79 ++ docker/docker-entrypoint.sh | 14 + docker/images/background.jpg | Bin 0 -> 50043 bytes docker/images/logo.svg | 79 ++ 18 files changed, 3616 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 Jenkinsfile create mode 100644 docker/.env create mode 100644 docker/configs/app/config.js create mode 100644 docker/configs/redis/redis.conf create mode 100644 docker/configs/server/config.js create mode 100644 docker/deployment/.env create mode 100644 docker/deployment/README.md create mode 100644 docker/deployment/configs/app/config.js create mode 100644 docker/deployment/configs/redis/redis.conf create mode 100644 docker/deployment/configs/server/config.js create mode 100644 docker/deployment/docker-compose.yml create mode 100644 docker/deployment/images/background.jpg create mode 100644 docker/deployment/images/logo.svg create mode 100755 docker/docker-entrypoint.sh create mode 100644 docker/images/background.jpg create mode 100644 docker/images/logo.svg diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c706d33 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +*/node_modules +*/build +server/public \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c99799f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,70 @@ +#FROM node:lts-alpine AS mm-builder +FROM node:10-slim AS mm-builder + + +# Args +ARG BASEDIR=/opt +ARG MM=multiparty-meeting +ARG NODE_ENV=production +ARG SERVER_DEBUG='' +ARG REACT_APP_DEBUG='' + + +#RUN apk add --no-cache git bash +RUN apt-get update;apt-get install -y git bash + +WORKDIR ${BASEDIR} +COPY server ${BASEDIR}/${MM}/server + +#install server dep +WORKDIR ${BASEDIR}/${MM}/server + +#RUN apk add --no-cache git build-base python linux-headers +RUN apt-get install -y git build-essential python + +RUN npm install + + +COPY app ${BASEDIR}/${MM}/app +#install app dep +WORKDIR ${BASEDIR}/${MM}/app +RUN npm install + +# set app in producion mode/minified/. +ENV NODE_ENV ${NODE_ENV} + +# Workaround for the next yarn run build => rm -rf public dir even if it does not exists. +# TODO: Fix it smarter +RUN mkdir -p ${BASEDIR}/${MM}/server/public + +ENV REACT_APP_DEBUG=${REACT_APP_DEBUG} + +# package web app +RUN npm run build + + +#FROM node:lts-alpine +FROM node:10-slim + +# Args +ARG BASEDIR=/opt +ARG MM=multiparty-meeting +ARG NODE_ENV=production +ARG SERVER_DEBUG='' + +WORKDIR ${BASEDIR} + +COPY --from=mm-builder ${BASEDIR}/${MM}/server ${BASEDIR}/${MM}/server + + + +# Web PORTS +EXPOSE 80 443 +EXPOSE 40000-49999/udp + + +## run server +ENV DEBUG ${SERVER_DEBUG} + +COPY docker/docker-entrypoint.sh / +ENTRYPOINT ["/docker-entrypoint.sh"] diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..4963015 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,15 @@ +node { + stage('Checkout') { + checkout scm + } + stage('Build docker') { + customImage = docker.build("public/mm:${env.BUILD_ID}") + } + stage('Push to registry') { + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { + docker.withRegistry('https://docker.briq.it', 'briq-docker-cred') { + customImage.push("${env.BRANCH_NAME}") + } + } + } +} diff --git a/docker/.env b/docker/.env new file mode 100644 index 0000000..ab1244b --- /dev/null +++ b/docker/.env @@ -0,0 +1,8 @@ +BASEDIR=/opt +MM=multiparty-meeting +NODE_ENV=production +SERVER_DEBUG= +#SERVER_DEBUG=mediasoup* +TAG=latest +BRANCH=master +#REACT_APP_DEBUG='*' diff --git a/docker/configs/app/config.js b/docker/configs/app/config.js new file mode 100644 index 0000000..f897ea2 --- /dev/null +++ b/docker/configs/app/config.js @@ -0,0 +1,84 @@ +// eslint-disable-next-line +var config = +{ + loginEnabled : false, + developmentPort : 3443, + productionPort : 443, + multipartyServer : 'fqdn', + turnServers : [ + { + urls : [ + 'turn:example.com:443?transport=tcp' + ], + username : 'example', + credential : 'example' + } + ], + /** + * If defaultResolution is set, it will override user settings when joining: + * low ~ 320x240 + * medium ~ 640x480 + * high ~ 1280x720 + * veryhigh ~ 1920x1080 + * ultra ~ 3840x2560 + **/ + defaultResolution : 'medium', + // Enable or disable simulcast for webcam video + simulcast : true, + // Enable or disable simulcast for screen sharing video + simulcastSharing : false, + // Simulcast encoding layers and levels + simulcastEncodings : + [ + { scaleResolutionDownBy: 4 }, + { scaleResolutionDownBy: 2 }, + { scaleResolutionDownBy: 1 } + ], + // Socket.io request timeout + requestTimeout : 10000, + transportOptions : + { + tcp : true + }, + lastN : 4, + mobileLastN : 1, + background : 'images/background.jpg', + // Add file and uncomment for adding logo to appbar + // logo : 'images/logo.svg', + title : 'Multiparty meeting', + theme : + { + palette : + { + primary : + { + main : '#313131' + } + }, + overrides : + { + MuiAppBar : + { + colorPrimary : + { + backgroundColor : '#313131' + } + }, + MuiFab : + { + primary : + { + backgroundColor : '#5F9B2D', + '&:hover' : + { + backgroundColor : '#518029' + } + } + } + }, + typography : + { + useNextVariants : true + } + } +}; diff --git a/docker/configs/redis/redis.conf b/docker/configs/redis/redis.conf new file mode 100644 index 0000000..d74ff98 --- /dev/null +++ b/docker/configs/redis/redis.conf @@ -0,0 +1,1372 @@ +# Redis configuration file example. +# +# Note that in order to read the configuration file, Redis must be +# started with the file path as first argument: +# +# ./redis-server /path/to/redis.conf + +# Note on units: when memory size is needed, it is possible to specify +# it in the usual form of 1k 5GB 4M and so forth: +# +# 1k => 1000 bytes +# 1kb => 1024 bytes +# 1m => 1000000 bytes +# 1mb => 1024*1024 bytes +# 1g => 1000000000 bytes +# 1gb => 1024*1024*1024 bytes +# +# units are case insensitive so 1GB 1Gb 1gB are all the same. + +################################## INCLUDES ################################### + +# Include one or more other config files here. This is useful if you +# have a standard template that goes to all Redis servers but also need +# to customize a few per-server settings. Include files can include +# other files, so use this wisely. +# +# Notice option "include" won't be rewritten by command "CONFIG REWRITE" +# from admin or Redis Sentinel. Since Redis always uses the last processed +# line as value of a configuration directive, you'd better put includes +# at the beginning of this file to avoid overwriting config change at runtime. +# +# If instead you are interested in using includes to override configuration +# options, it is better to use include as the last line. +# +# include /path/to/local.conf +# include /path/to/other.conf + +################################## MODULES ##################################### + +# Load modules at startup. If the server is not able to load modules +# it will abort. It is possible to use multiple loadmodule directives. +# +# loadmodule /path/to/my_module.so +# loadmodule /path/to/other_module.so + +################################## NETWORK ##################################### + +# By default, if no "bind" configuration directive is specified, Redis listens +# for connections from all the network interfaces available on the server. +# It is possible to listen to just one or multiple selected interfaces using +# the "bind" configuration directive, followed by one or more IP addresses. +# +# Examples: +# +# bind 192.168.1.100 10.0.0.1 +# bind 127.0.0.1 ::1 +# +# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the +# internet, binding to all the interfaces is dangerous and will expose the +# instance to everybody on the internet. So by default we uncomment the +# following bind directive, that will force Redis to listen only into +# the IPv4 loopback interface address (this means Redis will be able to +# accept connections only from clients running into the same computer it +# is running). +# +# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES +# JUST COMMENT THE FOLLOWING LINE. +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bind 127.0.0.1 + +# Protected mode is a layer of security protection, in order to avoid that +# Redis instances left open on the internet are accessed and exploited. +# +# When protected mode is on and if: +# +# 1) The server is not binding explicitly to a set of addresses using the +# "bind" directive. +# 2) No password is configured. +# +# The server only accepts connections from clients connecting from the +# IPv4 and IPv6 loopback addresses 127.0.0.1 and ::1, and from Unix domain +# sockets. +# +# By default protected mode is enabled. You should disable it only if +# you are sure you want clients from other hosts to connect to Redis +# even if no authentication is configured, nor a specific set of interfaces +# are explicitly listed using the "bind" directive. +protected-mode yes + +# Accept connections on the specified port, default is 6379 (IANA #815344). +# If port 0 is specified Redis will not listen on a TCP socket. +port 6379 + +# TCP listen() backlog. +# +# In high requests-per-second environments you need an high backlog in order +# to avoid slow clients connections issues. Note that the Linux kernel +# will silently truncate it to the value of /proc/sys/net/core/somaxconn so +# make sure to raise both the value of somaxconn and tcp_max_syn_backlog +# in order to get the desired effect. +tcp-backlog 511 + +# Unix socket. +# +# Specify the path for the Unix socket that will be used to listen for +# incoming connections. There is no default, so Redis will not listen +# on a unix socket when not specified. +# +# unixsocket /tmp/redis.sock +# unixsocketperm 700 + +# Close the connection after a client is idle for N seconds (0 to disable) +timeout 0 + +# TCP keepalive. +# +# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence +# of communication. This is useful for two reasons: +# +# 1) Detect dead peers. +# 2) Take the connection alive from the point of view of network +# equipment in the middle. +# +# On Linux, the specified value (in seconds) is the period used to send ACKs. +# Note that to close the connection the double of the time is needed. +# On other kernels the period depends on the kernel configuration. +# +# A reasonable value for this option is 300 seconds, which is the new +# Redis default starting with Redis 3.2.1. +tcp-keepalive 300 + +################################# GENERAL ##################################### + +# By default Redis does not run as a daemon. Use 'yes' if you need it. +# Note that Redis will write a pid file in /var/run/redis.pid when daemonized. +daemonize no + +# If you run Redis from upstart or systemd, Redis can interact with your +# supervision tree. Options: +# supervised no - no supervision interaction +# supervised upstart - signal upstart by putting Redis into SIGSTOP mode +# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET +# supervised auto - detect upstart or systemd method based on +# UPSTART_JOB or NOTIFY_SOCKET environment variables +# Note: these supervision methods only signal "process is ready." +# They do not enable continuous liveness pings back to your supervisor. +supervised no + +# If a pid file is specified, Redis writes it where specified at startup +# and removes it at exit. +# +# When the server runs non daemonized, no pid file is created if none is +# specified in the configuration. When the server is daemonized, the pid file +# is used even if not specified, defaulting to "/var/run/redis.pid". +# +# Creating a pid file is best effort: if Redis is not able to create it +# nothing bad happens, the server will start and run normally. +pidfile /var/run/redis_6379.pid + +# Specify the server verbosity level. +# This can be one of: +# debug (a lot of information, useful for development/testing) +# verbose (many rarely useful info, but not a mess like the debug level) +# notice (moderately verbose, what you want in production probably) +# warning (only very important / critical messages are logged) +loglevel notice + +# Specify the log file name. Also the empty string can be used to force +# Redis to log on the standard output. Note that if you use standard +# output for logging but daemonize, logs will be sent to /dev/null +logfile "" + +# To enable logging to the system logger, just set 'syslog-enabled' to yes, +# and optionally update the other syslog parameters to suit your needs. +# syslog-enabled no + +# Specify the syslog identity. +# syslog-ident redis + +# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. +# syslog-facility local0 + +# Set the number of databases. The default database is DB 0, you can select +# a different one on a per-connection basis using SELECT where +# dbid is a number between 0 and 'databases'-1 +databases 16 + +# By default Redis shows an ASCII art logo only when started to log to the +# standard output and if the standard output is a TTY. Basically this means +# that normally a logo is displayed only in interactive sessions. +# +# However it is possible to force the pre-4.0 behavior and always show a +# ASCII art logo in startup logs by setting the following option to yes. +always-show-logo yes + +################################ SNAPSHOTTING ################################ +# +# Save the DB on disk: +# +# save +# +# Will save the DB if both the given number of seconds and the given +# number of write operations against the DB occurred. +# +# In the example below the behaviour will be to save: +# after 900 sec (15 min) if at least 1 key changed +# after 300 sec (5 min) if at least 10 keys changed +# after 60 sec if at least 10000 keys changed +# +# Note: you can disable saving completely by commenting out all "save" lines. +# +# It is also possible to remove all the previously configured save +# points by adding a save directive with a single empty string argument +# like in the following example: +# +# save "" + +save 900 1 +save 300 10 +save 60 10000 + +# By default Redis will stop accepting writes if RDB snapshots are enabled +# (at least one save point) and the latest background save failed. +# This will make the user aware (in a hard way) that data is not persisting +# on disk properly, otherwise chances are that no one will notice and some +# disaster will happen. +# +# If the background saving process will start working again Redis will +# automatically allow writes again. +# +# However if you have setup your proper monitoring of the Redis server +# and persistence, you may want to disable this feature so that Redis will +# continue to work as usual even if there are problems with disk, +# permissions, and so forth. +stop-writes-on-bgsave-error yes + +# Compress string objects using LZF when dump .rdb databases? +# For default that's set to 'yes' as it's almost always a win. +# If you want to save some CPU in the saving child set it to 'no' but +# the dataset will likely be bigger if you have compressible values or keys. +rdbcompression yes + +# Since version 5 of RDB a CRC64 checksum is placed at the end of the file. +# This makes the format more resistant to corruption but there is a performance +# hit to pay (around 10%) when saving and loading RDB files, so you can disable it +# for maximum performances. +# +# RDB files created with checksum disabled have a checksum of zero that will +# tell the loading code to skip the check. +rdbchecksum yes + +# The filename where to dump the DB +dbfilename dump.rdb + +# The working directory. +# +# The DB will be written inside this directory, with the filename specified +# above using the 'dbfilename' configuration directive. +# +# The Append Only File will also be created inside this directory. +# +# Note that you must specify a directory here, not a file name. +dir ./ + +################################# REPLICATION ################################# + +# Master-Replica replication. Use replicaof to make a Redis instance a copy of +# another Redis server. A few things to understand ASAP about Redis replication. +# +# +------------------+ +---------------+ +# | Master | ---> | Replica | +# | (receive writes) | | (exact copy) | +# +------------------+ +---------------+ +# +# 1) Redis replication is asynchronous, but you can configure a master to +# stop accepting writes if it appears to be not connected with at least +# a given number of replicas. +# 2) Redis replicas are able to perform a partial resynchronization with the +# master if the replication link is lost for a relatively small amount of +# time. You may want to configure the replication backlog size (see the next +# sections of this file) with a sensible value depending on your needs. +# 3) Replication is automatic and does not need user intervention. After a +# network partition replicas automatically try to reconnect to masters +# and resynchronize with them. +# +# replicaof + +# If the master is password protected (using the "requirepass" configuration +# directive below) it is possible to tell the replica to authenticate before +# starting the replication synchronization process, otherwise the master will +# refuse the replica request. +# +# masterauth + +# When a replica loses its connection with the master, or when the replication +# is still in progress, the replica can act in two different ways: +# +# 1) if replica-serve-stale-data is set to 'yes' (the default) the replica will +# still reply to client requests, possibly with out of date data, or the +# data set may just be empty if this is the first synchronization. +# +# 2) if replica-serve-stale-data is set to 'no' the replica will reply with +# an error "SYNC with master in progress" to all the kind of commands +# but to INFO, replicaOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG, +# SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, +# COMMAND, POST, HOST: and LATENCY. +# +replica-serve-stale-data yes + +# You can configure a replica instance to accept writes or not. Writing against +# a replica instance may be useful to store some ephemeral data (because data +# written on a replica will be easily deleted after resync with the master) but +# may also cause problems if clients are writing to it because of a +# misconfiguration. +# +# Since Redis 2.6 by default replicas are read-only. +# +# Note: read only replicas are not designed to be exposed to untrusted clients +# on the internet. It's just a protection layer against misuse of the instance. +# Still a read only replica exports by default all the administrative commands +# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve +# security of read only replicas using 'rename-command' to shadow all the +# administrative / dangerous commands. +replica-read-only yes + +# Replication SYNC strategy: disk or socket. +# +# ------------------------------------------------------- +# WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY +# ------------------------------------------------------- +# +# New replicas and reconnecting replicas that are not able to continue the replication +# process just receiving differences, need to do what is called a "full +# synchronization". An RDB file is transmitted from the master to the replicas. +# The transmission can happen in two different ways: +# +# 1) Disk-backed: The Redis master creates a new process that writes the RDB +# file on disk. Later the file is transferred by the parent +# process to the replicas incrementally. +# 2) Diskless: The Redis master creates a new process that directly writes the +# RDB file to replica sockets, without touching the disk at all. +# +# With disk-backed replication, while the RDB file is generated, more replicas +# can be queued and served with the RDB file as soon as the current child producing +# the RDB file finishes its work. With diskless replication instead once +# the transfer starts, new replicas arriving will be queued and a new transfer +# will start when the current one terminates. +# +# When diskless replication is used, the master waits a configurable amount of +# time (in seconds) before starting the transfer in the hope that multiple replicas +# will arrive and the transfer can be parallelized. +# +# With slow disks and fast (large bandwidth) networks, diskless replication +# works better. +repl-diskless-sync no + +# When diskless replication is enabled, it is possible to configure the delay +# the server waits in order to spawn the child that transfers the RDB via socket +# to the replicas. +# +# This is important since once the transfer starts, it is not possible to serve +# new replicas arriving, that will be queued for the next RDB transfer, so the server +# waits a delay in order to let more replicas arrive. +# +# The delay is specified in seconds, and by default is 5 seconds. To disable +# it entirely just set it to 0 seconds and the transfer will start ASAP. +repl-diskless-sync-delay 5 + +# Replicas send PINGs to server in a predefined interval. It's possible to change +# this interval with the repl_ping_replica_period option. The default value is 10 +# seconds. +# +# repl-ping-replica-period 10 + +# The following option sets the replication timeout for: +# +# 1) Bulk transfer I/O during SYNC, from the point of view of replica. +# 2) Master timeout from the point of view of replicas (data, pings). +# 3) Replica timeout from the point of view of masters (REPLCONF ACK pings). +# +# It is important to make sure that this value is greater than the value +# specified for repl-ping-replica-period otherwise a timeout will be detected +# every time there is low traffic between the master and the replica. +# +# repl-timeout 60 + +# Disable TCP_NODELAY on the replica socket after SYNC? +# +# If you select "yes" Redis will use a smaller number of TCP packets and +# less bandwidth to send data to replicas. But this can add a delay for +# the data to appear on the replica side, up to 40 milliseconds with +# Linux kernels using a default configuration. +# +# If you select "no" the delay for data to appear on the replica side will +# be reduced but more bandwidth will be used for replication. +# +# By default we optimize for low latency, but in very high traffic conditions +# or when the master and replicas are many hops away, turning this to "yes" may +# be a good idea. +repl-disable-tcp-nodelay no + +# Set the replication backlog size. The backlog is a buffer that accumulates +# replica data when replicas are disconnected for some time, so that when a replica +# wants to reconnect again, often a full resync is not needed, but a partial +# resync is enough, just passing the portion of data the replica missed while +# disconnected. +# +# The bigger the replication backlog, the longer the time the replica can be +# disconnected and later be able to perform a partial resynchronization. +# +# The backlog is only allocated once there is at least a replica connected. +# +# repl-backlog-size 1mb + +# After a master has no longer connected replicas for some time, the backlog +# will be freed. The following option configures the amount of seconds that +# need to elapse, starting from the time the last replica disconnected, for +# the backlog buffer to be freed. +# +# Note that replicas never free the backlog for timeout, since they may be +# promoted to masters later, and should be able to correctly "partially +# resynchronize" with the replicas: hence they should always accumulate backlog. +# +# A value of 0 means to never release the backlog. +# +# repl-backlog-ttl 3600 + +# The replica priority is an integer number published by Redis in the INFO output. +# It is used by Redis Sentinel in order to select a replica to promote into a +# master if the master is no longer working correctly. +# +# A replica with a low priority number is considered better for promotion, so +# for instance if there are three replicas with priority 10, 100, 25 Sentinel will +# pick the one with priority 10, that is the lowest. +# +# However a special priority of 0 marks the replica as not able to perform the +# role of master, so a replica with priority of 0 will never be selected by +# Redis Sentinel for promotion. +# +# By default the priority is 100. +replica-priority 100 + +# It is possible for a master to stop accepting writes if there are less than +# N replicas connected, having a lag less or equal than M seconds. +# +# The N replicas need to be in "online" state. +# +# The lag in seconds, that must be <= the specified value, is calculated from +# the last ping received from the replica, that is usually sent every second. +# +# This option does not GUARANTEE that N replicas will accept the write, but +# will limit the window of exposure for lost writes in case not enough replicas +# are available, to the specified number of seconds. +# +# For example to require at least 3 replicas with a lag <= 10 seconds use: +# +# min-replicas-to-write 3 +# min-replicas-max-lag 10 +# +# Setting one or the other to 0 disables the feature. +# +# By default min-replicas-to-write is set to 0 (feature disabled) and +# min-replicas-max-lag is set to 10. + +# A Redis master is able to list the address and port of the attached +# replicas in different ways. For example the "INFO replication" section +# offers this information, which is used, among other tools, by +# Redis Sentinel in order to discover replica instances. +# Another place where this info is available is in the output of the +# "ROLE" command of a master. +# +# The listed IP and address normally reported by a replica is obtained +# in the following way: +# +# IP: The address is auto detected by checking the peer address +# of the socket used by the replica to connect with the master. +# +# Port: The port is communicated by the replica during the replication +# handshake, and is normally the port that the replica is using to +# listen for connections. +# +# However when port forwarding or Network Address Translation (NAT) is +# used, the replica may be actually reachable via different IP and port +# pairs. The following two options can be used by a replica in order to +# report to its master a specific set of IP and port, so that both INFO +# and ROLE will report those values. +# +# There is no need to use both the options if you need to override just +# the port or the IP address. +# +# replica-announce-ip 5.5.5.5 +# replica-announce-port 1234 + +################################## SECURITY ################################### + +# Require clients to issue AUTH before processing any other +# commands. This might be useful in environments in which you do not trust +# others with access to the host running redis-server. +# +# This should stay commented out for backward compatibility and because most +# people do not need auth (e.g. they run their own servers). +# +# Warning: since Redis is pretty fast an outside user can try up to +# 150k passwords per second against a good box. This means that you should +# use a very strong password otherwise it will be very easy to break. +# +# requirepass foobared + +# Command renaming. +# +# It is possible to change the name of dangerous commands in a shared +# environment. For instance the CONFIG command may be renamed into something +# hard to guess so that it will still be available for internal-use tools +# but not available for general clients. +# +# Example: +# +# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 +# +# It is also possible to completely kill a command by renaming it into +# an empty string: +# +# rename-command CONFIG "" +# +# Please note that changing the name of commands that are logged into the +# AOF file or transmitted to replicas may cause problems. + +################################### CLIENTS #################################### + +# Set the max number of connected clients at the same time. By default +# this limit is set to 10000 clients, however if the Redis server is not +# able to configure the process file limit to allow for the specified limit +# the max number of allowed clients is set to the current file limit +# minus 32 (as Redis reserves a few file descriptors for internal uses). +# +# Once the limit is reached Redis will close all the new connections sending +# an error 'max number of clients reached'. +# +# maxclients 10000 + +############################## MEMORY MANAGEMENT ################################ + +# Set a memory usage limit to the specified amount of bytes. +# When the memory limit is reached Redis will try to remove keys +# according to the eviction policy selected (see maxmemory-policy). +# +# If Redis can't remove keys according to the policy, or if the policy is +# set to 'noeviction', Redis will start to reply with errors to commands +# that would use more memory, like SET, LPUSH, and so on, and will continue +# to reply to read-only commands like GET. +# +# This option is usually useful when using Redis as an LRU or LFU cache, or to +# set a hard memory limit for an instance (using the 'noeviction' policy). +# +# WARNING: If you have replicas attached to an instance with maxmemory on, +# the size of the output buffers needed to feed the replicas are subtracted +# from the used memory count, so that network problems / resyncs will +# not trigger a loop where keys are evicted, and in turn the output +# buffer of replicas is full with DELs of keys evicted triggering the deletion +# of more keys, and so forth until the database is completely emptied. +# +# In short... if you have replicas attached it is suggested that you set a lower +# limit for maxmemory so that there is some free RAM on the system for replica +# output buffers (but this is not needed if the policy is 'noeviction'). +# +# maxmemory + +# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory +# is reached. You can select among five behaviors: +# +# volatile-lru -> Evict using approximated LRU among the keys with an expire set. +# allkeys-lru -> Evict any key using approximated LRU. +# volatile-lfu -> Evict using approximated LFU among the keys with an expire set. +# allkeys-lfu -> Evict any key using approximated LFU. +# volatile-random -> Remove a random key among the ones with an expire set. +# allkeys-random -> Remove a random key, any key. +# volatile-ttl -> Remove the key with the nearest expire time (minor TTL) +# noeviction -> Don't evict anything, just return an error on write operations. +# +# LRU means Least Recently Used +# LFU means Least Frequently Used +# +# Both LRU, LFU and volatile-ttl are implemented using approximated +# randomized algorithms. +# +# Note: with any of the above policies, Redis will return an error on write +# operations, when there are no suitable keys for eviction. +# +# At the date of writing these commands are: set setnx setex append +# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd +# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby +# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby +# getset mset msetnx exec sort +# +# The default is: +# +# maxmemory-policy noeviction + +# LRU, LFU and minimal TTL algorithms are not precise algorithms but approximated +# algorithms (in order to save memory), so you can tune it for speed or +# accuracy. For default Redis will check five keys and pick the one that was +# used less recently, you can change the sample size using the following +# configuration directive. +# +# The default of 5 produces good enough results. 10 Approximates very closely +# true LRU but costs more CPU. 3 is faster but not very accurate. +# +# maxmemory-samples 5 + +# Starting from Redis 5, by default a replica will ignore its maxmemory setting +# (unless it is promoted to master after a failover or manually). It means +# that the eviction of keys will be just handled by the master, sending the +# DEL commands to the replica as keys evict in the master side. +# +# This behavior ensures that masters and replicas stay consistent, and is usually +# what you want, however if your replica is writable, or you want the replica to have +# a different memory setting, and you are sure all the writes performed to the +# replica are idempotent, then you may change this default (but be sure to understand +# what you are doing). +# +# Note that since the replica by default does not evict, it may end using more +# memory than the one set via maxmemory (there are certain buffers that may +# be larger on the replica, or data structures may sometimes take more memory and so +# forth). So make sure you monitor your replicas and make sure they have enough +# memory to never hit a real out-of-memory condition before the master hits +# the configured maxmemory setting. +# +# replica-ignore-maxmemory yes + +############################# LAZY FREEING #################################### + +# Redis has two primitives to delete keys. One is called DEL and is a blocking +# deletion of the object. It means that the server stops processing new commands +# in order to reclaim all the memory associated with an object in a synchronous +# way. If the key deleted is associated with a small object, the time needed +# in order to execute the DEL command is very small and comparable to most other +# O(1) or O(log_N) commands in Redis. However if the key is associated with an +# aggregated value containing millions of elements, the server can block for +# a long time (even seconds) in order to complete the operation. +# +# For the above reasons Redis also offers non blocking deletion primitives +# such as UNLINK (non blocking DEL) and the ASYNC option of FLUSHALL and +# FLUSHDB commands, in order to reclaim memory in background. Those commands +# are executed in constant time. Another thread will incrementally free the +# object in the background as fast as possible. +# +# DEL, UNLINK and ASYNC option of FLUSHALL and FLUSHDB are user-controlled. +# It's up to the design of the application to understand when it is a good +# idea to use one or the other. However the Redis server sometimes has to +# delete keys or flush the whole database as a side effect of other operations. +# Specifically Redis deletes objects independently of a user call in the +# following scenarios: +# +# 1) On eviction, because of the maxmemory and maxmemory policy configurations, +# in order to make room for new data, without going over the specified +# memory limit. +# 2) Because of expire: when a key with an associated time to live (see the +# EXPIRE command) must be deleted from memory. +# 3) Because of a side effect of a command that stores data on a key that may +# already exist. For example the RENAME command may delete the old key +# content when it is replaced with another one. Similarly SUNIONSTORE +# or SORT with STORE option may delete existing keys. The SET command +# itself removes any old content of the specified key in order to replace +# it with the specified string. +# 4) During replication, when a replica performs a full resynchronization with +# its master, the content of the whole database is removed in order to +# load the RDB file just transferred. +# +# In all the above cases the default is to delete objects in a blocking way, +# like if DEL was called. However you can configure each case specifically +# in order to instead release memory in a non-blocking way like if UNLINK +# was called, using the following configuration directives: + +lazyfree-lazy-eviction no +lazyfree-lazy-expire no +lazyfree-lazy-server-del no +replica-lazy-flush no + +############################## APPEND ONLY MODE ############################### + +# By default Redis asynchronously dumps the dataset on disk. This mode is +# good enough in many applications, but an issue with the Redis process or +# a power outage may result into a few minutes of writes lost (depending on +# the configured save points). +# +# The Append Only File is an alternative persistence mode that provides +# much better durability. For instance using the default data fsync policy +# (see later in the config file) Redis can lose just one second of writes in a +# dramatic event like a server power outage, or a single write if something +# wrong with the Redis process itself happens, but the operating system is +# still running correctly. +# +# AOF and RDB persistence can be enabled at the same time without problems. +# If the AOF is enabled on startup Redis will load the AOF, that is the file +# with the better durability guarantees. +# +# Please check http://redis.io/topics/persistence for more information. + +appendonly no + +# The name of the append only file (default: "appendonly.aof") + +appendfilename "appendonly.aof" + +# The fsync() call tells the Operating System to actually write data on disk +# instead of waiting for more data in the output buffer. Some OS will really flush +# data on disk, some other OS will just try to do it ASAP. +# +# Redis supports three different modes: +# +# no: don't fsync, just let the OS flush the data when it wants. Faster. +# always: fsync after every write to the append only log. Slow, Safest. +# everysec: fsync only one time every second. Compromise. +# +# The default is "everysec", as that's usually the right compromise between +# speed and data safety. It's up to you to understand if you can relax this to +# "no" that will let the operating system flush the output buffer when +# it wants, for better performances (but if you can live with the idea of +# some data loss consider the default persistence mode that's snapshotting), +# or on the contrary, use "always" that's very slow but a bit safer than +# everysec. +# +# More details please check the following article: +# http://antirez.com/post/redis-persistence-demystified.html +# +# If unsure, use "everysec". + +# appendfsync always +appendfsync everysec +# appendfsync no + +# When the AOF fsync policy is set to always or everysec, and a background +# saving process (a background save or AOF log background rewriting) is +# performing a lot of I/O against the disk, in some Linux configurations +# Redis may block too long on the fsync() call. Note that there is no fix for +# this currently, as even performing fsync in a different thread will block +# our synchronous write(2) call. +# +# In order to mitigate this problem it's possible to use the following option +# that will prevent fsync() from being called in the main process while a +# BGSAVE or BGREWRITEAOF is in progress. +# +# This means that while another child is saving, the durability of Redis is +# the same as "appendfsync none". In practical terms, this means that it is +# possible to lose up to 30 seconds of log in the worst scenario (with the +# default Linux settings). +# +# If you have latency problems turn this to "yes". Otherwise leave it as +# "no" that is the safest pick from the point of view of durability. + +no-appendfsync-on-rewrite no + +# Automatic rewrite of the append only file. +# Redis is able to automatically rewrite the log file implicitly calling +# BGREWRITEAOF when the AOF log size grows by the specified percentage. +# +# This is how it works: Redis remembers the size of the AOF file after the +# latest rewrite (if no rewrite has happened since the restart, the size of +# the AOF at startup is used). +# +# This base size is compared to the current size. If the current size is +# bigger than the specified percentage, the rewrite is triggered. Also +# you need to specify a minimal size for the AOF file to be rewritten, this +# is useful to avoid rewriting the AOF file even if the percentage increase +# is reached but it is still pretty small. +# +# Specify a percentage of zero in order to disable the automatic AOF +# rewrite feature. + +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb + +# An AOF file may be found to be truncated at the end during the Redis +# startup process, when the AOF data gets loaded back into memory. +# This may happen when the system where Redis is running +# crashes, especially when an ext4 filesystem is mounted without the +# data=ordered option (however this can't happen when Redis itself +# crashes or aborts but the operating system still works correctly). +# +# Redis can either exit with an error when this happens, or load as much +# data as possible (the default now) and start if the AOF file is found +# to be truncated at the end. The following option controls this behavior. +# +# If aof-load-truncated is set to yes, a truncated AOF file is loaded and +# the Redis server starts emitting a log to inform the user of the event. +# Otherwise if the option is set to no, the server aborts with an error +# and refuses to start. When the option is set to no, the user requires +# to fix the AOF file using the "redis-check-aof" utility before to restart +# the server. +# +# Note that if the AOF file will be found to be corrupted in the middle +# the server will still exit with an error. This option only applies when +# Redis will try to read more data from the AOF file but not enough bytes +# will be found. +aof-load-truncated yes + +# When rewriting the AOF file, Redis is able to use an RDB preamble in the +# AOF file for faster rewrites and recoveries. When this option is turned +# on the rewritten AOF file is composed of two different stanzas: +# +# [RDB file][AOF tail] +# +# When loading Redis recognizes that the AOF file starts with the "REDIS" +# string and loads the prefixed RDB file, and continues loading the AOF +# tail. +aof-use-rdb-preamble yes + +################################ LUA SCRIPTING ############################### + +# Max execution time of a Lua script in milliseconds. +# +# If the maximum execution time is reached Redis will log that a script is +# still in execution after the maximum allowed time and will start to +# reply to queries with an error. +# +# When a long running script exceeds the maximum execution time only the +# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be +# used to stop a script that did not yet called write commands. The second +# is the only way to shut down the server in the case a write command was +# already issued by the script but the user doesn't want to wait for the natural +# termination of the script. +# +# Set it to 0 or a negative value for unlimited execution without warnings. +lua-time-limit 5000 + +################################ REDIS CLUSTER ############################### + +# Normal Redis instances can't be part of a Redis Cluster; only nodes that are +# started as cluster nodes can. In order to start a Redis instance as a +# cluster node enable the cluster support uncommenting the following: +# +# cluster-enabled yes + +# Every cluster node has a cluster configuration file. This file is not +# intended to be edited by hand. It is created and updated by Redis nodes. +# Every Redis Cluster node requires a different cluster configuration file. +# Make sure that instances running in the same system do not have +# overlapping cluster configuration file names. +# +# cluster-config-file nodes-6379.conf + +# Cluster node timeout is the amount of milliseconds a node must be unreachable +# for it to be considered in failure state. +# Most other internal time limits are multiple of the node timeout. +# +# cluster-node-timeout 15000 + +# A replica of a failing master will avoid to start a failover if its data +# looks too old. +# +# There is no simple way for a replica to actually have an exact measure of +# its "data age", so the following two checks are performed: +# +# 1) If there are multiple replicas able to failover, they exchange messages +# in order to try to give an advantage to the replica with the best +# replication offset (more data from the master processed). +# Replicas will try to get their rank by offset, and apply to the start +# of the failover a delay proportional to their rank. +# +# 2) Every single replica computes the time of the last interaction with +# its master. This can be the last ping or command received (if the master +# is still in the "connected" state), or the time that elapsed since the +# disconnection with the master (if the replication link is currently down). +# If the last interaction is too old, the replica will not try to failover +# at all. +# +# The point "2" can be tuned by user. Specifically a replica will not perform +# the failover if, since the last interaction with the master, the time +# elapsed is greater than: +# +# (node-timeout * replica-validity-factor) + repl-ping-replica-period +# +# So for example if node-timeout is 30 seconds, and the replica-validity-factor +# is 10, and assuming a default repl-ping-replica-period of 10 seconds, the +# replica will not try to failover if it was not able to talk with the master +# for longer than 310 seconds. +# +# A large replica-validity-factor may allow replicas with too old data to failover +# a master, while a too small value may prevent the cluster from being able to +# elect a replica at all. +# +# For maximum availability, it is possible to set the replica-validity-factor +# to a value of 0, which means, that replicas will always try to failover the +# master regardless of the last time they interacted with the master. +# (However they'll always try to apply a delay proportional to their +# offset rank). +# +# Zero is the only value able to guarantee that when all the partitions heal +# the cluster will always be able to continue. +# +# cluster-replica-validity-factor 10 + +# Cluster replicas are able to migrate to orphaned masters, that are masters +# that are left without working replicas. This improves the cluster ability +# to resist to failures as otherwise an orphaned master can't be failed over +# in case of failure if it has no working replicas. +# +# Replicas migrate to orphaned masters only if there are still at least a +# given number of other working replicas for their old master. This number +# is the "migration barrier". A migration barrier of 1 means that a replica +# will migrate only if there is at least 1 other working replica for its master +# and so forth. It usually reflects the number of replicas you want for every +# master in your cluster. +# +# Default is 1 (replicas migrate only if their masters remain with at least +# one replica). To disable migration just set it to a very large value. +# A value of 0 can be set but is useful only for debugging and dangerous +# in production. +# +# cluster-migration-barrier 1 + +# By default Redis Cluster nodes stop accepting queries if they detect there +# is at least an hash slot uncovered (no available node is serving it). +# This way if the cluster is partially down (for example a range of hash slots +# are no longer covered) all the cluster becomes, eventually, unavailable. +# It automatically returns available as soon as all the slots are covered again. +# +# However sometimes you want the subset of the cluster which is working, +# to continue to accept queries for the part of the key space that is still +# covered. In order to do so, just set the cluster-require-full-coverage +# option to no. +# +# cluster-require-full-coverage yes + +# This option, when set to yes, prevents replicas from trying to failover its +# master during master failures. However the master can still perform a +# manual failover, if forced to do so. +# +# This is useful in different scenarios, especially in the case of multiple +# data center operations, where we want one side to never be promoted if not +# in the case of a total DC failure. +# +# cluster-replica-no-failover no + +# In order to setup your cluster make sure to read the documentation +# available at http://redis.io web site. + +########################## CLUSTER DOCKER/NAT support ######################## + +# In certain deployments, Redis Cluster nodes address discovery fails, because +# addresses are NAT-ted or because ports are forwarded (the typical case is +# Docker and other containers). +# +# In order to make Redis Cluster working in such environments, a static +# configuration where each node knows its public address is needed. The +# following two options are used for this scope, and are: +# +# * cluster-announce-ip +# * cluster-announce-port +# * cluster-announce-bus-port +# +# Each instruct the node about its address, client port, and cluster message +# bus port. The information is then published in the header of the bus packets +# so that other nodes will be able to correctly map the address of the node +# publishing the information. +# +# If the above options are not used, the normal Redis Cluster auto-detection +# will be used instead. +# +# Note that when remapped, the bus port may not be at the fixed offset of +# clients port + 10000, so you can specify any port and bus-port depending +# on how they get remapped. If the bus-port is not set, a fixed offset of +# 10000 will be used as usually. +# +# Example: +# +# cluster-announce-ip 10.1.1.5 +# cluster-announce-port 6379 +# cluster-announce-bus-port 6380 + +################################## SLOW LOG ################################### + +# The Redis Slow Log is a system to log queries that exceeded a specified +# execution time. The execution time does not include the I/O operations +# like talking with the client, sending the reply and so forth, +# but just the time needed to actually execute the command (this is the only +# stage of command execution where the thread is blocked and can not serve +# other requests in the meantime). +# +# You can configure the slow log with two parameters: one tells Redis +# what is the execution time, in microseconds, to exceed in order for the +# command to get logged, and the other parameter is the length of the +# slow log. When a new command is logged the oldest one is removed from the +# queue of logged commands. + +# The following time is expressed in microseconds, so 1000000 is equivalent +# to one second. Note that a negative number disables the slow log, while +# a value of zero forces the logging of every command. +slowlog-log-slower-than 10000 + +# There is no limit to this length. Just be aware that it will consume memory. +# You can reclaim memory used by the slow log with SLOWLOG RESET. +slowlog-max-len 128 + +################################ LATENCY MONITOR ############################## + +# The Redis latency monitoring subsystem samples different operations +# at runtime in order to collect data related to possible sources of +# latency of a Redis instance. +# +# Via the LATENCY command this information is available to the user that can +# print graphs and obtain reports. +# +# The system only logs operations that were performed in a time equal or +# greater than the amount of milliseconds specified via the +# latency-monitor-threshold configuration directive. When its value is set +# to zero, the latency monitor is turned off. +# +# By default latency monitoring is disabled since it is mostly not needed +# if you don't have latency issues, and collecting data has a performance +# impact, that while very small, can be measured under big load. Latency +# monitoring can easily be enabled at runtime using the command +# "CONFIG SET latency-monitor-threshold " if needed. +latency-monitor-threshold 0 + +############################# EVENT NOTIFICATION ############################## + +# Redis can notify Pub/Sub clients about events happening in the key space. +# This feature is documented at http://redis.io/topics/notifications +# +# For instance if keyspace events notification is enabled, and a client +# performs a DEL operation on key "foo" stored in the Database 0, two +# messages will be published via Pub/Sub: +# +# PUBLISH __keyspace@0__:foo del +# PUBLISH __keyevent@0__:del foo +# +# It is possible to select the events that Redis will notify among a set +# of classes. Every class is identified by a single character: +# +# K Keyspace events, published with __keyspace@__ prefix. +# E Keyevent events, published with __keyevent@__ prefix. +# g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ... +# $ String commands +# l List commands +# s Set commands +# h Hash commands +# z Sorted set commands +# x Expired events (events generated every time a key expires) +# e Evicted events (events generated when a key is evicted for maxmemory) +# A Alias for g$lshzxe, so that the "AKE" string means all the events. +# +# The "notify-keyspace-events" takes as argument a string that is composed +# of zero or multiple characters. The empty string means that notifications +# are disabled. +# +# Example: to enable list and generic events, from the point of view of the +# event name, use: +# +# notify-keyspace-events Elg +# +# Example 2: to get the stream of the expired keys subscribing to channel +# name __keyevent@0__:expired use: +# +# notify-keyspace-events Ex +# +# By default all notifications are disabled because most users don't need +# this feature and the feature has some overhead. Note that if you don't +# specify at least one of K or E, no events will be delivered. +notify-keyspace-events "" + +############################### ADVANCED CONFIG ############################### + +# Hashes are encoded using a memory efficient data structure when they have a +# small number of entries, and the biggest entry does not exceed a given +# threshold. These thresholds can be configured using the following directives. +hash-max-ziplist-entries 512 +hash-max-ziplist-value 64 + +# Lists are also encoded in a special way to save a lot of space. +# The number of entries allowed per internal list node can be specified +# as a fixed maximum size or a maximum number of elements. +# For a fixed maximum size, use -5 through -1, meaning: +# -5: max size: 64 Kb <-- not recommended for normal workloads +# -4: max size: 32 Kb <-- not recommended +# -3: max size: 16 Kb <-- probably not recommended +# -2: max size: 8 Kb <-- good +# -1: max size: 4 Kb <-- good +# Positive numbers mean store up to _exactly_ that number of elements +# per list node. +# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size), +# but if your use case is unique, adjust the settings as necessary. +list-max-ziplist-size -2 + +# Lists may also be compressed. +# Compress depth is the number of quicklist ziplist nodes from *each* side of +# the list to *exclude* from compression. The head and tail of the list +# are always uncompressed for fast push/pop operations. Settings are: +# 0: disable all list compression +# 1: depth 1 means "don't start compressing until after 1 node into the list, +# going from either the head or tail" +# So: [head]->node->node->...->node->[tail] +# [head], [tail] will always be uncompressed; inner nodes will compress. +# 2: [head]->[next]->node->node->...->node->[prev]->[tail] +# 2 here means: don't compress head or head->next or tail->prev or tail, +# but compress all nodes between them. +# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail] +# etc. +list-compress-depth 0 + +# Sets have a special encoding in just one case: when a set is composed +# of just strings that happen to be integers in radix 10 in the range +# of 64 bit signed integers. +# The following configuration setting sets the limit in the size of the +# set in order to use this special memory saving encoding. +set-max-intset-entries 512 + +# Similarly to hashes and lists, sorted sets are also specially encoded in +# order to save a lot of space. This encoding is only used when the length and +# elements of a sorted set are below the following limits: +zset-max-ziplist-entries 128 +zset-max-ziplist-value 64 + +# HyperLogLog sparse representation bytes limit. The limit includes the +# 16 bytes header. When an HyperLogLog using the sparse representation crosses +# this limit, it is converted into the dense representation. +# +# A value greater than 16000 is totally useless, since at that point the +# dense representation is more memory efficient. +# +# The suggested value is ~ 3000 in order to have the benefits of +# the space efficient encoding without slowing down too much PFADD, +# which is O(N) with the sparse encoding. The value can be raised to +# ~ 10000 when CPU is not a concern, but space is, and the data set is +# composed of many HyperLogLogs with cardinality in the 0 - 15000 range. +hll-sparse-max-bytes 3000 + +# Streams macro node max size / items. The stream data structure is a radix +# tree of big nodes that encode multiple items inside. Using this configuration +# it is possible to configure how big a single node can be in bytes, and the +# maximum number of items it may contain before switching to a new node when +# appending new stream entries. If any of the following settings are set to +# zero, the limit is ignored, so for instance it is possible to set just a +# max entires limit by setting max-bytes to 0 and max-entries to the desired +# value. +stream-node-max-bytes 4096 +stream-node-max-entries 100 + +# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in +# order to help rehashing the main Redis hash table (the one mapping top-level +# keys to values). The hash table implementation Redis uses (see dict.c) +# performs a lazy rehashing: the more operation you run into a hash table +# that is rehashing, the more rehashing "steps" are performed, so if the +# server is idle the rehashing is never complete and some more memory is used +# by the hash table. +# +# The default is to use this millisecond 10 times every second in order to +# actively rehash the main dictionaries, freeing memory when possible. +# +# If unsure: +# use "activerehashing no" if you have hard latency requirements and it is +# not a good thing in your environment that Redis can reply from time to time +# to queries with 2 milliseconds delay. +# +# use "activerehashing yes" if you don't have such hard requirements but +# want to free memory asap when possible. +activerehashing yes + +# The client output buffer limits can be used to force disconnection of clients +# that are not reading data from the server fast enough for some reason (a +# common reason is that a Pub/Sub client can't consume messages as fast as the +# publisher can produce them). +# +# The limit can be set differently for the three different classes of clients: +# +# normal -> normal clients including MONITOR clients +# replica -> replica clients +# pubsub -> clients subscribed to at least one pubsub channel or pattern +# +# The syntax of every client-output-buffer-limit directive is the following: +# +# client-output-buffer-limit +# +# A client is immediately disconnected once the hard limit is reached, or if +# the soft limit is reached and remains reached for the specified number of +# seconds (continuously). +# So for instance if the hard limit is 32 megabytes and the soft limit is +# 16 megabytes / 10 seconds, the client will get disconnected immediately +# if the size of the output buffers reach 32 megabytes, but will also get +# disconnected if the client reaches 16 megabytes and continuously overcomes +# the limit for 10 seconds. +# +# By default normal clients are not limited because they don't receive data +# without asking (in a push way), but just after a request, so only +# asynchronous clients may create a scenario where data is requested faster +# than it can read. +# +# Instead there is a default limit for pubsub and replica clients, since +# subscribers and replicas receive data in a push fashion. +# +# Both the hard or the soft limit can be disabled by setting them to zero. +client-output-buffer-limit normal 0 0 0 +client-output-buffer-limit replica 256mb 64mb 60 +client-output-buffer-limit pubsub 32mb 8mb 60 + +# Client query buffers accumulate new commands. They are limited to a fixed +# amount by default in order to avoid that a protocol desynchronization (for +# instance due to a bug in the client) will lead to unbound memory usage in +# the query buffer. However you can configure it here if you have very special +# needs, such us huge multi/exec requests or alike. +# +# client-query-buffer-limit 1gb + +# In the Redis protocol, bulk requests, that are, elements representing single +# strings, are normally limited ot 512 mb. However you can change this limit +# here. +# +# proto-max-bulk-len 512mb + +# Redis calls an internal function to perform many background tasks, like +# closing connections of clients in timeout, purging expired keys that are +# never requested, and so forth. +# +# Not all tasks are performed with the same frequency, but Redis checks for +# tasks to perform according to the specified "hz" value. +# +# By default "hz" is set to 10. Raising the value will use more CPU when +# Redis is idle, but at the same time will make Redis more responsive when +# there are many keys expiring at the same time, and timeouts may be +# handled with more precision. +# +# The range is between 1 and 500, however a value over 100 is usually not +# a good idea. Most users should use the default of 10 and raise this up to +# 100 only in environments where very low latency is required. +hz 10 + +# Normally it is useful to have an HZ value which is proportional to the +# number of clients connected. This is useful in order, for instance, to +# avoid too many clients are processed for each background task invocation +# in order to avoid latency spikes. +# +# Since the default HZ value by default is conservatively set to 10, Redis +# offers, and enables by default, the ability to use an adaptive HZ value +# which will temporary raise when there are many connected clients. +# +# When dynamic HZ is enabled, the actual configured HZ will be used as +# as a baseline, but multiples of the configured HZ value will be actually +# used as needed once more clients are connected. In this way an idle +# instance will use very little CPU time while a busy instance will be +# more responsive. +dynamic-hz yes + +# When a child rewrites the AOF file, if the following option is enabled +# the file will be fsync-ed every 32 MB of data generated. This is useful +# in order to commit the file to the disk more incrementally and avoid +# big latency spikes. +aof-rewrite-incremental-fsync yes + +# When redis saves RDB file, if the following option is enabled +# the file will be fsync-ed every 32 MB of data generated. This is useful +# in order to commit the file to the disk more incrementally and avoid +# big latency spikes. +rdb-save-incremental-fsync yes + +# Redis LFU eviction (see maxmemory setting) can be tuned. However it is a good +# idea to start with the default settings and only change them after investigating +# how to improve the performances and how the keys LFU change over time, which +# is possible to inspect via the OBJECT FREQ command. +# +# There are two tunable parameters in the Redis LFU implementation: the +# counter logarithm factor and the counter decay time. It is important to +# understand what the two parameters mean before changing them. +# +# The LFU counter is just 8 bits per key, it's maximum value is 255, so Redis +# uses a probabilistic increment with logarithmic behavior. Given the value +# of the old counter, when a key is accessed, the counter is incremented in +# this way: +# +# 1. A random number R between 0 and 1 is extracted. +# 2. A probability P is calculated as 1/(old_value*lfu_log_factor+1). +# 3. The counter is incremented only if R < P. +# +# The default lfu-log-factor is 10. This is a table of how the frequency +# counter changes with a different number of accesses with different +# logarithmic factors: +# +# +--------+------------+------------+------------+------------+------------+ +# | factor | 100 hits | 1000 hits | 100K hits | 1M hits | 10M hits | +# +--------+------------+------------+------------+------------+------------+ +# | 0 | 104 | 255 | 255 | 255 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# | 1 | 18 | 49 | 255 | 255 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# | 10 | 10 | 18 | 142 | 255 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# | 100 | 8 | 11 | 49 | 143 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# +# NOTE: The above table was obtained by running the following commands: +# +# redis-benchmark -n 1000000 incr foo +# redis-cli object freq foo +# +# NOTE 2: The counter initial value is 5 in order to give new objects a chance +# to accumulate hits. +# +# The counter decay time is the time, in minutes, that must elapse in order +# for the key counter to be divided by two (or decremented if it has a value +# less <= 10). +# +# The default value for the lfu-decay-time is 1. A Special value of 0 means to +# decay the counter every time it happens to be scanned. +# +# lfu-log-factor 10 +# lfu-decay-time 1 + +########################### ACTIVE DEFRAGMENTATION ####################### +# +# WARNING THIS FEATURE IS EXPERIMENTAL. However it was stress tested +# even in production and manually tested by multiple engineers for some +# time. +# +# What is active defragmentation? +# ------------------------------- +# +# Active (online) defragmentation allows a Redis server to compact the +# spaces left between small allocations and deallocations of data in memory, +# thus allowing to reclaim back memory. +# +# Fragmentation is a natural process that happens with every allocator (but +# less so with Jemalloc, fortunately) and certain workloads. Normally a server +# restart is needed in order to lower the fragmentation, or at least to flush +# away all the data and create it again. However thanks to this feature +# implemented by Oran Agra for Redis 4.0 this process can happen at runtime +# in an "hot" way, while the server is running. +# +# Basically when the fragmentation is over a certain level (see the +# configuration options below) Redis will start to create new copies of the +# values in contiguous memory regions by exploiting certain specific Jemalloc +# features (in order to understand if an allocation is causing fragmentation +# and to allocate it in a better place), and at the same time, will release the +# old copies of the data. This process, repeated incrementally for all the keys +# will cause the fragmentation to drop back to normal values. +# +# Important things to understand: +# +# 1. This feature is disabled by default, and only works if you compiled Redis +# to use the copy of Jemalloc we ship with the source code of Redis. +# This is the default with Linux builds. +# +# 2. You never need to enable this feature if you don't have fragmentation +# issues. +# +# 3. Once you experience fragmentation, you can enable this feature when +# needed with the command "CONFIG SET activedefrag yes". +# +# The configuration parameters are able to fine tune the behavior of the +# defragmentation process. If you are not sure about what they mean it is +# a good idea to leave the defaults untouched. + +# Enabled active defragmentation +# activedefrag yes + +# Minimum amount of fragmentation waste to start active defrag +# active-defrag-ignore-bytes 100mb + +# Minimum percentage of fragmentation to start active defrag +# active-defrag-threshold-lower 10 + +# Maximum percentage of fragmentation at which we use maximum effort +# active-defrag-threshold-upper 100 + +# Minimal effort for defrag in CPU percentage +# active-defrag-cycle-min 5 + +# Maximal effort for defrag in CPU percentage +# active-defrag-cycle-max 75 + +# Maximum number of set/hash/zset/list fields that will be processed from +# the main dictionary scan +# active-defrag-max-scan-fields 1000 + diff --git a/docker/configs/server/config.js b/docker/configs/server/config.js new file mode 100644 index 0000000..d3bdb02 --- /dev/null +++ b/docker/configs/server/config.js @@ -0,0 +1,158 @@ +const os = require("os"); +const ifaces = os.networkInterfaces(); + +function getListenIps() { + let listenIP = []; + Object.keys(ifaces).forEach(function (ifname) { + var alias = 0; + + ifaces[ifname].forEach(function (iface) { + if ( + (iface.family !== "IPv4" && + (iface.family !== "IPv6" || iface.scopeid !== 0)) || + iface.internal !== false + ) { + // skip over internal (i.e. 127.0.0.1) and non-ipv4 or ipv6 non global addresses + return; + } + listenIP.push({ ip: iface.address, announcedIp: null }); + + ++alias; + }); + }); + return listenIP; +} + +module.exports = { + // oAuth2 conf + /* auth : + { + lti : + { + consumerKey : '_bo2uqnwon1ym4qkte5hhd4fzlnoufvts5h3hblxzcy', + consumerSecret : '_1xpnaa4iw36cwpnx7991e630yo0u4044so1crhvcnz' + }, + oidc: + { + // The issuer URL for OpenID Connect discovery + // The OpenID Provider Configuration Document + // could be discovered on: + // issuerURL + '/.well-known/openid-configuration' + + issuerURL : 'https://example.com', + clientOptions : + { + client_id : '', + client_secret : '', + scope : 'openid email profile', + // where client.example.com is your multiparty meeting server + redirect_uri : 'https://client.example.com/auth/callback' + } + } + },*/ + redisOptions: {}, + // session cookie secret + cookieSecret: "T0P-S3cR3t_cook!e", + cookieName: "multiparty-meeting.sid", + tls: { + cert: `${__dirname}/../certs/cert.pem`, + key: `${__dirname}/../certs/privkey.pem` + }, + // Listening port for https server. + listeningPort: 443, + // Any http request is redirected to https. + // Listening port for http server. + listeningRedirectPort: 80, + // Listens only on http, only on listeningPort + // listeningRedirectPort disabled + // use case: loadbalancer backend + httpOnly: false, + // WebServer/Express trust proxy config for httpOnly mode + // You can find more info: + // - https://expressjs.com/en/guide/behind-proxies.html + // - https://www.npmjs.com/package/proxy-addr + // use case: loadbalancer backend + trustProxy : '', + // If this is set to true, only signed-in users will be able + // to join a room directly. Non-signed-in users (guests) will + // always be put in the lobby regardless of room lock status. + // If false, there is no difference between guests and signed-in + // users when joining. + requireSignInToAccess: false, + // This flag has no effect when requireSignInToAccess is false + // When truthy, the room will be open to all users when the first + // authenticated user has already joined the room. + activateOnHostJoin: false, + // Mediasoup settings + mediasoup: { + numWorkers: Object.keys(os.cpus()).length, + // mediasoup Worker settings. + worker: { + logLevel: "warn", + logTags: ["info", "ice", "dtls", "rtp", "srtp", "rtcp"], + rtcMinPort: 40000, + rtcMaxPort: 49999 + }, + // mediasoup Router settings. + router: { + // Router media codecs. + mediaCodecs: [ + { + kind: 'audio', + mimeType: 'audio/opus', + clockRate: 48000, + channels: 2 + }, + { + kind: 'video', + mimeType: 'video/VP8', + clockRate: 90000, + parameters: + { + 'x-google-start-bitrate': 1000 + } + }, + { + kind: 'video', + mimeType: 'video/VP9', + clockRate: 90000, + parameters: + { + 'profile-id': 2, + 'x-google-start-bitrate': 1000 + } + }, + { + kind: 'video', + mimeType: 'video/h264', + clockRate: 90000, + parameters: + { + 'packetization-mode': 1, + 'profile-level-id': '4d0032', + 'level-asymmetry-allowed': 1, + 'x-google-start-bitrate': 1000 + } + }, + { + kind: 'video', + mimeType: 'video/h264', + clockRate: 90000, + parameters: + { + 'packetization-mode': 1, + 'profile-level-id': '42e01f', + 'level-asymmetry-allowed': 1, + 'x-google-start-bitrate': 1000 + } + } + ] + }, + // mediasoup WebRtcTransport settings. + webRtcTransport: { + listenIps: getListenIps(), + maxIncomingBitrate: 1500000, + initialAvailableOutgoingBitrate: 1000000 + } + } +}; diff --git a/docker/deployment/.env b/docker/deployment/.env new file mode 100644 index 0000000..ab1244b --- /dev/null +++ b/docker/deployment/.env @@ -0,0 +1,8 @@ +BASEDIR=/opt +MM=multiparty-meeting +NODE_ENV=production +SERVER_DEBUG= +#SERVER_DEBUG=mediasoup* +TAG=latest +BRANCH=master +#REACT_APP_DEBUG='*' diff --git a/docker/deployment/README.md b/docker/deployment/README.md new file mode 100644 index 0000000..d27aa3d --- /dev/null +++ b/docker/deployment/README.md @@ -0,0 +1,63 @@ +# Multiparty Meeting => MM +MM stand as a shortcut for multiparty-meeting. + +This is the container, or a "dockerized" version of the [multiparty meeting](https://github.com/havfo/multiparty-meeting), +and like MM is shortcut, this container is a simillar shortcut that saves time. +:) + +## Run it in few easy step. +1. Git clone this code to your docker machine. +2. Copy your cert in `certs/cert.pem` and `certs/privkey.pem` + 1. In case you need to generate a new cert and private key, you can use (note -nodes flag, which allows to generate unencrypted private key) +``` + $ openssl req -x509 -newkey rsa:4096 -keyout privkey.pem -out cert.pem -days 365 -nodes +``` +3. **Recomended**: set TURN server and credential in `configs/app/config.js` + 1. In case you are using coturn, you can generate a user and key with +``` + $ turnadmin -k -u -p +``` +Placeholder looks like +``` + turnServers : [ + { + urls : [ + 'turn:example.com:443?transport=tcp' + ], + username : 'example', + credential : 'example' + } +``` +You would need to replace example.com by your IP or domain, add the username previously used `` and credential is the code generated by the above mentioned command. + +4. **Optional:** Change other stuff in config: + 1. **Optional:** replace logo/logo.svg with your company logo svg. + 2. **Optional:** sort audio/video codecs according to preference. + +## Run: +There is two ways +1. Simple use `docker run` command +``` +$ sudo ./run.sh +``` + +2. Or with `docker-compose` +/ [install docker compose](https://docs.docker.com/compose/install/) / +``` + $ sudo docker-compose up --detach +``` +## Rebuild + +If you change app-config.js or or something in .env then you have to rebuild the image. +``` + $ sudo docker-compose up --build --detach +``` + +## Docker networking +Container works in "host" network mode, because birdge mode has the following issue + +[Docker - Docker hangs when attempting to bind a large number of ports] (https://success.docker.com/article/docker-compose-and-docker-run-hang-when-binding-a-large-port-range) + + +## Further Informations +Read more about configs and settings in [multiparty meeting](https://github.com/havfo/multiparty-meeting) README. diff --git a/docker/deployment/configs/app/config.js b/docker/deployment/configs/app/config.js new file mode 100644 index 0000000..f897ea2 --- /dev/null +++ b/docker/deployment/configs/app/config.js @@ -0,0 +1,84 @@ +// eslint-disable-next-line +var config = +{ + loginEnabled : false, + developmentPort : 3443, + productionPort : 443, + multipartyServer : 'fqdn', + turnServers : [ + { + urls : [ + 'turn:example.com:443?transport=tcp' + ], + username : 'example', + credential : 'example' + } + ], + /** + * If defaultResolution is set, it will override user settings when joining: + * low ~ 320x240 + * medium ~ 640x480 + * high ~ 1280x720 + * veryhigh ~ 1920x1080 + * ultra ~ 3840x2560 + **/ + defaultResolution : 'medium', + // Enable or disable simulcast for webcam video + simulcast : true, + // Enable or disable simulcast for screen sharing video + simulcastSharing : false, + // Simulcast encoding layers and levels + simulcastEncodings : + [ + { scaleResolutionDownBy: 4 }, + { scaleResolutionDownBy: 2 }, + { scaleResolutionDownBy: 1 } + ], + // Socket.io request timeout + requestTimeout : 10000, + transportOptions : + { + tcp : true + }, + lastN : 4, + mobileLastN : 1, + background : 'images/background.jpg', + // Add file and uncomment for adding logo to appbar + // logo : 'images/logo.svg', + title : 'Multiparty meeting', + theme : + { + palette : + { + primary : + { + main : '#313131' + } + }, + overrides : + { + MuiAppBar : + { + colorPrimary : + { + backgroundColor : '#313131' + } + }, + MuiFab : + { + primary : + { + backgroundColor : '#5F9B2D', + '&:hover' : + { + backgroundColor : '#518029' + } + } + } + }, + typography : + { + useNextVariants : true + } + } +}; diff --git a/docker/deployment/configs/redis/redis.conf b/docker/deployment/configs/redis/redis.conf new file mode 100644 index 0000000..d74ff98 --- /dev/null +++ b/docker/deployment/configs/redis/redis.conf @@ -0,0 +1,1372 @@ +# Redis configuration file example. +# +# Note that in order to read the configuration file, Redis must be +# started with the file path as first argument: +# +# ./redis-server /path/to/redis.conf + +# Note on units: when memory size is needed, it is possible to specify +# it in the usual form of 1k 5GB 4M and so forth: +# +# 1k => 1000 bytes +# 1kb => 1024 bytes +# 1m => 1000000 bytes +# 1mb => 1024*1024 bytes +# 1g => 1000000000 bytes +# 1gb => 1024*1024*1024 bytes +# +# units are case insensitive so 1GB 1Gb 1gB are all the same. + +################################## INCLUDES ################################### + +# Include one or more other config files here. This is useful if you +# have a standard template that goes to all Redis servers but also need +# to customize a few per-server settings. Include files can include +# other files, so use this wisely. +# +# Notice option "include" won't be rewritten by command "CONFIG REWRITE" +# from admin or Redis Sentinel. Since Redis always uses the last processed +# line as value of a configuration directive, you'd better put includes +# at the beginning of this file to avoid overwriting config change at runtime. +# +# If instead you are interested in using includes to override configuration +# options, it is better to use include as the last line. +# +# include /path/to/local.conf +# include /path/to/other.conf + +################################## MODULES ##################################### + +# Load modules at startup. If the server is not able to load modules +# it will abort. It is possible to use multiple loadmodule directives. +# +# loadmodule /path/to/my_module.so +# loadmodule /path/to/other_module.so + +################################## NETWORK ##################################### + +# By default, if no "bind" configuration directive is specified, Redis listens +# for connections from all the network interfaces available on the server. +# It is possible to listen to just one or multiple selected interfaces using +# the "bind" configuration directive, followed by one or more IP addresses. +# +# Examples: +# +# bind 192.168.1.100 10.0.0.1 +# bind 127.0.0.1 ::1 +# +# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the +# internet, binding to all the interfaces is dangerous and will expose the +# instance to everybody on the internet. So by default we uncomment the +# following bind directive, that will force Redis to listen only into +# the IPv4 loopback interface address (this means Redis will be able to +# accept connections only from clients running into the same computer it +# is running). +# +# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES +# JUST COMMENT THE FOLLOWING LINE. +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bind 127.0.0.1 + +# Protected mode is a layer of security protection, in order to avoid that +# Redis instances left open on the internet are accessed and exploited. +# +# When protected mode is on and if: +# +# 1) The server is not binding explicitly to a set of addresses using the +# "bind" directive. +# 2) No password is configured. +# +# The server only accepts connections from clients connecting from the +# IPv4 and IPv6 loopback addresses 127.0.0.1 and ::1, and from Unix domain +# sockets. +# +# By default protected mode is enabled. You should disable it only if +# you are sure you want clients from other hosts to connect to Redis +# even if no authentication is configured, nor a specific set of interfaces +# are explicitly listed using the "bind" directive. +protected-mode yes + +# Accept connections on the specified port, default is 6379 (IANA #815344). +# If port 0 is specified Redis will not listen on a TCP socket. +port 6379 + +# TCP listen() backlog. +# +# In high requests-per-second environments you need an high backlog in order +# to avoid slow clients connections issues. Note that the Linux kernel +# will silently truncate it to the value of /proc/sys/net/core/somaxconn so +# make sure to raise both the value of somaxconn and tcp_max_syn_backlog +# in order to get the desired effect. +tcp-backlog 511 + +# Unix socket. +# +# Specify the path for the Unix socket that will be used to listen for +# incoming connections. There is no default, so Redis will not listen +# on a unix socket when not specified. +# +# unixsocket /tmp/redis.sock +# unixsocketperm 700 + +# Close the connection after a client is idle for N seconds (0 to disable) +timeout 0 + +# TCP keepalive. +# +# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence +# of communication. This is useful for two reasons: +# +# 1) Detect dead peers. +# 2) Take the connection alive from the point of view of network +# equipment in the middle. +# +# On Linux, the specified value (in seconds) is the period used to send ACKs. +# Note that to close the connection the double of the time is needed. +# On other kernels the period depends on the kernel configuration. +# +# A reasonable value for this option is 300 seconds, which is the new +# Redis default starting with Redis 3.2.1. +tcp-keepalive 300 + +################################# GENERAL ##################################### + +# By default Redis does not run as a daemon. Use 'yes' if you need it. +# Note that Redis will write a pid file in /var/run/redis.pid when daemonized. +daemonize no + +# If you run Redis from upstart or systemd, Redis can interact with your +# supervision tree. Options: +# supervised no - no supervision interaction +# supervised upstart - signal upstart by putting Redis into SIGSTOP mode +# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET +# supervised auto - detect upstart or systemd method based on +# UPSTART_JOB or NOTIFY_SOCKET environment variables +# Note: these supervision methods only signal "process is ready." +# They do not enable continuous liveness pings back to your supervisor. +supervised no + +# If a pid file is specified, Redis writes it where specified at startup +# and removes it at exit. +# +# When the server runs non daemonized, no pid file is created if none is +# specified in the configuration. When the server is daemonized, the pid file +# is used even if not specified, defaulting to "/var/run/redis.pid". +# +# Creating a pid file is best effort: if Redis is not able to create it +# nothing bad happens, the server will start and run normally. +pidfile /var/run/redis_6379.pid + +# Specify the server verbosity level. +# This can be one of: +# debug (a lot of information, useful for development/testing) +# verbose (many rarely useful info, but not a mess like the debug level) +# notice (moderately verbose, what you want in production probably) +# warning (only very important / critical messages are logged) +loglevel notice + +# Specify the log file name. Also the empty string can be used to force +# Redis to log on the standard output. Note that if you use standard +# output for logging but daemonize, logs will be sent to /dev/null +logfile "" + +# To enable logging to the system logger, just set 'syslog-enabled' to yes, +# and optionally update the other syslog parameters to suit your needs. +# syslog-enabled no + +# Specify the syslog identity. +# syslog-ident redis + +# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. +# syslog-facility local0 + +# Set the number of databases. The default database is DB 0, you can select +# a different one on a per-connection basis using SELECT where +# dbid is a number between 0 and 'databases'-1 +databases 16 + +# By default Redis shows an ASCII art logo only when started to log to the +# standard output and if the standard output is a TTY. Basically this means +# that normally a logo is displayed only in interactive sessions. +# +# However it is possible to force the pre-4.0 behavior and always show a +# ASCII art logo in startup logs by setting the following option to yes. +always-show-logo yes + +################################ SNAPSHOTTING ################################ +# +# Save the DB on disk: +# +# save +# +# Will save the DB if both the given number of seconds and the given +# number of write operations against the DB occurred. +# +# In the example below the behaviour will be to save: +# after 900 sec (15 min) if at least 1 key changed +# after 300 sec (5 min) if at least 10 keys changed +# after 60 sec if at least 10000 keys changed +# +# Note: you can disable saving completely by commenting out all "save" lines. +# +# It is also possible to remove all the previously configured save +# points by adding a save directive with a single empty string argument +# like in the following example: +# +# save "" + +save 900 1 +save 300 10 +save 60 10000 + +# By default Redis will stop accepting writes if RDB snapshots are enabled +# (at least one save point) and the latest background save failed. +# This will make the user aware (in a hard way) that data is not persisting +# on disk properly, otherwise chances are that no one will notice and some +# disaster will happen. +# +# If the background saving process will start working again Redis will +# automatically allow writes again. +# +# However if you have setup your proper monitoring of the Redis server +# and persistence, you may want to disable this feature so that Redis will +# continue to work as usual even if there are problems with disk, +# permissions, and so forth. +stop-writes-on-bgsave-error yes + +# Compress string objects using LZF when dump .rdb databases? +# For default that's set to 'yes' as it's almost always a win. +# If you want to save some CPU in the saving child set it to 'no' but +# the dataset will likely be bigger if you have compressible values or keys. +rdbcompression yes + +# Since version 5 of RDB a CRC64 checksum is placed at the end of the file. +# This makes the format more resistant to corruption but there is a performance +# hit to pay (around 10%) when saving and loading RDB files, so you can disable it +# for maximum performances. +# +# RDB files created with checksum disabled have a checksum of zero that will +# tell the loading code to skip the check. +rdbchecksum yes + +# The filename where to dump the DB +dbfilename dump.rdb + +# The working directory. +# +# The DB will be written inside this directory, with the filename specified +# above using the 'dbfilename' configuration directive. +# +# The Append Only File will also be created inside this directory. +# +# Note that you must specify a directory here, not a file name. +dir ./ + +################################# REPLICATION ################################# + +# Master-Replica replication. Use replicaof to make a Redis instance a copy of +# another Redis server. A few things to understand ASAP about Redis replication. +# +# +------------------+ +---------------+ +# | Master | ---> | Replica | +# | (receive writes) | | (exact copy) | +# +------------------+ +---------------+ +# +# 1) Redis replication is asynchronous, but you can configure a master to +# stop accepting writes if it appears to be not connected with at least +# a given number of replicas. +# 2) Redis replicas are able to perform a partial resynchronization with the +# master if the replication link is lost for a relatively small amount of +# time. You may want to configure the replication backlog size (see the next +# sections of this file) with a sensible value depending on your needs. +# 3) Replication is automatic and does not need user intervention. After a +# network partition replicas automatically try to reconnect to masters +# and resynchronize with them. +# +# replicaof + +# If the master is password protected (using the "requirepass" configuration +# directive below) it is possible to tell the replica to authenticate before +# starting the replication synchronization process, otherwise the master will +# refuse the replica request. +# +# masterauth + +# When a replica loses its connection with the master, or when the replication +# is still in progress, the replica can act in two different ways: +# +# 1) if replica-serve-stale-data is set to 'yes' (the default) the replica will +# still reply to client requests, possibly with out of date data, or the +# data set may just be empty if this is the first synchronization. +# +# 2) if replica-serve-stale-data is set to 'no' the replica will reply with +# an error "SYNC with master in progress" to all the kind of commands +# but to INFO, replicaOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG, +# SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, +# COMMAND, POST, HOST: and LATENCY. +# +replica-serve-stale-data yes + +# You can configure a replica instance to accept writes or not. Writing against +# a replica instance may be useful to store some ephemeral data (because data +# written on a replica will be easily deleted after resync with the master) but +# may also cause problems if clients are writing to it because of a +# misconfiguration. +# +# Since Redis 2.6 by default replicas are read-only. +# +# Note: read only replicas are not designed to be exposed to untrusted clients +# on the internet. It's just a protection layer against misuse of the instance. +# Still a read only replica exports by default all the administrative commands +# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve +# security of read only replicas using 'rename-command' to shadow all the +# administrative / dangerous commands. +replica-read-only yes + +# Replication SYNC strategy: disk or socket. +# +# ------------------------------------------------------- +# WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY +# ------------------------------------------------------- +# +# New replicas and reconnecting replicas that are not able to continue the replication +# process just receiving differences, need to do what is called a "full +# synchronization". An RDB file is transmitted from the master to the replicas. +# The transmission can happen in two different ways: +# +# 1) Disk-backed: The Redis master creates a new process that writes the RDB +# file on disk. Later the file is transferred by the parent +# process to the replicas incrementally. +# 2) Diskless: The Redis master creates a new process that directly writes the +# RDB file to replica sockets, without touching the disk at all. +# +# With disk-backed replication, while the RDB file is generated, more replicas +# can be queued and served with the RDB file as soon as the current child producing +# the RDB file finishes its work. With diskless replication instead once +# the transfer starts, new replicas arriving will be queued and a new transfer +# will start when the current one terminates. +# +# When diskless replication is used, the master waits a configurable amount of +# time (in seconds) before starting the transfer in the hope that multiple replicas +# will arrive and the transfer can be parallelized. +# +# With slow disks and fast (large bandwidth) networks, diskless replication +# works better. +repl-diskless-sync no + +# When diskless replication is enabled, it is possible to configure the delay +# the server waits in order to spawn the child that transfers the RDB via socket +# to the replicas. +# +# This is important since once the transfer starts, it is not possible to serve +# new replicas arriving, that will be queued for the next RDB transfer, so the server +# waits a delay in order to let more replicas arrive. +# +# The delay is specified in seconds, and by default is 5 seconds. To disable +# it entirely just set it to 0 seconds and the transfer will start ASAP. +repl-diskless-sync-delay 5 + +# Replicas send PINGs to server in a predefined interval. It's possible to change +# this interval with the repl_ping_replica_period option. The default value is 10 +# seconds. +# +# repl-ping-replica-period 10 + +# The following option sets the replication timeout for: +# +# 1) Bulk transfer I/O during SYNC, from the point of view of replica. +# 2) Master timeout from the point of view of replicas (data, pings). +# 3) Replica timeout from the point of view of masters (REPLCONF ACK pings). +# +# It is important to make sure that this value is greater than the value +# specified for repl-ping-replica-period otherwise a timeout will be detected +# every time there is low traffic between the master and the replica. +# +# repl-timeout 60 + +# Disable TCP_NODELAY on the replica socket after SYNC? +# +# If you select "yes" Redis will use a smaller number of TCP packets and +# less bandwidth to send data to replicas. But this can add a delay for +# the data to appear on the replica side, up to 40 milliseconds with +# Linux kernels using a default configuration. +# +# If you select "no" the delay for data to appear on the replica side will +# be reduced but more bandwidth will be used for replication. +# +# By default we optimize for low latency, but in very high traffic conditions +# or when the master and replicas are many hops away, turning this to "yes" may +# be a good idea. +repl-disable-tcp-nodelay no + +# Set the replication backlog size. The backlog is a buffer that accumulates +# replica data when replicas are disconnected for some time, so that when a replica +# wants to reconnect again, often a full resync is not needed, but a partial +# resync is enough, just passing the portion of data the replica missed while +# disconnected. +# +# The bigger the replication backlog, the longer the time the replica can be +# disconnected and later be able to perform a partial resynchronization. +# +# The backlog is only allocated once there is at least a replica connected. +# +# repl-backlog-size 1mb + +# After a master has no longer connected replicas for some time, the backlog +# will be freed. The following option configures the amount of seconds that +# need to elapse, starting from the time the last replica disconnected, for +# the backlog buffer to be freed. +# +# Note that replicas never free the backlog for timeout, since they may be +# promoted to masters later, and should be able to correctly "partially +# resynchronize" with the replicas: hence they should always accumulate backlog. +# +# A value of 0 means to never release the backlog. +# +# repl-backlog-ttl 3600 + +# The replica priority is an integer number published by Redis in the INFO output. +# It is used by Redis Sentinel in order to select a replica to promote into a +# master if the master is no longer working correctly. +# +# A replica with a low priority number is considered better for promotion, so +# for instance if there are three replicas with priority 10, 100, 25 Sentinel will +# pick the one with priority 10, that is the lowest. +# +# However a special priority of 0 marks the replica as not able to perform the +# role of master, so a replica with priority of 0 will never be selected by +# Redis Sentinel for promotion. +# +# By default the priority is 100. +replica-priority 100 + +# It is possible for a master to stop accepting writes if there are less than +# N replicas connected, having a lag less or equal than M seconds. +# +# The N replicas need to be in "online" state. +# +# The lag in seconds, that must be <= the specified value, is calculated from +# the last ping received from the replica, that is usually sent every second. +# +# This option does not GUARANTEE that N replicas will accept the write, but +# will limit the window of exposure for lost writes in case not enough replicas +# are available, to the specified number of seconds. +# +# For example to require at least 3 replicas with a lag <= 10 seconds use: +# +# min-replicas-to-write 3 +# min-replicas-max-lag 10 +# +# Setting one or the other to 0 disables the feature. +# +# By default min-replicas-to-write is set to 0 (feature disabled) and +# min-replicas-max-lag is set to 10. + +# A Redis master is able to list the address and port of the attached +# replicas in different ways. For example the "INFO replication" section +# offers this information, which is used, among other tools, by +# Redis Sentinel in order to discover replica instances. +# Another place where this info is available is in the output of the +# "ROLE" command of a master. +# +# The listed IP and address normally reported by a replica is obtained +# in the following way: +# +# IP: The address is auto detected by checking the peer address +# of the socket used by the replica to connect with the master. +# +# Port: The port is communicated by the replica during the replication +# handshake, and is normally the port that the replica is using to +# listen for connections. +# +# However when port forwarding or Network Address Translation (NAT) is +# used, the replica may be actually reachable via different IP and port +# pairs. The following two options can be used by a replica in order to +# report to its master a specific set of IP and port, so that both INFO +# and ROLE will report those values. +# +# There is no need to use both the options if you need to override just +# the port or the IP address. +# +# replica-announce-ip 5.5.5.5 +# replica-announce-port 1234 + +################################## SECURITY ################################### + +# Require clients to issue AUTH before processing any other +# commands. This might be useful in environments in which you do not trust +# others with access to the host running redis-server. +# +# This should stay commented out for backward compatibility and because most +# people do not need auth (e.g. they run their own servers). +# +# Warning: since Redis is pretty fast an outside user can try up to +# 150k passwords per second against a good box. This means that you should +# use a very strong password otherwise it will be very easy to break. +# +# requirepass foobared + +# Command renaming. +# +# It is possible to change the name of dangerous commands in a shared +# environment. For instance the CONFIG command may be renamed into something +# hard to guess so that it will still be available for internal-use tools +# but not available for general clients. +# +# Example: +# +# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 +# +# It is also possible to completely kill a command by renaming it into +# an empty string: +# +# rename-command CONFIG "" +# +# Please note that changing the name of commands that are logged into the +# AOF file or transmitted to replicas may cause problems. + +################################### CLIENTS #################################### + +# Set the max number of connected clients at the same time. By default +# this limit is set to 10000 clients, however if the Redis server is not +# able to configure the process file limit to allow for the specified limit +# the max number of allowed clients is set to the current file limit +# minus 32 (as Redis reserves a few file descriptors for internal uses). +# +# Once the limit is reached Redis will close all the new connections sending +# an error 'max number of clients reached'. +# +# maxclients 10000 + +############################## MEMORY MANAGEMENT ################################ + +# Set a memory usage limit to the specified amount of bytes. +# When the memory limit is reached Redis will try to remove keys +# according to the eviction policy selected (see maxmemory-policy). +# +# If Redis can't remove keys according to the policy, or if the policy is +# set to 'noeviction', Redis will start to reply with errors to commands +# that would use more memory, like SET, LPUSH, and so on, and will continue +# to reply to read-only commands like GET. +# +# This option is usually useful when using Redis as an LRU or LFU cache, or to +# set a hard memory limit for an instance (using the 'noeviction' policy). +# +# WARNING: If you have replicas attached to an instance with maxmemory on, +# the size of the output buffers needed to feed the replicas are subtracted +# from the used memory count, so that network problems / resyncs will +# not trigger a loop where keys are evicted, and in turn the output +# buffer of replicas is full with DELs of keys evicted triggering the deletion +# of more keys, and so forth until the database is completely emptied. +# +# In short... if you have replicas attached it is suggested that you set a lower +# limit for maxmemory so that there is some free RAM on the system for replica +# output buffers (but this is not needed if the policy is 'noeviction'). +# +# maxmemory + +# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory +# is reached. You can select among five behaviors: +# +# volatile-lru -> Evict using approximated LRU among the keys with an expire set. +# allkeys-lru -> Evict any key using approximated LRU. +# volatile-lfu -> Evict using approximated LFU among the keys with an expire set. +# allkeys-lfu -> Evict any key using approximated LFU. +# volatile-random -> Remove a random key among the ones with an expire set. +# allkeys-random -> Remove a random key, any key. +# volatile-ttl -> Remove the key with the nearest expire time (minor TTL) +# noeviction -> Don't evict anything, just return an error on write operations. +# +# LRU means Least Recently Used +# LFU means Least Frequently Used +# +# Both LRU, LFU and volatile-ttl are implemented using approximated +# randomized algorithms. +# +# Note: with any of the above policies, Redis will return an error on write +# operations, when there are no suitable keys for eviction. +# +# At the date of writing these commands are: set setnx setex append +# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd +# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby +# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby +# getset mset msetnx exec sort +# +# The default is: +# +# maxmemory-policy noeviction + +# LRU, LFU and minimal TTL algorithms are not precise algorithms but approximated +# algorithms (in order to save memory), so you can tune it for speed or +# accuracy. For default Redis will check five keys and pick the one that was +# used less recently, you can change the sample size using the following +# configuration directive. +# +# The default of 5 produces good enough results. 10 Approximates very closely +# true LRU but costs more CPU. 3 is faster but not very accurate. +# +# maxmemory-samples 5 + +# Starting from Redis 5, by default a replica will ignore its maxmemory setting +# (unless it is promoted to master after a failover or manually). It means +# that the eviction of keys will be just handled by the master, sending the +# DEL commands to the replica as keys evict in the master side. +# +# This behavior ensures that masters and replicas stay consistent, and is usually +# what you want, however if your replica is writable, or you want the replica to have +# a different memory setting, and you are sure all the writes performed to the +# replica are idempotent, then you may change this default (but be sure to understand +# what you are doing). +# +# Note that since the replica by default does not evict, it may end using more +# memory than the one set via maxmemory (there are certain buffers that may +# be larger on the replica, or data structures may sometimes take more memory and so +# forth). So make sure you monitor your replicas and make sure they have enough +# memory to never hit a real out-of-memory condition before the master hits +# the configured maxmemory setting. +# +# replica-ignore-maxmemory yes + +############################# LAZY FREEING #################################### + +# Redis has two primitives to delete keys. One is called DEL and is a blocking +# deletion of the object. It means that the server stops processing new commands +# in order to reclaim all the memory associated with an object in a synchronous +# way. If the key deleted is associated with a small object, the time needed +# in order to execute the DEL command is very small and comparable to most other +# O(1) or O(log_N) commands in Redis. However if the key is associated with an +# aggregated value containing millions of elements, the server can block for +# a long time (even seconds) in order to complete the operation. +# +# For the above reasons Redis also offers non blocking deletion primitives +# such as UNLINK (non blocking DEL) and the ASYNC option of FLUSHALL and +# FLUSHDB commands, in order to reclaim memory in background. Those commands +# are executed in constant time. Another thread will incrementally free the +# object in the background as fast as possible. +# +# DEL, UNLINK and ASYNC option of FLUSHALL and FLUSHDB are user-controlled. +# It's up to the design of the application to understand when it is a good +# idea to use one or the other. However the Redis server sometimes has to +# delete keys or flush the whole database as a side effect of other operations. +# Specifically Redis deletes objects independently of a user call in the +# following scenarios: +# +# 1) On eviction, because of the maxmemory and maxmemory policy configurations, +# in order to make room for new data, without going over the specified +# memory limit. +# 2) Because of expire: when a key with an associated time to live (see the +# EXPIRE command) must be deleted from memory. +# 3) Because of a side effect of a command that stores data on a key that may +# already exist. For example the RENAME command may delete the old key +# content when it is replaced with another one. Similarly SUNIONSTORE +# or SORT with STORE option may delete existing keys. The SET command +# itself removes any old content of the specified key in order to replace +# it with the specified string. +# 4) During replication, when a replica performs a full resynchronization with +# its master, the content of the whole database is removed in order to +# load the RDB file just transferred. +# +# In all the above cases the default is to delete objects in a blocking way, +# like if DEL was called. However you can configure each case specifically +# in order to instead release memory in a non-blocking way like if UNLINK +# was called, using the following configuration directives: + +lazyfree-lazy-eviction no +lazyfree-lazy-expire no +lazyfree-lazy-server-del no +replica-lazy-flush no + +############################## APPEND ONLY MODE ############################### + +# By default Redis asynchronously dumps the dataset on disk. This mode is +# good enough in many applications, but an issue with the Redis process or +# a power outage may result into a few minutes of writes lost (depending on +# the configured save points). +# +# The Append Only File is an alternative persistence mode that provides +# much better durability. For instance using the default data fsync policy +# (see later in the config file) Redis can lose just one second of writes in a +# dramatic event like a server power outage, or a single write if something +# wrong with the Redis process itself happens, but the operating system is +# still running correctly. +# +# AOF and RDB persistence can be enabled at the same time without problems. +# If the AOF is enabled on startup Redis will load the AOF, that is the file +# with the better durability guarantees. +# +# Please check http://redis.io/topics/persistence for more information. + +appendonly no + +# The name of the append only file (default: "appendonly.aof") + +appendfilename "appendonly.aof" + +# The fsync() call tells the Operating System to actually write data on disk +# instead of waiting for more data in the output buffer. Some OS will really flush +# data on disk, some other OS will just try to do it ASAP. +# +# Redis supports three different modes: +# +# no: don't fsync, just let the OS flush the data when it wants. Faster. +# always: fsync after every write to the append only log. Slow, Safest. +# everysec: fsync only one time every second. Compromise. +# +# The default is "everysec", as that's usually the right compromise between +# speed and data safety. It's up to you to understand if you can relax this to +# "no" that will let the operating system flush the output buffer when +# it wants, for better performances (but if you can live with the idea of +# some data loss consider the default persistence mode that's snapshotting), +# or on the contrary, use "always" that's very slow but a bit safer than +# everysec. +# +# More details please check the following article: +# http://antirez.com/post/redis-persistence-demystified.html +# +# If unsure, use "everysec". + +# appendfsync always +appendfsync everysec +# appendfsync no + +# When the AOF fsync policy is set to always or everysec, and a background +# saving process (a background save or AOF log background rewriting) is +# performing a lot of I/O against the disk, in some Linux configurations +# Redis may block too long on the fsync() call. Note that there is no fix for +# this currently, as even performing fsync in a different thread will block +# our synchronous write(2) call. +# +# In order to mitigate this problem it's possible to use the following option +# that will prevent fsync() from being called in the main process while a +# BGSAVE or BGREWRITEAOF is in progress. +# +# This means that while another child is saving, the durability of Redis is +# the same as "appendfsync none". In practical terms, this means that it is +# possible to lose up to 30 seconds of log in the worst scenario (with the +# default Linux settings). +# +# If you have latency problems turn this to "yes". Otherwise leave it as +# "no" that is the safest pick from the point of view of durability. + +no-appendfsync-on-rewrite no + +# Automatic rewrite of the append only file. +# Redis is able to automatically rewrite the log file implicitly calling +# BGREWRITEAOF when the AOF log size grows by the specified percentage. +# +# This is how it works: Redis remembers the size of the AOF file after the +# latest rewrite (if no rewrite has happened since the restart, the size of +# the AOF at startup is used). +# +# This base size is compared to the current size. If the current size is +# bigger than the specified percentage, the rewrite is triggered. Also +# you need to specify a minimal size for the AOF file to be rewritten, this +# is useful to avoid rewriting the AOF file even if the percentage increase +# is reached but it is still pretty small. +# +# Specify a percentage of zero in order to disable the automatic AOF +# rewrite feature. + +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb + +# An AOF file may be found to be truncated at the end during the Redis +# startup process, when the AOF data gets loaded back into memory. +# This may happen when the system where Redis is running +# crashes, especially when an ext4 filesystem is mounted without the +# data=ordered option (however this can't happen when Redis itself +# crashes or aborts but the operating system still works correctly). +# +# Redis can either exit with an error when this happens, or load as much +# data as possible (the default now) and start if the AOF file is found +# to be truncated at the end. The following option controls this behavior. +# +# If aof-load-truncated is set to yes, a truncated AOF file is loaded and +# the Redis server starts emitting a log to inform the user of the event. +# Otherwise if the option is set to no, the server aborts with an error +# and refuses to start. When the option is set to no, the user requires +# to fix the AOF file using the "redis-check-aof" utility before to restart +# the server. +# +# Note that if the AOF file will be found to be corrupted in the middle +# the server will still exit with an error. This option only applies when +# Redis will try to read more data from the AOF file but not enough bytes +# will be found. +aof-load-truncated yes + +# When rewriting the AOF file, Redis is able to use an RDB preamble in the +# AOF file for faster rewrites and recoveries. When this option is turned +# on the rewritten AOF file is composed of two different stanzas: +# +# [RDB file][AOF tail] +# +# When loading Redis recognizes that the AOF file starts with the "REDIS" +# string and loads the prefixed RDB file, and continues loading the AOF +# tail. +aof-use-rdb-preamble yes + +################################ LUA SCRIPTING ############################### + +# Max execution time of a Lua script in milliseconds. +# +# If the maximum execution time is reached Redis will log that a script is +# still in execution after the maximum allowed time and will start to +# reply to queries with an error. +# +# When a long running script exceeds the maximum execution time only the +# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be +# used to stop a script that did not yet called write commands. The second +# is the only way to shut down the server in the case a write command was +# already issued by the script but the user doesn't want to wait for the natural +# termination of the script. +# +# Set it to 0 or a negative value for unlimited execution without warnings. +lua-time-limit 5000 + +################################ REDIS CLUSTER ############################### + +# Normal Redis instances can't be part of a Redis Cluster; only nodes that are +# started as cluster nodes can. In order to start a Redis instance as a +# cluster node enable the cluster support uncommenting the following: +# +# cluster-enabled yes + +# Every cluster node has a cluster configuration file. This file is not +# intended to be edited by hand. It is created and updated by Redis nodes. +# Every Redis Cluster node requires a different cluster configuration file. +# Make sure that instances running in the same system do not have +# overlapping cluster configuration file names. +# +# cluster-config-file nodes-6379.conf + +# Cluster node timeout is the amount of milliseconds a node must be unreachable +# for it to be considered in failure state. +# Most other internal time limits are multiple of the node timeout. +# +# cluster-node-timeout 15000 + +# A replica of a failing master will avoid to start a failover if its data +# looks too old. +# +# There is no simple way for a replica to actually have an exact measure of +# its "data age", so the following two checks are performed: +# +# 1) If there are multiple replicas able to failover, they exchange messages +# in order to try to give an advantage to the replica with the best +# replication offset (more data from the master processed). +# Replicas will try to get their rank by offset, and apply to the start +# of the failover a delay proportional to their rank. +# +# 2) Every single replica computes the time of the last interaction with +# its master. This can be the last ping or command received (if the master +# is still in the "connected" state), or the time that elapsed since the +# disconnection with the master (if the replication link is currently down). +# If the last interaction is too old, the replica will not try to failover +# at all. +# +# The point "2" can be tuned by user. Specifically a replica will not perform +# the failover if, since the last interaction with the master, the time +# elapsed is greater than: +# +# (node-timeout * replica-validity-factor) + repl-ping-replica-period +# +# So for example if node-timeout is 30 seconds, and the replica-validity-factor +# is 10, and assuming a default repl-ping-replica-period of 10 seconds, the +# replica will not try to failover if it was not able to talk with the master +# for longer than 310 seconds. +# +# A large replica-validity-factor may allow replicas with too old data to failover +# a master, while a too small value may prevent the cluster from being able to +# elect a replica at all. +# +# For maximum availability, it is possible to set the replica-validity-factor +# to a value of 0, which means, that replicas will always try to failover the +# master regardless of the last time they interacted with the master. +# (However they'll always try to apply a delay proportional to their +# offset rank). +# +# Zero is the only value able to guarantee that when all the partitions heal +# the cluster will always be able to continue. +# +# cluster-replica-validity-factor 10 + +# Cluster replicas are able to migrate to orphaned masters, that are masters +# that are left without working replicas. This improves the cluster ability +# to resist to failures as otherwise an orphaned master can't be failed over +# in case of failure if it has no working replicas. +# +# Replicas migrate to orphaned masters only if there are still at least a +# given number of other working replicas for their old master. This number +# is the "migration barrier". A migration barrier of 1 means that a replica +# will migrate only if there is at least 1 other working replica for its master +# and so forth. It usually reflects the number of replicas you want for every +# master in your cluster. +# +# Default is 1 (replicas migrate only if their masters remain with at least +# one replica). To disable migration just set it to a very large value. +# A value of 0 can be set but is useful only for debugging and dangerous +# in production. +# +# cluster-migration-barrier 1 + +# By default Redis Cluster nodes stop accepting queries if they detect there +# is at least an hash slot uncovered (no available node is serving it). +# This way if the cluster is partially down (for example a range of hash slots +# are no longer covered) all the cluster becomes, eventually, unavailable. +# It automatically returns available as soon as all the slots are covered again. +# +# However sometimes you want the subset of the cluster which is working, +# to continue to accept queries for the part of the key space that is still +# covered. In order to do so, just set the cluster-require-full-coverage +# option to no. +# +# cluster-require-full-coverage yes + +# This option, when set to yes, prevents replicas from trying to failover its +# master during master failures. However the master can still perform a +# manual failover, if forced to do so. +# +# This is useful in different scenarios, especially in the case of multiple +# data center operations, where we want one side to never be promoted if not +# in the case of a total DC failure. +# +# cluster-replica-no-failover no + +# In order to setup your cluster make sure to read the documentation +# available at http://redis.io web site. + +########################## CLUSTER DOCKER/NAT support ######################## + +# In certain deployments, Redis Cluster nodes address discovery fails, because +# addresses are NAT-ted or because ports are forwarded (the typical case is +# Docker and other containers). +# +# In order to make Redis Cluster working in such environments, a static +# configuration where each node knows its public address is needed. The +# following two options are used for this scope, and are: +# +# * cluster-announce-ip +# * cluster-announce-port +# * cluster-announce-bus-port +# +# Each instruct the node about its address, client port, and cluster message +# bus port. The information is then published in the header of the bus packets +# so that other nodes will be able to correctly map the address of the node +# publishing the information. +# +# If the above options are not used, the normal Redis Cluster auto-detection +# will be used instead. +# +# Note that when remapped, the bus port may not be at the fixed offset of +# clients port + 10000, so you can specify any port and bus-port depending +# on how they get remapped. If the bus-port is not set, a fixed offset of +# 10000 will be used as usually. +# +# Example: +# +# cluster-announce-ip 10.1.1.5 +# cluster-announce-port 6379 +# cluster-announce-bus-port 6380 + +################################## SLOW LOG ################################### + +# The Redis Slow Log is a system to log queries that exceeded a specified +# execution time. The execution time does not include the I/O operations +# like talking with the client, sending the reply and so forth, +# but just the time needed to actually execute the command (this is the only +# stage of command execution where the thread is blocked and can not serve +# other requests in the meantime). +# +# You can configure the slow log with two parameters: one tells Redis +# what is the execution time, in microseconds, to exceed in order for the +# command to get logged, and the other parameter is the length of the +# slow log. When a new command is logged the oldest one is removed from the +# queue of logged commands. + +# The following time is expressed in microseconds, so 1000000 is equivalent +# to one second. Note that a negative number disables the slow log, while +# a value of zero forces the logging of every command. +slowlog-log-slower-than 10000 + +# There is no limit to this length. Just be aware that it will consume memory. +# You can reclaim memory used by the slow log with SLOWLOG RESET. +slowlog-max-len 128 + +################################ LATENCY MONITOR ############################## + +# The Redis latency monitoring subsystem samples different operations +# at runtime in order to collect data related to possible sources of +# latency of a Redis instance. +# +# Via the LATENCY command this information is available to the user that can +# print graphs and obtain reports. +# +# The system only logs operations that were performed in a time equal or +# greater than the amount of milliseconds specified via the +# latency-monitor-threshold configuration directive. When its value is set +# to zero, the latency monitor is turned off. +# +# By default latency monitoring is disabled since it is mostly not needed +# if you don't have latency issues, and collecting data has a performance +# impact, that while very small, can be measured under big load. Latency +# monitoring can easily be enabled at runtime using the command +# "CONFIG SET latency-monitor-threshold " if needed. +latency-monitor-threshold 0 + +############################# EVENT NOTIFICATION ############################## + +# Redis can notify Pub/Sub clients about events happening in the key space. +# This feature is documented at http://redis.io/topics/notifications +# +# For instance if keyspace events notification is enabled, and a client +# performs a DEL operation on key "foo" stored in the Database 0, two +# messages will be published via Pub/Sub: +# +# PUBLISH __keyspace@0__:foo del +# PUBLISH __keyevent@0__:del foo +# +# It is possible to select the events that Redis will notify among a set +# of classes. Every class is identified by a single character: +# +# K Keyspace events, published with __keyspace@__ prefix. +# E Keyevent events, published with __keyevent@__ prefix. +# g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ... +# $ String commands +# l List commands +# s Set commands +# h Hash commands +# z Sorted set commands +# x Expired events (events generated every time a key expires) +# e Evicted events (events generated when a key is evicted for maxmemory) +# A Alias for g$lshzxe, so that the "AKE" string means all the events. +# +# The "notify-keyspace-events" takes as argument a string that is composed +# of zero or multiple characters. The empty string means that notifications +# are disabled. +# +# Example: to enable list and generic events, from the point of view of the +# event name, use: +# +# notify-keyspace-events Elg +# +# Example 2: to get the stream of the expired keys subscribing to channel +# name __keyevent@0__:expired use: +# +# notify-keyspace-events Ex +# +# By default all notifications are disabled because most users don't need +# this feature and the feature has some overhead. Note that if you don't +# specify at least one of K or E, no events will be delivered. +notify-keyspace-events "" + +############################### ADVANCED CONFIG ############################### + +# Hashes are encoded using a memory efficient data structure when they have a +# small number of entries, and the biggest entry does not exceed a given +# threshold. These thresholds can be configured using the following directives. +hash-max-ziplist-entries 512 +hash-max-ziplist-value 64 + +# Lists are also encoded in a special way to save a lot of space. +# The number of entries allowed per internal list node can be specified +# as a fixed maximum size or a maximum number of elements. +# For a fixed maximum size, use -5 through -1, meaning: +# -5: max size: 64 Kb <-- not recommended for normal workloads +# -4: max size: 32 Kb <-- not recommended +# -3: max size: 16 Kb <-- probably not recommended +# -2: max size: 8 Kb <-- good +# -1: max size: 4 Kb <-- good +# Positive numbers mean store up to _exactly_ that number of elements +# per list node. +# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size), +# but if your use case is unique, adjust the settings as necessary. +list-max-ziplist-size -2 + +# Lists may also be compressed. +# Compress depth is the number of quicklist ziplist nodes from *each* side of +# the list to *exclude* from compression. The head and tail of the list +# are always uncompressed for fast push/pop operations. Settings are: +# 0: disable all list compression +# 1: depth 1 means "don't start compressing until after 1 node into the list, +# going from either the head or tail" +# So: [head]->node->node->...->node->[tail] +# [head], [tail] will always be uncompressed; inner nodes will compress. +# 2: [head]->[next]->node->node->...->node->[prev]->[tail] +# 2 here means: don't compress head or head->next or tail->prev or tail, +# but compress all nodes between them. +# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail] +# etc. +list-compress-depth 0 + +# Sets have a special encoding in just one case: when a set is composed +# of just strings that happen to be integers in radix 10 in the range +# of 64 bit signed integers. +# The following configuration setting sets the limit in the size of the +# set in order to use this special memory saving encoding. +set-max-intset-entries 512 + +# Similarly to hashes and lists, sorted sets are also specially encoded in +# order to save a lot of space. This encoding is only used when the length and +# elements of a sorted set are below the following limits: +zset-max-ziplist-entries 128 +zset-max-ziplist-value 64 + +# HyperLogLog sparse representation bytes limit. The limit includes the +# 16 bytes header. When an HyperLogLog using the sparse representation crosses +# this limit, it is converted into the dense representation. +# +# A value greater than 16000 is totally useless, since at that point the +# dense representation is more memory efficient. +# +# The suggested value is ~ 3000 in order to have the benefits of +# the space efficient encoding without slowing down too much PFADD, +# which is O(N) with the sparse encoding. The value can be raised to +# ~ 10000 when CPU is not a concern, but space is, and the data set is +# composed of many HyperLogLogs with cardinality in the 0 - 15000 range. +hll-sparse-max-bytes 3000 + +# Streams macro node max size / items. The stream data structure is a radix +# tree of big nodes that encode multiple items inside. Using this configuration +# it is possible to configure how big a single node can be in bytes, and the +# maximum number of items it may contain before switching to a new node when +# appending new stream entries. If any of the following settings are set to +# zero, the limit is ignored, so for instance it is possible to set just a +# max entires limit by setting max-bytes to 0 and max-entries to the desired +# value. +stream-node-max-bytes 4096 +stream-node-max-entries 100 + +# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in +# order to help rehashing the main Redis hash table (the one mapping top-level +# keys to values). The hash table implementation Redis uses (see dict.c) +# performs a lazy rehashing: the more operation you run into a hash table +# that is rehashing, the more rehashing "steps" are performed, so if the +# server is idle the rehashing is never complete and some more memory is used +# by the hash table. +# +# The default is to use this millisecond 10 times every second in order to +# actively rehash the main dictionaries, freeing memory when possible. +# +# If unsure: +# use "activerehashing no" if you have hard latency requirements and it is +# not a good thing in your environment that Redis can reply from time to time +# to queries with 2 milliseconds delay. +# +# use "activerehashing yes" if you don't have such hard requirements but +# want to free memory asap when possible. +activerehashing yes + +# The client output buffer limits can be used to force disconnection of clients +# that are not reading data from the server fast enough for some reason (a +# common reason is that a Pub/Sub client can't consume messages as fast as the +# publisher can produce them). +# +# The limit can be set differently for the three different classes of clients: +# +# normal -> normal clients including MONITOR clients +# replica -> replica clients +# pubsub -> clients subscribed to at least one pubsub channel or pattern +# +# The syntax of every client-output-buffer-limit directive is the following: +# +# client-output-buffer-limit +# +# A client is immediately disconnected once the hard limit is reached, or if +# the soft limit is reached and remains reached for the specified number of +# seconds (continuously). +# So for instance if the hard limit is 32 megabytes and the soft limit is +# 16 megabytes / 10 seconds, the client will get disconnected immediately +# if the size of the output buffers reach 32 megabytes, but will also get +# disconnected if the client reaches 16 megabytes and continuously overcomes +# the limit for 10 seconds. +# +# By default normal clients are not limited because they don't receive data +# without asking (in a push way), but just after a request, so only +# asynchronous clients may create a scenario where data is requested faster +# than it can read. +# +# Instead there is a default limit for pubsub and replica clients, since +# subscribers and replicas receive data in a push fashion. +# +# Both the hard or the soft limit can be disabled by setting them to zero. +client-output-buffer-limit normal 0 0 0 +client-output-buffer-limit replica 256mb 64mb 60 +client-output-buffer-limit pubsub 32mb 8mb 60 + +# Client query buffers accumulate new commands. They are limited to a fixed +# amount by default in order to avoid that a protocol desynchronization (for +# instance due to a bug in the client) will lead to unbound memory usage in +# the query buffer. However you can configure it here if you have very special +# needs, such us huge multi/exec requests or alike. +# +# client-query-buffer-limit 1gb + +# In the Redis protocol, bulk requests, that are, elements representing single +# strings, are normally limited ot 512 mb. However you can change this limit +# here. +# +# proto-max-bulk-len 512mb + +# Redis calls an internal function to perform many background tasks, like +# closing connections of clients in timeout, purging expired keys that are +# never requested, and so forth. +# +# Not all tasks are performed with the same frequency, but Redis checks for +# tasks to perform according to the specified "hz" value. +# +# By default "hz" is set to 10. Raising the value will use more CPU when +# Redis is idle, but at the same time will make Redis more responsive when +# there are many keys expiring at the same time, and timeouts may be +# handled with more precision. +# +# The range is between 1 and 500, however a value over 100 is usually not +# a good idea. Most users should use the default of 10 and raise this up to +# 100 only in environments where very low latency is required. +hz 10 + +# Normally it is useful to have an HZ value which is proportional to the +# number of clients connected. This is useful in order, for instance, to +# avoid too many clients are processed for each background task invocation +# in order to avoid latency spikes. +# +# Since the default HZ value by default is conservatively set to 10, Redis +# offers, and enables by default, the ability to use an adaptive HZ value +# which will temporary raise when there are many connected clients. +# +# When dynamic HZ is enabled, the actual configured HZ will be used as +# as a baseline, but multiples of the configured HZ value will be actually +# used as needed once more clients are connected. In this way an idle +# instance will use very little CPU time while a busy instance will be +# more responsive. +dynamic-hz yes + +# When a child rewrites the AOF file, if the following option is enabled +# the file will be fsync-ed every 32 MB of data generated. This is useful +# in order to commit the file to the disk more incrementally and avoid +# big latency spikes. +aof-rewrite-incremental-fsync yes + +# When redis saves RDB file, if the following option is enabled +# the file will be fsync-ed every 32 MB of data generated. This is useful +# in order to commit the file to the disk more incrementally and avoid +# big latency spikes. +rdb-save-incremental-fsync yes + +# Redis LFU eviction (see maxmemory setting) can be tuned. However it is a good +# idea to start with the default settings and only change them after investigating +# how to improve the performances and how the keys LFU change over time, which +# is possible to inspect via the OBJECT FREQ command. +# +# There are two tunable parameters in the Redis LFU implementation: the +# counter logarithm factor and the counter decay time. It is important to +# understand what the two parameters mean before changing them. +# +# The LFU counter is just 8 bits per key, it's maximum value is 255, so Redis +# uses a probabilistic increment with logarithmic behavior. Given the value +# of the old counter, when a key is accessed, the counter is incremented in +# this way: +# +# 1. A random number R between 0 and 1 is extracted. +# 2. A probability P is calculated as 1/(old_value*lfu_log_factor+1). +# 3. The counter is incremented only if R < P. +# +# The default lfu-log-factor is 10. This is a table of how the frequency +# counter changes with a different number of accesses with different +# logarithmic factors: +# +# +--------+------------+------------+------------+------------+------------+ +# | factor | 100 hits | 1000 hits | 100K hits | 1M hits | 10M hits | +# +--------+------------+------------+------------+------------+------------+ +# | 0 | 104 | 255 | 255 | 255 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# | 1 | 18 | 49 | 255 | 255 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# | 10 | 10 | 18 | 142 | 255 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# | 100 | 8 | 11 | 49 | 143 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# +# NOTE: The above table was obtained by running the following commands: +# +# redis-benchmark -n 1000000 incr foo +# redis-cli object freq foo +# +# NOTE 2: The counter initial value is 5 in order to give new objects a chance +# to accumulate hits. +# +# The counter decay time is the time, in minutes, that must elapse in order +# for the key counter to be divided by two (or decremented if it has a value +# less <= 10). +# +# The default value for the lfu-decay-time is 1. A Special value of 0 means to +# decay the counter every time it happens to be scanned. +# +# lfu-log-factor 10 +# lfu-decay-time 1 + +########################### ACTIVE DEFRAGMENTATION ####################### +# +# WARNING THIS FEATURE IS EXPERIMENTAL. However it was stress tested +# even in production and manually tested by multiple engineers for some +# time. +# +# What is active defragmentation? +# ------------------------------- +# +# Active (online) defragmentation allows a Redis server to compact the +# spaces left between small allocations and deallocations of data in memory, +# thus allowing to reclaim back memory. +# +# Fragmentation is a natural process that happens with every allocator (but +# less so with Jemalloc, fortunately) and certain workloads. Normally a server +# restart is needed in order to lower the fragmentation, or at least to flush +# away all the data and create it again. However thanks to this feature +# implemented by Oran Agra for Redis 4.0 this process can happen at runtime +# in an "hot" way, while the server is running. +# +# Basically when the fragmentation is over a certain level (see the +# configuration options below) Redis will start to create new copies of the +# values in contiguous memory regions by exploiting certain specific Jemalloc +# features (in order to understand if an allocation is causing fragmentation +# and to allocate it in a better place), and at the same time, will release the +# old copies of the data. This process, repeated incrementally for all the keys +# will cause the fragmentation to drop back to normal values. +# +# Important things to understand: +# +# 1. This feature is disabled by default, and only works if you compiled Redis +# to use the copy of Jemalloc we ship with the source code of Redis. +# This is the default with Linux builds. +# +# 2. You never need to enable this feature if you don't have fragmentation +# issues. +# +# 3. Once you experience fragmentation, you can enable this feature when +# needed with the command "CONFIG SET activedefrag yes". +# +# The configuration parameters are able to fine tune the behavior of the +# defragmentation process. If you are not sure about what they mean it is +# a good idea to leave the defaults untouched. + +# Enabled active defragmentation +# activedefrag yes + +# Minimum amount of fragmentation waste to start active defrag +# active-defrag-ignore-bytes 100mb + +# Minimum percentage of fragmentation to start active defrag +# active-defrag-threshold-lower 10 + +# Maximum percentage of fragmentation at which we use maximum effort +# active-defrag-threshold-upper 100 + +# Minimal effort for defrag in CPU percentage +# active-defrag-cycle-min 5 + +# Maximal effort for defrag in CPU percentage +# active-defrag-cycle-max 75 + +# Maximum number of set/hash/zset/list fields that will be processed from +# the main dictionary scan +# active-defrag-max-scan-fields 1000 + diff --git a/docker/deployment/configs/server/config.js b/docker/deployment/configs/server/config.js new file mode 100644 index 0000000..d3bdb02 --- /dev/null +++ b/docker/deployment/configs/server/config.js @@ -0,0 +1,158 @@ +const os = require("os"); +const ifaces = os.networkInterfaces(); + +function getListenIps() { + let listenIP = []; + Object.keys(ifaces).forEach(function (ifname) { + var alias = 0; + + ifaces[ifname].forEach(function (iface) { + if ( + (iface.family !== "IPv4" && + (iface.family !== "IPv6" || iface.scopeid !== 0)) || + iface.internal !== false + ) { + // skip over internal (i.e. 127.0.0.1) and non-ipv4 or ipv6 non global addresses + return; + } + listenIP.push({ ip: iface.address, announcedIp: null }); + + ++alias; + }); + }); + return listenIP; +} + +module.exports = { + // oAuth2 conf + /* auth : + { + lti : + { + consumerKey : '_bo2uqnwon1ym4qkte5hhd4fzlnoufvts5h3hblxzcy', + consumerSecret : '_1xpnaa4iw36cwpnx7991e630yo0u4044so1crhvcnz' + }, + oidc: + { + // The issuer URL for OpenID Connect discovery + // The OpenID Provider Configuration Document + // could be discovered on: + // issuerURL + '/.well-known/openid-configuration' + + issuerURL : 'https://example.com', + clientOptions : + { + client_id : '', + client_secret : '', + scope : 'openid email profile', + // where client.example.com is your multiparty meeting server + redirect_uri : 'https://client.example.com/auth/callback' + } + } + },*/ + redisOptions: {}, + // session cookie secret + cookieSecret: "T0P-S3cR3t_cook!e", + cookieName: "multiparty-meeting.sid", + tls: { + cert: `${__dirname}/../certs/cert.pem`, + key: `${__dirname}/../certs/privkey.pem` + }, + // Listening port for https server. + listeningPort: 443, + // Any http request is redirected to https. + // Listening port for http server. + listeningRedirectPort: 80, + // Listens only on http, only on listeningPort + // listeningRedirectPort disabled + // use case: loadbalancer backend + httpOnly: false, + // WebServer/Express trust proxy config for httpOnly mode + // You can find more info: + // - https://expressjs.com/en/guide/behind-proxies.html + // - https://www.npmjs.com/package/proxy-addr + // use case: loadbalancer backend + trustProxy : '', + // If this is set to true, only signed-in users will be able + // to join a room directly. Non-signed-in users (guests) will + // always be put in the lobby regardless of room lock status. + // If false, there is no difference between guests and signed-in + // users when joining. + requireSignInToAccess: false, + // This flag has no effect when requireSignInToAccess is false + // When truthy, the room will be open to all users when the first + // authenticated user has already joined the room. + activateOnHostJoin: false, + // Mediasoup settings + mediasoup: { + numWorkers: Object.keys(os.cpus()).length, + // mediasoup Worker settings. + worker: { + logLevel: "warn", + logTags: ["info", "ice", "dtls", "rtp", "srtp", "rtcp"], + rtcMinPort: 40000, + rtcMaxPort: 49999 + }, + // mediasoup Router settings. + router: { + // Router media codecs. + mediaCodecs: [ + { + kind: 'audio', + mimeType: 'audio/opus', + clockRate: 48000, + channels: 2 + }, + { + kind: 'video', + mimeType: 'video/VP8', + clockRate: 90000, + parameters: + { + 'x-google-start-bitrate': 1000 + } + }, + { + kind: 'video', + mimeType: 'video/VP9', + clockRate: 90000, + parameters: + { + 'profile-id': 2, + 'x-google-start-bitrate': 1000 + } + }, + { + kind: 'video', + mimeType: 'video/h264', + clockRate: 90000, + parameters: + { + 'packetization-mode': 1, + 'profile-level-id': '4d0032', + 'level-asymmetry-allowed': 1, + 'x-google-start-bitrate': 1000 + } + }, + { + kind: 'video', + mimeType: 'video/h264', + clockRate: 90000, + parameters: + { + 'packetization-mode': 1, + 'profile-level-id': '42e01f', + 'level-asymmetry-allowed': 1, + 'x-google-start-bitrate': 1000 + } + } + ] + }, + // mediasoup WebRtcTransport settings. + webRtcTransport: { + listenIps: getListenIps(), + maxIncomingBitrate: 1500000, + initialAvailableOutgoingBitrate: 1000000 + } + } +}; diff --git a/docker/deployment/docker-compose.yml b/docker/deployment/docker-compose.yml new file mode 100644 index 0000000..7bc9c17 --- /dev/null +++ b/docker/deployment/docker-compose.yml @@ -0,0 +1,49 @@ +version: "3.3" + +services: +# multiparty-meeting + mm: + env_file: .env + image: misi/mm:${TAG} + build: + args: + - BASEDIR=${BASEDIR} + - MM=${MM} + - NODE_ENV=${NODE_ENV} + - SERVER_DEBUG=${SERVER_DEBUG} + context: ./ + restart: always + volumes: + - ./configs/server:${BASEDIR}/${MM}/server/config + - ./configs/app:${BASEDIR}/${MM}/server/public/config + - ./certs:${BASEDIR}/${MM}/server/certs + - ./images:${BASEDIR}/${MM}/server/public/images + ports: + - "80:80" + - "443:443" + - "40000-49999:40000-49999/udp" + network_mode: "host" + stdin_open: true + tty: true + redis: + image: redis + network_mode: "host" + entrypoint: redis-server /usr/local/etc/redis/redis.conf + restart: always + volumes: + - ./configs/redis:/usr/local/etc/redis + ouroboros: + container_name: ouroboros + hostname: ouroboros + image: pyouroboros/ouroboros + environment: + - CLEANUP=true + - INTERVAL=300 + - LOG_LEVEL=info + - SELF_UPDATE=true + - IGNORE=mongo influxdb postgres mariadb + - TZ=Europe/Budapest + - CRON="* 2 * * *" + restart: unless-stopped + volumes: + - /var/run/docker.sock:/var/run/docker.sock diff --git a/docker/deployment/images/background.jpg b/docker/deployment/images/background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..71365515d00179cd545c874425ac15c2063318f4 GIT binary patch literal 50043 zcmb5Vc~n#P_BQ^>A&DV?1_*%!)BpwpMyePQQR@kkFesW}qye!FGl_r*Xwh1GKtM(z z3{e7T!X#5IAVpB`y`X|X1&N|{t_pQ(wJM#p?Ynco@4J4#^{(~%<9E7@7*0-h_I~#B zJbRz?pIiStM2uibkOYxPBoqPvp?~h84Ty}HnVDf^cp$?;F{e`Cu%OYX=2jL~R#q04 zmU9_Q>$&r6=UH0XIM~?QvFz>b=UTHJ*(?r|WzRBQgk*Xpg+ixN>8yE{^H~4CkAE&8 zYZ}>S4q--eL6|kk%$oGiCFEcV6@vYL9VE)tuSXu@Tr*!b_4^#kWUIt4s3lIt6gS6TUPngeZRvGhaV_HPu^@xiz!c}m zw0UmBOhk8@@QH>Y2#)#b>l4doT{1Li~?ON=vU^ElZG9=pcrPR>ML96K?pB}nPc zIk`ruwMmT5$Rn*dU&iS!!Y%k_TXA#$({ac=>MSB<=&|x2Bq@0j>@)G6IWm?ug>BHj z-6-_X7_`HCGNO~xGo0?S)mV8_`pvhv)ZTPzj*hLazJ-vn%GzIDUE@&atR0>|XZ6k6 z`_A8lX*M$BQ{46hPdtuAq!oS2#z_*(Mh!WmZXea=B{wIc%BYq&L&0SF4F*=op2*{j zP;o<9kYW6q1B9=@(eR_P6^1fAs4bdkPQQLki^8K;7z)lGn-h}Yml)TAhibD_HL8_V zLbk+TW-nQ(m+^)5V~Fw~LrhpD=rLJMMnYIM!nD~kM4>29clFia^czS}FRQY`WXNov zu`0WVgt~Kywt8$RA2s;qCAC#c;iMk28nqb)Pn{;29Shdr@8>auY-kXMMv=q>%`$ldEF!)VJ=C(>2%Z9cNCwq z`aB)QwUFEldMRBH_LH! zCO$rIdtUsbI-%G)$}KP1DAXeoQ_>rK3t_VAv+Vo45Mc(W18aY*|$fVopM4d#lyf%A0%n5r~9eBkcY4-Pt929zA75;Akah) zWS3#?pBU9O-WPA=IPByS6yu6M2%CxnXtw9Y5%q_BM_2U41AHr$lm>58)GZs`d4S+= z-8eaz!mnD!j8|JrcHdpR(%N5EaD7pV#!zLQI4MQ^Yt_A)7MzrHD`KPHn8YAx(sw2{ zrW`xVPO*7)>;_s{X9L;UX5AK_3qUC!H*et!^ydCOq&T+PTE(YJQDuB<-46C-gw0N_ z1)r=lPTWHD%IJ)OTssNw?)DZ(?zKN~_2v?{w;@u*R`8r^*onaiUvW0Q2Vc?RS1~_o zXfiz`&++h*K0HWi=6llZQFT|@iWVqO!8Hd%8GqAHsA-~mg<H+{!x56myy7 zu0>M~z9R^jYCNjR=DyA3mk6Eqr|Q@;W<;Z6WgU_?q~jLu@di>mzY^c^6lYpTB^&wS zEjXPSXQ;Bp5gqVBZLPtZBXYEuHV5mWN1d;B2fV6Q8kl-s!HT|EgtQ3WTZc&1*Vqxk zHi=PvaljLa=@7a8w1frEpD#l|ac+mb-->Ny+U}r?C}m|VVk*u3FN-exJV4f3sH&Bf z3G3##J*s`dXp9iEFK+@>$<-sP1pl@~bARf9OlYj}-vM|LG-qkMlaO1_#601NaEEAK6m8AqdnrOY6A<0{5Ao*o+TA@bl9NO{@pWRw5fk61`cSt?`!$C+`{Epi4blJq;|YVEPVZqqZ4Kr1r*jX$d>`J$xW8 zLvT=AOa!}TbZKKmTw7dQd`2FJ-CKP|;t0hmYKxvkARun7+8mi@-u9Dgy5I-XC4tzk zFRIVV&F4>aZ(|5BwHr(ZhO%xKwv z^AFtZn3vwC)Y~Zdn_fBWbc;hkhme76OJ=ET}*)KiR&m5ZX>W^t^#imzmGvCp6u>##p1kJDwH{!zhA4Gle zz-EAN#J}a%P1;jO(09aFB7rgPQ269T9^PFN40seGy5#xQh#az6Xl^5DyUl%Y4_!rF zSLLLndVndSX1VpmMGxdUB*pn7XgG=6BhQx0fl*3iyT&1fodmpio?#VC$}U4oZP$#l z%q2L#GEHsmPmMQlnpaGv)#N2x`%gTYxWyx&)Fc)LveOsKpq%u64LM%}T$xennL%QD z_5c>@<}BSQNdYO33t=k+jZ=E$t!J_oJ3|lo_X3^t#YOGmSB50ume1nHnT-(}RZN5| z+^hSddqSjItU?YhAcTP(+sW%DK+FV9UzQUB1p!E?u3^Io1s!GjC4gjZ+vKL7+(Dtn zTM#vjQBPRDKtV0>J6&1~%deA38yxd+t$DzzP1TqJ>_>~ZQC9oPA;qC)083228d+;;*imeT`Kj+ApOB=sLQYj>ww1FDwz9Mn(;Lmp^ygaiDrqAQm`Q7v2^TbH8+-_ zE`(yj9}~hcS7I_2!o{HHWU`rW4!IJKmoY&GH$8)RwQR(WBzT8>9V*4yG8$LRL=k!v z!FuZ77J{teD>-_^dMbA3tDjVYE)#SQO`=|k8o`31N>e0=j}1s6QH|1WqMjhIY=Pdw zPw{22CYm5)GGKxQG@RISSmY0j!6K}=Y3Md{# zq-IY51ydn$uetD$hnsQ0gHwV9MjRVQ0?rhNEMu-L(aD&SyibLQ(p^L`+czogu7r*Z zErO(si3tlnPyiG7D&EKkKUA=h4+ta>KO(rDDMnxe{bhU_FP|#Mnh5hCw{BD!oQi45_>$wm*2lG-teEGTWJd{u4)y@{~K=}5rz7$Zo2mApn7ACtH7 zktt>-Q~_Z$EfNzjLQEK-T~Q^D5#}aL8OrKWfj`7Wjqm1?An2^BL*9t)2f73_sspppL?Nk`+J{3BLU~g+A*Dy;l@sE6f~64X^}r@*5=tV~ zV{Kzd&>G!=T(|m%4l(GM1_m+cXCUKjm;_~f9$RMOI;l)sKV%(Mm2U#qSgbj4q}m3d z<$X%X9~TG)rxBLBpc$0ZbQt{pTSID?X{w-R;qJhUZBl{0@tS5sFrwVdKootq4%zXi zCdpGwD{L^1(wt0S1VZ)jXG(9=f-smf#3+IfCYJ)rjLg|J1=^-KRSoVL#)WscjwX;b z7>h+vL5%2E5IhFt4|%b5fbL2$n0>drxDgqD`T24gbJGu81`2}d6ow8P1x^w)VS{_> zKqH#47JWCNeu|?V2{`y?5JGfXtS2QJ;;j9k_bP)MBLGbTJwk0_K~I?}Yw&{uZH=>_mf zOHfo5VIEbLqu@f(tE{7{Tf`tNB#*qjWUF``)Quj2q#hMeLi?@$i5vZ8D}^vQ@2w}G zevO7Wf@y{W;1QNh(0gn`#o6PCF4dDl2|5{dPwB}D7mYgpT0SpZ#*71wYzaoZ>@m|Y zf|05i7>fj;f`nnChP=))tQ_1ooC#tKPVxsX3N%H6DiZ)@=4mvDkHke&O~E{CNnvn3 z(S(}7VewH@r%K2SW)by9Qa!W?U>HH{oca9><#U-Q5gW7Rv?lh|_v$Fwg(j^2QsGneRv?m=_rh=MkRq z1z_2OAfXel0j08n4#W&CAH+}NO+W#mg{-I0WWvTC5Gg?upGvsn!Vtu?78$fTJ@8>Y zG%AlW#KM5aHVR=fRv>p)c`J*AWtUgs1{xVv#wn|$h`{wc29Dhf12@|OZ=>MlSBeWO zgQDr;=7_v_@Rx~E{<6GoY*bkp*HYm-0wJ3D<}t-=;6IRHWch+kM*mY39-5Wku4xlRnBLofMlWhW~g}X>i zn7X4IB(M@8Ax7}gB1I@9u83dX0l#MgiB2pgHnJB`rIe8+f< zr5^n4x^RE#)TD_oL`luWl-_#7zFUY%pELxEZN{wtR_-1ee>CYaWGzz1wRE~fgm?@g zI)*GjP*bth9vj9&1x*UC_2;N;rL1bht2?Ip_S9}oBzmtoYzz<+u^TOEf zON(G4FDS!1o~5Z5DHP^WJOX;c(g>RhF3w}q$y%$6f#B>Fzs=&>_>iP6zsyY=*COIuZ!^h5 zwaI$LyEAE|+yZ95ZVbGU1Ncy|l81bAEZn*aAkF={QH)LkLJ)_JwU}kXQf;M>E<{!o zAcly>wv8KuHR={XUT7H-@_|*%{o4vb7DGcedI0n+Y$`!Q%|ZtNg*b^qCQ+ObYzl#7 z<3#Act_w%lHlPv~HrmP{;bMX?Es3-?>P0RDHExjw&n=GNv$b4wdd(ApmmPZL#UjXS z^a>@NtI&>D-{9MM2=tTGa)Q2{-(5-9v&N*TO}dg#?a+f*3!1Zs@+b93j~aUrf^mo3 zD8rEeA^O)~6=7!PlbEn6vq@n~0aPjzUr3e`AVph!*$gHIlLdxlxi#Vxl3P8lC-HJ* z#jo7L_-vY%VVN;nRY;A?_ZYL0LH1(JqsmNs!&ri8A6i7Ach|S*Ve?=n!cT9&kx9v$ zDE#gsAvRgqC==RksFo#e7r6@GJJeS0vbV?DWzlq7{3so_H!nw* zgJ?xOSR#gVf=MYE((QFfe^g;?2a@&daI0(V;Lm65!Q_3JQ zqztY(IbT)h88;S=7)yM|Xe&B86vT=Sg4<|E)?H8tvPo6hE?I8fKv12kI*)=R)}X## zZ?7;WEe%fOT2Nm}%Fgz;?$OCP=YP(KRcXFVH}CL4ShRA=%yC{0gCgo6atR)ZjGSfLNaoVU z0%dC&FP}UPk}k0KhEPW^*^2XxSu0w)qR~V*El5cj&k;!@Z;?DR^r*Y}_GCA)BPI?E zw+^SiQsE#2PwLo{l=`oAmGxsi7S!t2U~EdCcPbHR&bd*K1QLxLg~DIulO(3WE?j)m z-SjMzH4}~y5{Y?bTrI}pBFsW33yOlcVt5<3LBoWTNq#iQHJQ-5L=v5rKk&KZMbgN7 zRnDzgBoT|mizd1Q5sr7O7m>9z3u<;Hu~MgU$Gf$I&FaZSE^Dm!Y2eKa2AZ1eRF9LF z7Vd~uKUE@PrADtO1nnuR&?0wO_R8(}lzvVyf)6yrliOM26mai(WpdUNEriEqI!B8| z(V$eA0tZ@;6Vb3xMG-|S=&baJBmf6=GR74}TT?{Jz=Y!xiod`Co5 zS03zxJTpF2Xy$u|=NQ&o*fff~nf?OaBnBsxK zMXO3!gY7ygX-{Fwhzj1gN6&#At>jZ~Qm+vLPB4}gh$z9@nG}~PI}dpq;-TEEJ(Xvf zh{hU2oif&p$_{6AbK=6BrBY{*c3veCm&v7NOwuBSg>i#;8M`+u>9?vM|T{C+Xl{4DL!oFJnEaloCuRIhVrdRExw& zi!8N}JKQ{sfhtip17T7V=*+yisGi0X#T9Ci3)@d_N6yDix5RQ$P;}EsnHIdT%TiIO z4UJ4-BVM`K*mer#G;Rlf>=ll|Bw(2BI-p`ofAcB#R!VTnw9*Pz-Fi?}Y*3T}eMAW^ zAJjKO$yI(GOe8}vACU0Q?J#t9!t`Z_3J?X67KOVD>?mAbI2nmW(r_jjVRrnZTrO&1 za4j>iI}bik1)TOb|AfaDT}jlitK)Jgni>Iu*ZjyL%E7?U)vu+K?{b^iP`Yb4wyK8IEXI9 z?7~GRKyhspaUgt=`PP6rxDQsrHRGBqvuHRv%C+&5gZMz1I(N+81~Ka{(j~(yGvX^P zsMo5%^>S`XPGzXGfPz|Xs@g$(1$rO~NEXx^AgAf~Ag~K6cmTkGq#=?8oCCGQIGSzz z0+LMGDX%9XMi~zV%?5tohZo;><84e9`+t| zN-%~1YWWJ6F{xh8i37f~!zrRh9EsvAYAC@x0%M7!a$0UM;u76<8XRUUC~rApxXdU7 z3XGJl%ny@P+w*C(aEt`#Uy&vU_l$TWSuE68Kq5iD1M>(`yG{YR0Cf!2LR^Uqx&48k zlY#Lpgk0FxN?y6Q>i~=0Fguvqvk5URC_#>WVYfNKdeq;lq$NWZA?IK7k>7>H2y76~ z5`_*B3NoES;ACedi3=D7!c}D3QT(Qm3j&xcNU#K;T~LeCm2~n^^H>((9_3!K+37ME zTCK3LBv2p4thr(xGP!Kr1|O*OQW1;|K;(xxRkPzz7H9 z2)mK)Y`%qiXq9 zA$~5NFXxG*fFgwDFXD-yS#qJotU#3}ltl>+0`R-=ZGFL_`i^9Q7a3&HvSqv+gko)A z+EQX(Tv&JrrKHAsAU9Ea33#I&k83*REb1$vTn__lb9Hl>+~ikSGdmCb3@Rb+YbGd& ziGfsrUul+{O6=Tj0ZT4XN>F24H^HUnLn2$yc50cF5hjkW;veOMxQcr)NESiM^^-F( z8o}o>_)u7+br~SzSY!^k@Qi`OU zAmKR08F~|L0qCK5DA=IiBq@*-IuOc0j8L%T$lOKV4z${+pyiv)r(aWkpaOM8%b5aN zIYMZZOX4wTgqaJ10@C27ipk)Y0mp1IG^=dvfRSKEMx4Ip+fBepD)%Hk2Gwii^f%|r z^M1K1Az)zMuDLcX?&nZ&5FrQ*rWk5Q0GnB z$2w889qa)uD0CuFjRzArLb=sUWW#a>Y(Op109j!J{DNB&xEFMcC>D$+X1S9GykmK< z48zl`jSKN@aqglARtcGOcDB+;i=GH!Y^6lh0`GWVwYxh!zPls?YL zN}c*4PzH_En}KB`=Vq12FAH*MKzvRDo^P?IFj>dt<&_ttp%Fm?%g%#0+Frs^(8BYR zbv+0VHxJEbZPHp`^3h&Nd0B2>-V9g1Gvu6DTOJS$HsFQh zq{_VNylO{f#<%l9rc66=Fn6~ga_RPC83~e8?)p_FOg(M%RPFO?vqpg5w3qFv^WzED=iE-C;`S9STQYNRWeA ziZajUAZ53rh;Jd?DIl`Vso70L0SPbG1%r4LC_7UEQ6Pyo8jCFLSr*jB(O&B9+!0cB zR45URUpT?Ybc$D*RXoHTuFUt8JaO*Vw0ec9@J;#f zjRPfl+-9&@<-)XIwa66r2sp!3Qi@nh%S#a^yRRxKr*zkSu+%YHXYwI)Leg(5k=e<0 z;7Imledp6$w&O`oUUhR}D9y2d66J#XqTX&krNRRh7VgrWk(YzAda1V@z)noYsl#7}oscx?3^x1nAL~qx$PShwf(hmn^^&eAZ z_0%g?6-M5|vb``nY9oN*NtzllFdGFae6*E%ujl0l0H0i~^x71xZTd^0K)TdAM^*)cGr%@-j2G)6Vr~F2J(_ zy(N5Qfl^#WDK}SYQ4n(MCh%lF<-2u28BK@6o4|y*8^Iryt<)I#6!HV1bXC^1$2#z9 zRkM5Gs=ljJJ7JQ?UZ;nhO2&Ek`FTtDC;RzFL)3&qFcpl2lb>+uf|ch zok?g_VIsur3{{ACmnvYE(dbv!K^8NRS>}8P4nZaI!Qjz0y5rQZ0BwEq9on0L0<{lQ zW)COLI#%0pJ9}lAiv{0BPxQ{z#B-t@t06=WzwO|y2DByXvbs5I06`-#%FLx<5kV#D|zJvEzle`?zoHnGIVqi z(J2ii9|Fzt%7)u9xf4+xBWhTR?1--Ehn@_EQgT6AsRR{RkPl)qdmDe?-fc(uZmDCu zqcZu*-gIN5V=UaqfYTvM<*b>MhZ*T70hCfkvPLOr>x5qXb{!%$lJlCQj5-*UuOuR% z9TL{aMXoMmf~B!K5kU1xHj=j2HaClr%>Y4#ZswG6)sJ<-od9!+GkjTVW~ZQvLSDA> zm|#TIEGb(Ng($^Z9Nc!e7XU?_;MNV_wIWo7Dhl5hQboP3XTo&LHF;`Id=Uk468y%na-OKMDKNQ&g;RpJ>*Vzpp4V|L zELvRo?G#!unD59Iw{n&!)bUu;CS7>}bYOE-t4_|$2JAN4fk*nvC*xR?NDB7Ul`>&K z&IlGU$i>200a}!=4h$3HWG&g8Km1z21Uh7Q6QGIh@VcKq%!QvjcSZK7_}Dn$S}0ZV zozG3wz_4=uR~S756|6Bq-mT`JmiJ1&p3s5Yq6E{p((U;feU8y}ANZ=l2Lg2zhKaM< zfzbvxRfIQn_ReISZ!>k@Be0sDvovHu&)14bp#^F1Jtqr+n;zwJ1%4NBsj=Ug^ft6fMrLxS2%2Egm;06}R2Tb!jl*#U*b5|u#R4`6qlonEBBW&FIIem`UmOnB7(c5~ z&vzMYFWd=GyOt$r4Mdo;!1I(xa^cQpn4YlIRX25FO=H_N9oaBW=fF+6k%W6UZZTVpC$=8XLQ-sHec>swf1gA|g@xje7b@ z4df_A)#!NsXO&58ZYJO`Lv=g$IrbUDIm(o`#%A%i9vRUfDxAJsTm)YUB48^3oUsES z-$rv*0%){@^&TTCJ}n;`AVs@ecuw-tRU<(=yT#0#6THzGdVWPm5w+TJ0&GYf!bve) zw4!BYVYu=FLPH~nHX54EK(=mO;6ahx`q)lBE@z>Ks7gl<;08KqmoSxV^S+(b7QmbdYI> zlBH}nKeN17J=I8BrW?xbH^#yMA+;nz85R_<;hoYTCCZ^_k*c=NjoQe7VaYN2klULq zV8%$j7P(SHQngpOb8K$biYS<+y&_?FMb6>&0$n_TOZh@30|{B#xn{Utk65|-NgO&o z1CiR8_FO7~I3vh_lQDvWY;s|&0=t=&$kivhprv|qt`ub0LWT`gm`scyD$-MxILazRt$f{rKw5= zfr;SD#5DK?iGod;OD;_QzV*-bA>yLr0qp4JRgM;Z&we;XAM({~PMklHr@yyV(YsZk z=#pY%5p>eG-0 zw!?qOX4xJ`_${BV{eDn5x4C|3g!9!Q@0*^^x1-zkM$9j@imqSf)KthTTp5zC6>M#% z+V1eHw=TJny0pVAXFq(CcTf}kIA06zj=N`_9U9$QcQo?Fsjte+j@O5M%Qw1|Pt&6* zh2oaS9rA4(M%QljvCVR5?oziM8Ef3PX!qt9QRueR(KEQC#o^ME`ub=^&l>hemXg!& z^TUc*G|CN+s?TrdZtjY15!zT?CgI+nQ2WNxmM6w6%a>f({pJU|)8fR2RTG!|G8Fy^ z;!L$GgC>47CSBsDVdWFA{xkQIJ(Z(VLxi+s4+KS)ck3>jNY0USe06b;l<~*MmGDI z{tZi@=#f|P3R^30!d1KX1>|?7EOIRVg#8cNRmb|^dYdtNsDJ88>_@p}-kc7_S4Xnz zTaU5$;|+l$u8fX|e92=`oHX*rl{M9Y_h!F+_@hA1`iAAPgw@XCvp%?yH08=#I`8q& zpZYb&J%)<5>2Gab#QbQPVh#Jq6GPjl%h@A2^cX9?)@ry5!w!<)qd#Zy*QFQdnXjz~d_y+7I&Z*yPGeEb?u)z+x~hm=s6P1P0mYxQhUawGi={&)hbtBwtM_Kk`EFs6Bi+S% z=47E~!Q~ouUj0Dre(u@zC$9!3u021T$llV(zM8aedF|Z~b23(0&U~);BgUZ5jJXH`>^}3bWR+mfX&$~R&WxbQD<2>t?#Q|HV7RWyz zcs)EF^;gDT?_Al8N;mk{jbD6mINPIi zyLHj@<*S0-mx~x%bx z&0e=IyT7LB(B+Af>2EJyJktDTW**P`YTJoNrzdPnqZVYV|Gn&|vvczPPQJ3z(dY2x z1-3Wd-_>&&=5?egFV5VA;WZ9d=Ivf~fBLfM+Mb2V8wU>k`DQxj$LNf@sBbf;4{S)? zUHrjOIzfdn6(5ZQcdapLW<@-2B!|_q>@t^+$Y{&$IKHj~6d!?&|Y#v6kQetl&uV zoYH{ETbV!j`cL?n?Y_U~<25O+Q9tgF+0D=|a4)cjI`ygV>X11{WT;1&`PwP-&9f!vGrm(^`SD+) zldshOxfuR!>XCD&lGc8Ky)iC3f=?Y#JvytLE|@O6^pn-#RW`cLnFnDJ&R^UdFD zGyg#gR$Kn`=8aX<>K`nfoK@5BVp?zA-+y+C(-z7{r8Bjji>;euS6)Ra&QA8Om-}W4 zuWq%tJmhWu&}!)Dxhlw3{+l-^0;Ar0IowNGamBIdDyja*z6YgFbF$xz9Hc26OLGoP zFKOGm=3nDa!^c)}&L8-wbis$Vm)AU*f0Z^e3k=S*{@=v?KlBZc1OH`hAoca6FHHZJ z$p6*#d_yr zI7+OQoqwG<dbPD0^Y!-lA=#F6$p9M4p5U8d8zfxgWTM@B9_c=eqy zHObTt|?6PwDW8Z_?)?AJelmr5=@Sih3ZW^*=ZE^Pgte%8`5(>H^) zImFe~rBzd=V$bz)u=B2S;(csoGyZXA`NfCxCh{n*uEav^FDGrS+#F)zaFWI@4%{=h zY$Up{2pLN@9DZw1jCj#G683+Sv!1isYR;UT*qAF?^){amZ&+?yaCr0kj01O~w+>4J zd`67QsaR!nVsRnmuTREp6JBL5*eiU@zDQ#2b()v)qaenwbj8VRNAKd|!jl7&4X>{6 z>e+XG|ao`@|Q^)_a)_Axk_h+Yoo*@RmL6Z`2>I8+SSqP-9V( zdgch@ffxT*+v3;=FYju$!_WSYpYqE7LA9SG#vY7L6CH6`@IeIMco}G{@mEA zV$J>dr>8Pa*3h!w9(w$EYct2yx$9LfF%>JX_xP-|s<>FiwmNp~Wrs{~toFLgt4ED3 zfAo#?EPUcq`0wWf#v?pOiBrgj8O;(&1Cclt71h02bSZsy*DHr3nk>zQEoojJQ6EVT z&1J;;bZIp207AF>MrMS`!WUki4;H(WxHbd|wE-VQY$_b~U4FfwS+k+#>dVN#pM_gB z->;+}T{{_dNwJbDD!Xr)Cn`()t!;_j-z72J$-hiRTs?O~K~{sNv0-bmvKx}({o1a0 zx6}P$v8(I+fPi^PD*hjnrN5;VHv1Ny{glmytHk_Sb9Qf%AU4t%nrmrgCy;h!X*7K% zAb$Jjw%dp2%yn*1{pWj&4Vp$-W1{AQ4`d!LX14x)s^uZu9yz}z`SJ5JR>JpQfE(T}T;s3h3S7S{e1H?$65!X0icqnK0>gzI-bAp6nmQT0RvU zW~$zb8x<)RQz~pN;QIG-7iLV4*lpUBdi}IY6EG2TCFoMoaQW~Y8)2%WrPa_}n~O27 zmZFbr-keFEW24zx7gqa&l*_%wafp@QQ_L+(no?3qli6%X{`6tpTx(I_05s}h=Yg}z z&5q0zj#KDvSL%J;#y=h&v(MP&w1InRq}#jZ5O*Xdf4{lp>h!KN9u);n6J}z%=;WU> zvGRe7NO>^UajxCU7vFEt#VS)Q^WcMx^GFv)fIwP_$5Z@DQ(F}mhxBgzFv3fCOq(f@ zTsSptGy2i)0*yvea735qyJ_c((yucTlO~lC{YYYxd@44vPkS=nZsl~DmnC%4utf6B z&~+Y>Avt({ps=CzqeV3q)ng_lES>50|}dUhsPDMYDioH@&?0yMu>|H&$6LzjXbuwNJF*=S``d zjcPBSVIQDL*W{sV1*KsR+}Vz^GKQy0+2{5WPfNzfBmj`QSxM}&m-@m z`IL>nUtX79yq+VG*o+*?y6{w2YZ&SpznCsw?ZKK|l|STDAvnbX>t z%EP=yFJbbWKMF=-nL+2F6}()tj+E~G^>h8>7hcPMTtEGB->z>5eCPgq`Bm?A_aY?~ zhu#IIhI8gpwj`ws(~7fO)3c7SRD#W-k3?G+CRH@<4(>_4d;8aVpPtPr*ER*7Z2xdF z{B2i5))Ok`}V@*k8~^NYw+qi@+^lw9b1SpukXC5`j|Po$aRvCPgkf#}*? zPs))wvQhra$DEXRA2~y^me0yG%~0ooKLTtIb{K~>$Gxm{B^&;R2Hwc)6g7Oa|6XiV zmv_ki@BWPHG}I5*t{q96PZa0nKa5pTtkwv_EDvck(jHUGN9D<v_M@+vfYfKX=^2 zJmg&89X{`m+=w%u-;0fW=YOe4B3Zo3c41OA=!3%-i$M~O6fbkNI6mF*&4O#IY!6Qd zjwC+qFdrzacF_5(S44YxBn=PE=PYk6adlb3`CGbd*0y3p@&KB+K-wTyUK2Cw{l1nOUW$hPY%M-{cGw%DroxAC{n3yvJxe3-Uec%$o@Y-BL> zf%m>>#RgaBkqn=Li=5w|znBS47AbwnwK!idtdM6bE#(&PX{FYIpS*LsVy$f zfBaeL*ULv5cG>?T{|i!WI$D0*opERR`@21tx166lr)JqeLdV({whdcczgdv<>yZ*> z^4zjJ^2xAeZHt}DTHbzXb>eJr*-X#-)C`FvIMt=V-h8Axguegc!o8nn-Db~~ls&id z0TSu+TrDi=d)^k)J)E2CdSwo2i$ly6+q@&L31)}SEO7wH_BYQnrv5WUUmiK~x6OYk z4n7puEl@AJ6w54ezHqqr??)QVB}321UjvdA+BYi4qkg?t2zaM;^^pd~pro97eBgaH7BWVkkYmR@l@ax@o<+XvAWj)*8 zHspjut0_~xKilQF_HKi!-dpaiFgEn03TG|W$9$SnxW6YhRUNh(z>$&iyszQa^r3=b zujO|q4S5#}0_HpsPdrTsuxXkgUGc5l<+!Q1f?4 zT^DcAsgCPf_0OVyuJoB75%7L-(LmzekDeX0eM*vO;?Ab7>wEM#@50HiJwJ;H_-k>% zfZEIZ{=?UqmtQXUDPUKEX0|q_bWYY!i5F#>hTrD(6)#!%X4=)P>oiB=7LZV6eD!w! zs=fE$o?4ai8^@i=-}lDSL+Z7D=ERya#n52UsRutgoq1{Gy8QH~y8;$iH55Eu*46M} zj#vF*+MlJJt@nptNyO9biifkme6%3t`WF06;w!HuFNUn_SA09qP9O)QusJ>_(4NdS zg9boGw^nSpTe-|`opI`WsNKRhTe{A@4Y--~^7N6FpErDe?J72%6+L^%Wq4wa zS-kIuX47jDX9@e3O@BYA$`7Z&G*wLzOtSD zKJ(wVPnUdb7<$C?+_&|9m4{RAyw&uh9tQ$eJmjYptN+?nJO1ID-91s~J{miJg8#y6 z^_g`hhE4n}&dfDNZ+yLQ`IL{=cfPG_N`vbz{*zz!t+lOgNO-U`^s5zj_jjF+dXu-{ zXHKExTw~UyC#gPmYlg)q{tVb$aj56=+n05{YGcy+? zQm9?~FzL7MB^)28dHVjXFN%+}O2+T5-%wJ!*}5R$&c9!J35_GCU)I#p9oHGMt{6i# z1GlgIG54_9-;cvz&FmO{HC=FL+i$Y*%Z3dt^4phMzi*fuFc#VPxwY}>{O#)_cg(kL zE{Vx9IUG2a9aQvppR5r|&npZABj2+f=DQXQJZC#ie0Alou5r&J^Zr%Pe3jhVJ$U$% zmk)EDVZ)OnhrQmMez^PG?79cbHJaX+3;5qZ{_^q59fd3*XZ2B^N;qe%?qbKm*CpD* zs=1|q^8#9UfrtwC)9sT8v9cBcfFc+IjQyD zhj)bxyGx?nh+pyeOv zdVBcN?d!jsoIGZH@%ootI~*HaU9(<|EqJrmco@wT(<_CF7a660W4>!Y$>RUA>%{{P zr57G`wjL(sP8Xa^P);wjj>$^+d+X5ewoVzd8Fz2|=U8Rpjq9ZhWvVc; zkFV(8aHxK0y6axoz_Z&u8S2*u?F}zxefZahj(^zck=PXqOjqS8Z{5(CRy6;jsfS&& z2D{S_oHjl@VK(@7q~X<_{Ug86*+qWAPxXkp@-XrCCr*btQ`gkZUHTy8gl5KgvFIsT z_3P6+0k#1tt5ViJRv0Ga#w-7zLdD~Z^$#6;4}O&VozKav(b2o#e@rde;q95!m3zJ7 zQc~;W59cW=UzjucH^dGXZ zUGnm~CB7NLkni@NtF2fxUw;fwhp|_&AZ}}l6hk7oy@u| zhTrFxCR{DKV@zrJ<4jHb-+jlUhZ4l|yp>ZelP!-1p8Kq4&b%*u9}u#5i}zOv^Vc5b ze5g7XUrN2Yd+XgcJC1#=$7fxCSRP*-bWyYQk=mH}=Ait>Oz)c=!cVT${?D=FUw3-< zem3y-%_3=Gx zZ`~UmH5@vfUf(=#MeX~9q19^_C-3-Re7$3$o$#+XuuQ}+IvF}>y z6M@3GXZQJ;nMc3XZMMDf#q_!byN>nl{%l@N-G{ROzGmp1WPICPYTo7ZN1mEJT3x#J zZ|u$ft7G?vzdY*b@%v0fkNR!xFQG#vC8hk-C97Ba$Y&p|K0$h0Tldwf#g)@<&wsr6 z&bp6Yh1?OU@6QMnnI*MXJR*NDsRdh3JUjoiec$Md-}b&vc^7p3)t!GE)6&wC?mu33 zZ)WD{xe~YBtC?*J-uTo$ovyn|`hCIH*?(;xUu-(;4~?%D?loqv{J3dr3^B~{hzcFD zHy^!Nun$81{i)mRL|SUvLG$)sRvmc!CbG7+?mrGMAKrf4w?NVH+GFL~)|lU7Dz+xL z_fMqlV=XNC2W7ub`s4MPjMtw({8@b`ZSS+Lisu#mY zN?(;Y*48-su-EVp1Yi{-p9%MQ?|yjx?s@yI6%_%8M&DLcl&~i53R50M5C5gOHEHNB zU0n9;AC$BA_~MV|eidKx;8o4$brk`N7azJmbMJQgohqUHwrpl!*XZa!i1@U&_QaP@ z`d63Dy_dKU}`&IjbR%69J!xwgIySg4Hy*-RQ-8b5G<@YNGPLDUv%)H-4EvehQ zDsA6UM`76U_Ox8L>jM`KJGZ8OAxQWArtxRNfuD{*$vRYevyma7g#=DQkdXj$G+rM_sOr*t<1yfz=Pdy{{ zFWfl#`27cQcb`&zkLY_oV|<*{Uq5_vtahw&{dC>a#ajn{U$AAQfAwnNzKM`eJkK4N zdH(B>`@SjfnoGhqmQN;0pClP_gY7@Gsi^n|xdANpd+%FSx3=~(&n;=!hrY|KTbc6i z4SVdm0L8j}p&O?RzW9%(-F=k@)mEghKt2bn*K zJbvWf&S~Cgn)?3lCHCIYKXyfRg`TW^x_B|YE}`+SpZ?=Ic2II8 z7g$>_T;3hc?Y&ej+yCN3)-J#Cc<%3$kQHo9B``Z~DOu%dj&C&#&5W z+}he^-x(+8$tQj}M-CeGFFVp{ZS|Ye%PcLMWRD|HA^?S_F5OS~pNki+4G)cQcenCs zyY@#Oqhb1rqq%po{4*AX7JaCCcP};fk-O%rVz0Jmi$A_G((-tblWVII>c_XMj!)Zj zFCpvqnS$^wd%RK)y1$_-XV~IRmN~g6=33im{hgJ2*=$#9uhk{z3vI<)5829VpVj(% z-oj%pD@PxGJ$PvP$5|2kgMTaTwik>gTvuv7?~}VMHsieJ!U_4MfI6MSIXPN6e;+z% z=DyB;x7n<<++BYSYMYfBKKbK*NNfARr>A?n#{OKrsdTH^te+poI5X^*J=T_1NBOp?ze!er+5K*lYoufDSoGgaAG6(7XV)3uxEH3rVb#7T zU4_@2R(#CW@&BG0ZM?Ij>w~+_hq@Q@Y}}_kdSi4rJVF1+=HGT!6g6mZo^uV}Zm z)v2XN_FrC?6QLEn>5nrRE{FUeNoijBJ*Mf+&+r*W;}=Kg$t&6{v^MjonZG>4NcR&{ z{tIAKM*OlRf8GPp-|~E0m-5cW*wzTh@;f&5?dUZ4`_%O$JVYE6lo|T%&eapycJkZd z*2!u!_uT8dEmAIS{Sdz8%6i2|9!p)C4_XcMdYo?v=m-xFn_;urB4fe&OBn~h9!&ml zZMf}7z`)y_oD8`GuSnR-t^c$>`TTkJOAqt!t1>bYu9{rke6#OwkNht=Rm+w(%@1phzyIj5 z@!}lK^riuylgF*9Ow|<^o7!&R?=Ai7pGU%zH;-|&{Blkx6fd=|JLs*oA#Oa?>{Mz# zEomRQr|dDqQgA1A?jK5jlpbHCaZE0xdh*4jMJWi_Q?=JM+VLn~xtEsH!-+ zZ0fttyno|vSPP!4b-0m}YYpUG+aLDu>n~Iiv~}Uu`{d}>&$t)WBlHxrPtpVhkZx`2VMZrKE1K(7dkY% z=jGSpmDd7~TKw_xguVEmflnRD9&d~s;+F+!SzK&tdTZhX0`k9ea9)+q>nQQ7)h=&b zjvh!0@j2Z$@U$*t)liz;mqT?|TwETe+M;#A zRn<13yUjp+Jp%*n&o2dC)-tv7_x`WpxYM6Yn<@qddM;3waZQp475g^^H`yGa-i>R$PIJbWQ!p0BTZ*spZNCV-Ni0Blk>M=QOuiu5jt6v zpp}!MSK9j;e5u+jQ}Qw5k;e*!pdq6n;x&-GJdsf2I$RZSzg6>{pH@I3$oA6d`@#_ zPHy&|HBBwUd4)?;@?7$^hqql)OtoCNU>2{h>q_C~iMxO)O?RKiKevJ~<=h*P^#88u z8(~f`@)&3gzH@a??t{(qf{f-oDm9YdwxjCs$$?DUn{RVYoY0x;osn^HOp%+bvvo&? zqV^kjG;q#=AS3yhpS?$>WZ7~zu)K8pA-|vRx=x*Wc)fr3>^$|ncV}MZdUz}hyV>`% z{k30NU9q&hf3a;@`zE6wTx!}LI5g8 zOCNvU?Ok)GSo^W}f$13~cL5G|T>)39Iy=kiN%*eu%@chW4m8^*HMQ=ktqZgY==`$m zpYY$O&8phVES{-hWtK4K%-!cTQC5G2h!l)2Cx2w zsvRzW1`u543UV{@&WLRa>3R2MOUTs?uhy9ceYiX3eeiBWV5z42tuRyH!lWrhHPt#R zGt;|FkDi-(%*ZI{=HHnaPU`o!S;ShdpF43nyM1$U@wQd5wfoK9g`8?WwA#4AwOc*y zkEwX{t4-2a^!_X}Ay&pGAcu{5K);%&q9Kk&PKzfel(v;G-=7rIjI zUyi(z=j@zwq#aOXwziq^#jcRS@UXjWZ&)d7ieEjOv+J|%<$=D5Z;H9XnmXx+LyN7H zJ>26znSanSJANiMBV%RCnzn0?%nn&v3`Q(FSp_}_)-{(uACv_cUHdM?4^Ce>h1GqQqhWfc6;6>2^_85j;XxePXzj3hOW8JvJM$LZv=jOO2&aoz$X+ zFhvrBchw9-W-^p=SQa6YLToyboVo|3rjU*lJP3sI)F=T>S=A(=^cVysWEA4V-Y6*s zf91#0h@uHnF&L^rafB$GfwKu@ zD5If9f}mj=mk1a}UNR11lgv6OFG12oM6h8PK`j#}rCKr^6}YfT#6!+j$PG9}t%<}G zs~nb7jFJnKmn;#$uw*=rQNT1*^;o9EW-Sq}u4v`RBG27peU0x zcmT3OP8_}(4txDUkK97oGoggk5;-&?7$QLr3#VgdB*+lM6;7dpwrB(_ClP*fVx)Wt zjc9}m6i~Peju@mKZj0f3h9gfej)WHA4hRg9P0%dV!r=AdfK$(=ONLb!%M=%6uLn#G2``Dl(XkH84z&>vp+zMEL147v7F7CxP1ph)5 z+Jr-C8tQRmUl6hVadJ~5*8r%(RWV9H$?1taVZ)vRmWUWyP#eij7jmKg8WTb1hE8(C z1>De|<}<;ynVg<`L7f%`w1Gp&Qtt;Ux;mBaY(O zMR7RIoMK9qVHLw>V{tfxvO<)x46zL|H4xfKaM%SjuRvr8hNxD{K^Tu_@F@jEi+Gre zp{EiLqYaNN7f^^tVo3b}x)^Kf$pjB2hU0k@6BRK8o*a^q%%2Z5V|r>ds=;Ao3ekuQ zluT8XauH>UC@#fJ;^fL)jZ z_=XGEq$Fek2nK_C%g(8ynon|_avz~&VZBX)|%th-_`S|qnaUwtXf z7J{;9CB#&vBtQ=X6=4LH$Ys(zR8No~90n4{;Gr}LjDRR;HclgsJ`-#JFJvLXG}vtl zo8@6ouA!ioXDG+O;iMT2Js8EY9x9;~9G!tLHbwzj31u-v4H66sN#saE1Ed6sU^5f| z(~#hnk0L^dOhapkO$;soO^qWTxe>CdIf{)aLdaG{ zG%5g_5KW;i&7&9~UYG(kxeX$HH1Y3Rh(LL5>LyvZ3! zz*3<&O%+KTkq_`D2Q|o?!!e-*PSQ%?Ux=oKV8=M{LI?_Kxin6az^9a5D36ek^n`)p zEFIZ!2#A0LsAI#fGXet+6>wl5hJ!uO`0^>ifT^hQ*^YWNMFAmlR)a7u#f2k(fB>l~ z^s^%!34u8U(P4nibRL0tJyfCzgRDfF z)i4ZPq$xPPjOD3{v5kC4lpY9TRn0_9q(HK!#HEh?Z(_oEBw{leh;%`oa>x_p9|I3l z7!aExsF#CK0iuayl!!V}fsifa7AI!IBrt_G>=s}U_I88H5gb1R?N%1Vk&LBOy62MB zOql0*q?09$k%iC|^z6u2gSjakQ3yt1EMy>k3OKT&>yWU3tIp?Oq#}mF!BQXy6i=a+ zCCJlr3CbZ)04ybQzflvPE1jz>6N@OBj5_wxDNf zF=$dABs*p2h=oW;fJ7IHrU?BwC}v=<=HSjtmSCzdf^ZSSO_mT0)=Y|wGd);P$K6Ow&C@6S7#k5kPD&zB zA=DnBUG$WyL0RTV)fg)5GuIc%l^_-}0n zBGp$Cic4Y;Y3L;s9S*^gsCWP{IPwlC*1#pWCzB(nCGY?Q!+8o~4AI$BFmEAaA_k~q zBgJA2sK*KbeQ-d7WlC0*@&QbZkEL7G6Q0-+%9Ab30+~!T661=Lyo{mFL?_`47e^#m zf-+k|nTiTvcWM$XpcL53rVT+QFnSLwJyAkKnRhUWV*D{T2`b>j^bw}tK{CW-CSs6v zj8K{_VN*0>J5tD*DUl#lPf27?go_@aRa02UGxWyLDK55|17oG*f^;ULTu}j-Ha|VP zkn}`qRsnH{hANchhAaSS@qug*`5MX-$Cj%aPNxcRV|`o?=L+vpFg5THdyOXt%C&GH z%abM&DXtox$S_PasYMut_aUMR#dR$a)Gea{KW5Q}m_E%wh*Z?TF;!J52eeke_A9{1 zI3=+sP8~)>eLcdGm*~r_3^+i-%s-t?ImVzE91H<)Q6Dgbzzo?#phKLoJk^04oV>K3tpCRW>|cua7RtBr<@R%1F`7o z!?_*OP&jQQ&Xc$VFhPMFp=%0|fLzHjL>frs0&%{SJ)cb>;t-Bbk!&~5$~kxW}pbMco?olV}K_tfD}E*q$m`}Knx`| zrHEtLl;N~N>Fk-^JJtboo|RD(XP%2IeP{6>RBI)&7uri?xFE_48#!oi*d ziZ%WNj!4Fq3hFq;xK*V*$I}JrgEP@kUyB!Mkpzuf5dI`ug5fF2G$K-BtPE1DDq?s& z0#!wzmP9xKkpv^hGFD8%kzOu`g}5e(u>?I*B*Cp1=?dvdT$o~zBku?jAV5{b)uWLV zF${T-9#Tw4PiYdME(nBXP;xMJ9xi~u$s`oU0%(MoIR~M1219{jdLhP4gqA>p1MwBw z2#+W7^&Pb!a%RXQ*-|Ye6ii7;QQXfIx-ficFf>q@3_&;s%Js#RA(kd-GsK<(Y7i`w zqHrOx-E%gGiJZzoOraTAMh}>)kc0^UrV=pP7>Z3Vg;W|QdytwQ3j$DLHWGy)T$ybM zf3IW`BuU{QX_7&zJrFn{b^&ILAx|n@cv;BSKd^*Q0*};#U>s3~5I~N&fa`HkjbWi2 z1ii?S1v5Bk#1;2KSY{X(8Iv>y83i0&NN^~jBZ3@o3Q)z?2$-eB&NO0iDP{{HQ&<{< zk&F}pQo&IyMdFaG5p@vgDuS``Nd+_&tI%v0RDck1PZjKB52=8$5)tSO78PpTaKxg3 zBPIeQ)c|u~uq}W@e~9;xA%V0AxDOE#0&$Vp5<(xb(^=rVQ-KLQhB0s!Bru>+IPn02 zXpZ6CRfR1?V~7y*wL(IjMi%m;!(HvDQy z#IZ?5!jr)sX<}nC7}=6`;+*0E+Z2*hm8c=65RnJjxM%>z#uQlsMj%3b1Q8LzPT)Di zydwcx>W~CY1yq|TL^Y$sLKu|miE}aB6oVlZ5TLh3{FFGD$Q`FW@6r4zKV;}55xWe8 zO!Hug11)7tEpxyq=^PT`J~le+zWFeP8ze#|E&$2|05;&-OAxjMx+14!A;KYNEu2b2 zaCFGC1`{%nXezdY@UnoA79fflfiMV-Q6}z=vz;*}38==5;Q(_bLXu#EWk4GeB_pZ^ zMqnd%E+sUa>X~6{fD8)|WHeexLqZEfVdAPG_!uy6%0W_GA2AR^K!T@Cqi{co{1R{w z5|Uyy1W~9~8yvJt@e06+-0+PEwH#v0m^eI}9!tbXEz@P_0vl2)0m-5;XUH6uN-y9`%%tk#3pC5xLHw4@fF~B!JhJjO4LAAh95mba)aS|quT?nsn`rXlSifbRo z030LLmt7=#4S7jgd=Meu84zySd(IC{(RA`zkPhSU`}1XECh zv;=px1c@kJO3q)!MFfvvu`y*7i-en4#;Y9h>FFAz6Rsvg1xjdG2FL_ZhU;jVZO8x-0j9_cYJgyj2ZWFi#45nz z4>UySEPPWU*OB7G=&>>`LkdI?g^GN#eo1CdpcL`&CCG&kx+wdT6o4Trp{AaK+yVmH zSPW0Uhf7tokUXSmV4c9bVJH)91Ui}W6u@evK*E%dmKcUYs*8A(BCz&FNkj+Sh#E%LCk`#R?HGGa17h~`ROjA;( zg)t3Lnk7h*2Ecs44NWBJ7&svY_z_4k60uDSri9P+VwqyGs0hw~i-D$=vPRx?Z{GYk z5h>J*MehHp^UyS09^=NzX)!=wN4O+rNh2$GHl|KX5HDy&ToPo9BaDK9;9?6Laj*Mm z3KAU℞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 literal 0 HcmV?d00001 diff --git a/docker/deployment/images/logo.svg b/docker/deployment/images/logo.svg new file mode 100644 index 0000000..bd3534c --- /dev/null +++ b/docker/deployment/images/logo.svg @@ -0,0 +1,79 @@ + + + + + + + + + + image/svg+xml + + + + + + + + { your logo } + + diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh new file mode 100755 index 0000000..4bb869a --- /dev/null +++ b/docker/docker-entrypoint.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +echo "Container starting up..." +set -e + +cd /opt/multiparty-meeting/server +ls -la +echo ".............." +ls -la /opt +echo ".............." +ls -la /opt/multiparty-meeting +node /opt/multiparty-meeting/server/server.js + +exec "$@" diff --git a/docker/images/background.jpg b/docker/images/background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..71365515d00179cd545c874425ac15c2063318f4 GIT binary patch literal 50043 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℞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 literal 0 HcmV?d00001 diff --git a/docker/images/logo.svg b/docker/images/logo.svg new file mode 100644 index 0000000..bd3534c --- /dev/null +++ b/docker/images/logo.svg @@ -0,0 +1,79 @@ + + + + + + + + + + image/svg+xml + + + + + + + + { your logo } + +