1925: Optimize Rainloop: Change to NGINX r=mergify[bot] a=Erriez

## What type of PR?

- Reduce build time.
- Reduce image size.
- Faster user response using CGI.

## What does this PR do?

### Related issue(s)
- Mention an issue like: #1830, #1200 and #1924
- Auto close an issue like: closes #1924

## Prerequistes

Documentation updates TBD (requires some guidance):
- [ ] In case of feature or enhancement: documentation updated accordingly
- [ ] Unless it's docs or a minor change: add [changelog](https://mailu.io/master/contributors/workflow.html#changelog) entry file.

## Technical details

- Image from `php:7.4-apache` to `nginx:1.21-alpine` followed by PHP7 installation.
- Move `.ini` files to directory `defaults/`.
- Move files `sso.php and include.php` to directory `login/`.
- NGINX configuration:
  - `access_log off;` as this is handled by front.
  - `error_log /dev/stderr err; configured to reduce output. The logging in `start.py` is useless.
  - PHP configuration optimized for CGI usage and requires a config file `config/php-rainloop.conf` -> `/etc/php7/php-fpm.d/rainloop.conf`.
  - `.ini` files are parsed / substituted by `socrate` Python module.

Further optimization is possible by completely removing Python. This is only used to parse the `.ini` files and can be done via Bash scripts. This saves more build time and image size can be reduced to 112MB.

## Reviewing

This PR requires multiple reviewers and extensive testing before merging into master. Data/settings are compatible with previous images.

Co-authored-by: Erriez <Erriez@users.noreply.github.com>
master
bors[bot] 3 years ago committed by GitHub
commit 53fe567d06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,51 +1,71 @@
ARG ARCH="" ARG ARCH=""
ARG QEMU=other
# NOTE: only add file if building for arm # NOTE: only add file if building for arm
FROM ${ARCH}php:7.4-apache as build_arm FROM ${ARCH}alpine:3.14
ONBUILD COPY --from=balenalib/rpi-alpine:3.14 /usr/bin/qemu-arm-static /usr/bin/qemu-arm-static ONBUILD COPY --from=balenalib/rpi-alpine:3.14 /usr/bin/qemu-arm-static /usr/bin/qemu-arm-static
FROM ${ARCH}php:7.4-apache as build_other # Shared later between dovecot postfix nginx rspamd rainloop and roundloop
RUN apk add --no-cache \
python3 py3-pip \
&& pip3 install socrate==0.2.0
FROM build_${QEMU} # https://www.rainloop.net/docs/system-requirements/
#Shared layer between rainloop and roundcube # Rainloop:
RUN apt-get update && apt-get install -y \ # cURL Builtin
python3 curl python3-pip git python3-multidict \ # iconv php7-iconv
&& rm -rf /var/lib/apt/lists \ # json php7-json
&& echo "ServerSignature Off" >> /etc/apache2/apache2.conf # libxml php7-xml
# dom php7-dom
# openssl php7-openssl
# DateTime Builtin
# PCRE Builtin
# SPL Builtin
# Recommended:
# php7-fpm FastCGI Process Manager
# Optional PHP extension (for contacts):
# php7-pdo Accessing databases in PHP
# php7-pdo_sqlite Access to SQLite 3 databases
RUN apk add --no-cache \
nginx \
php7 php7-fpm php7-curl php7-iconv php7-json php7-xml php7-dom php7-openssl php7-pdo php7-pdo_sqlite \
&& rm /etc/nginx/http.d/default.conf \
&& rm /etc/php7/php-fpm.d/www.conf \
&& mkdir -p /run/nginx \
&& mkdir -p /var/www/rainloop
# Shared layer between nginx, dovecot, postfix, postgresql, rspamd, unbound, rainloop, roundcube # nginx / PHP config files
RUN pip3 install socrate COPY config/nginx-rainloop.conf /etc/nginx/http.d/rainloop.conf
COPY config/php-rainloop.conf /etc/php7/php-fpm.d/rainloop.conf
# Rainloop login
COPY login/include.php /var/www/rainloop/include.php
COPY login/sso.php /var/www/rainloop/sso.php
# Parsed en moved at startup
COPY defaults/php.ini /defaults/php.ini
COPY defaults/application.ini /defaults/application.ini
COPY defaults/default.ini /defaults/default.ini
# Install Rainloop from source
ENV RAINLOOP_URL https://github.com/RainLoop/rainloop-webmail/releases/download/v1.16.0/rainloop-community-1.16.0.zip ENV RAINLOOP_URL https://github.com/RainLoop/rainloop-webmail/releases/download/v1.16.0/rainloop-community-1.16.0.zip
RUN apt-get update && apt-get install -y \ RUN apk add --no-cache \
unzip python3-jinja2 \ curl unzip \
&& rm -rf /var/www/html/ \ && cd /var/www/rainloop \
&& mkdir /var/www/html \
&& cd /var/www/html \
&& curl -L -O ${RAINLOOP_URL} \ && curl -L -O ${RAINLOOP_URL} \
&& unzip -q *.zip \ && unzip -q *.zip \
&& rm -f *.zip \ && rm -f *.zip \
&& rm -rf data/ \ && rm -rf data/ \
&& find . -type d -exec chmod 755 {} \; \ && find . -type d -exec chmod 755 {} \; \
&& find . -type f -exec chmod 644 {} \; \ && find . -type f -exec chmod 644 {} \; \
&& chown -R www-data: * \ && chown -R nginx:nginx /var/www/rainloop \
&& apt-get purge -y unzip \ && apk del unzip
&& rm -rf /var/lib/apt/lists
COPY include.php /var/www/html/include.php
COPY sso.php /var/www/html/sso.php
COPY php.ini /php.ini
COPY application.ini /application.ini
COPY default.ini /default.ini
COPY start.py /start.py COPY start.py /start.py
EXPOSE 80/tcp EXPOSE 80/tcp
VOLUME ["/data"] VOLUME ["/data"]
CMD /start.py CMD php-fpm7 && /start.py
HEALTHCHECK CMD curl -f -L http://localhost/ || exit 1 HEALTHCHECK CMD curl -f -L http://localhost/ || exit 1

@ -0,0 +1,38 @@
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/rainloop;
# /dev/stdout (Default), <path>, off
access_log off;
# /dev/stderr (Default), <path>, debug, info, notice, warn, error, crit, alert, emerg
error_log /dev/stderr warn;
index index.php;
location / {
try_files $uri /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_intercept_errors on;
fastcgi_index index.php;
fastcgi_keep_conn on;
include /etc/nginx/fastcgi_params;
fastcgi_pass unix:/var/run/php7-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.ht {
deny all;
}
location ^~ /data {
deny all;
}
}

@ -0,0 +1,101 @@
; Start a new pool named 'rainloop'.
; the variable $pool can be used in any directive and will be replaced by the
; pool name ('rainloop' here)
[rainloop]
; Redirect worker stdout and stderr into main error log. If not set, stdout and
; stderr will be redirected to /dev/null according to FastCGI specs.
; Default value: no.
catch_workers_output = 1
; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
; will be used.
user = nginx
group = nginx
; The address on which to accept FastCGI requests.
; Valid syntaxes are:
; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on
; a specific port;
; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
; a specific port;
; 'port' - to listen on a TCP socket to all addresses
; (IPv6 and IPv4-mapped) on a specific port;
; '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = /var/run/php7-fpm.sock
; Set permissions for unix socket, if one is used. In Linux, read/write
; permissions must be set in order to allow connections from a web server. Many
; BSD-derived systems allow connections regardless of permissions.
; Default Values: user and group are set as the running user
; mode is set to 0660
listen.owner = nginx
listen.group = nginx
listen.mode = 0660
; Choose how the process manager will control the number of child processes.
; Possible Values:
; static - a fixed number (pm.max_children) of child processes;
; dynamic - the number of child processes are set dynamically based on the
; following directives. With this process management, there will be
; always at least 1 children.
; pm.max_children - the maximum number of children that can
; be alive at the same time.
; pm.start_servers - the number of children created on startup.
; pm.min_spare_servers - the minimum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is less than this
; number then some children will be created.
; pm.max_spare_servers - the maximum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is greater than this
; number then some children will be killed.
; ondemand - no children are created at startup. Children will be forked when
; new requests will connect. The following parameter are used:
; pm.max_children - the maximum number of children that
; can be alive at the same time.
; pm.process_idle_timeout - The number of seconds after which
; an idle process will be killed.
; Note: This value is mandatory.
pm = ondemand
; The number of child processes to be created when pm is set to 'static' and the
; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
; This value sets the limit on the number of simultaneous requests that will be
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
; CGI. The below defaults are based on a server without much resources. Don't
; forget to tweak pm.* to fit your needs.
; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
; Note: This value is mandatory.
pm.max_children = 5
; The number of child processes created on startup.
; Note: Used only when pm is set to 'dynamic'
; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
; pm.start_servers = 2
; The desired minimum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
; pm.min_spare_servers = 1
; The desired maximum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
; pm.max_spare_servers = 3
; This sets the maximum time in seconds a script is allowed to run before it is
; terminated by the parser. This helps prevent poorly written scripts from tying up
; the server. The default setting is 30s.
; Note: Used only when pm is set to 'ondemand'
pm.process_idle_timeout = 10s
; The number of requests each child process should execute before respawning.
; This can be useful to work around memory leaks in 3rd party libraries. For endless
; request processing specify '0'.
; Equivalent to PHP_FCGI_MAX_REQUESTS. Default value: 0.
; Noted: Used only when pm is set to 'ondemand'
pm.max_requests = 200

@ -18,7 +18,7 @@ if (file_exists(APP_INDEX_ROOT_PATH.'rainloop/v/'.APP_VERSION.'/include.php')) {
} }
// Retrieve email and password // Retrieve email and password
if (in_array('HTTP_X_REMOTE_USER', $_SERVER) && in_array('HTTP_X_REMOTE_USER_TOKEN', $_SERVER)) { if (isset($_SERVER['HTTP_X_REMOTE_USER']) && isset($_SERVER['HTTP_X_REMOTE_USER_TOKEN'])) {
$email = $_SERVER['HTTP_X_REMOTE_USER']; $email = $_SERVER['HTTP_X_REMOTE_USER'];
$password = $_SERVER['HTTP_X_REMOTE_USER_TOKEN']; $password = $_SERVER['HTTP_X_REMOTE_USER_TOKEN'];
$ssoHash = \RainLoop\Api::GetUserSsoHash($email, $password); $ssoHash = \RainLoop\Api::GetUserSsoHash($email, $password);

@ -19,12 +19,11 @@ shutil.rmtree(base + "domains/", ignore_errors=True)
os.makedirs(base + "domains", exist_ok=True) os.makedirs(base + "domains", exist_ok=True)
os.makedirs(base + "configs", exist_ok=True) os.makedirs(base + "configs", exist_ok=True)
conf.jinja("/default.ini", os.environ, "/data/_data_/_default_/domains/default.ini") conf.jinja("/defaults/default.ini", os.environ, "/data/_data_/_default_/domains/default.ini")
conf.jinja("/application.ini", os.environ, "/data/_data_/_default_/configs/application.ini") conf.jinja("/defaults/application.ini", os.environ, "/data/_data_/_default_/configs/application.ini")
conf.jinja("/php.ini", os.environ, "/usr/local/etc/php/conf.d/rainloop.ini") conf.jinja("/defaults/php.ini", os.environ, "/etc/php7/php.ini")
os.system("chown -R www-data:www-data /data") os.system("chown -R nginx:nginx /data")
os.system("chmod -R a+rX /var/www/html/") os.system("chmod -R a+rX /var/www/rainloop/")
os.execv("/usr/local/bin/apache2-foreground", ["apache2-foreground"])
os.execv("/usr/sbin/nginx", ["nginx", "-g", "daemon off;"])

Loading…
Cancel
Save