Update container build to use Alpine Linux and Gunicorn instead of CentOS and Apache. Fixes #3246

This results in a much more slim-line container, requiring fewer resources to run.
In addition, the majority of the build is now done using the Docker infrastructure, allowing for quicker rebuilds and better use of layers.
This commit is contained in:
Максим Кольцов 2018-04-04 16:18:17 +01:00 committed by Dave Page
parent 1617d003cd
commit 05e2e3cb39
10 changed files with 120 additions and 192 deletions

View File

@ -33,6 +33,7 @@ Features
| `Feature #3182 <https://redmine.postgresql.org/issues/3182>`_ - Update Jasmine to v3 | `Feature #3182 <https://redmine.postgresql.org/issues/3182>`_ - Update Jasmine to v3
| `Feature #3184 <https://redmine.postgresql.org/issues/3184>`_ - Add a French translation | `Feature #3184 <https://redmine.postgresql.org/issues/3184>`_ - Add a French translation
| `Feature #3195 <https://redmine.postgresql.org/issues/3195>`_ - Pass the service name to external processes | `Feature #3195 <https://redmine.postgresql.org/issues/3195>`_ - Pass the service name to external processes
| `Feature #3246 <https://redmine.postgresql.org/issues/3246>`_ - Update container build to use Alpine Linux and Gunicorn instead of CentOS/Apache
| `In addition, various changes were made for PEP8 compliance` | `In addition, various changes were made for PEP8 compliance`

2
pkg/docker/.dockerignore Normal file
View File

@ -0,0 +1,2 @@
pgadmin4/web/**/tests/
pgadmin4/web/regression/

View File

@ -7,58 +7,63 @@
# #
######################################################################### #########################################################################
# Get the basics out of the way # First of all, build frontend with NodeJS in a separate builder container
FROM centos:latest # Node-6 with ABI v48 is supported by all needed C++ packages
FROM node:6 AS node-builder
LABEL name="pgAdmin 4" \ COPY ./pgadmin4/web/ /pgadmin4/web/
vendor="The pgAdmin Development Team" \ WORKDIR /pgadmin4/web
license="PostgreSQL"
# We only need the web/ directory, and a few other things RUN yarn install --cache-folder ./ycache --verbose && \
COPY web /var/www/pgadmin yarn run bundle && \
COPY requirements.txt /var/www/pgadmin rm -rf ./ycache ./pgadmin/static/js/generated/.cache
# Install everything we need. Use easy_install to get pip, to avoid setting up EPEL # Build Sphinx documentation in separate container
RUN yum install -y python-setuptools python-devel httpd mod_wsgi mod_ssl gcc FROM python:3.6-alpine3.7 as docs-builder
RUN easy_install pip
RUN pip install j2cli
# Now install the Python runtime dependencies # Install only dependencies absolutely required for documentation building
RUN pip install -r /var/www/pgadmin/requirements.txt RUN apk add --no-cache make
RUN pip install --no-cache-dir \
sphinx flask_security flask_paranoid python-dateutil flask_sqlalchemy \
flask_gravatar simplejson
# Create required directories for config COPY ./pgadmin4/ /pgadmin4
RUN LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 make -C /pgadmin4/docs/en_US -f Makefile.sphinx html
# Create required directories for running # Then install backend, copy static files and set up entrypoint
RUN mkdir -p /var/log/pgadmin # Need alpine3.7 to get pg_dump and friends in postgresql-client package
RUN chown -R apache /var/log/pgadmin FROM python:3.6-alpine3.7
RUN mkdir -p /var/lib/pgadmin
RUN chown -R apache /var/lib/pgadmin
RUN mkdir -p /certs
RUN chown -R apache /certs
RUN chmod 700 /certs
# Push logs to the container's output streams RUN pip --no-cache-dir install gunicorn
RUN ln -sf /proc/self/fd/1 /var/log/httpd/access_log && \ RUN apk add --no-cache postgresql-client postgresql-libs
ln -sf /proc/self/fd/1 /var/log/httpd/ssl_access_log && \
ln -sf /proc/self/fd/2 /var/log/httpd/error_log && \
ln -sf /proc/self/fd/2 /var/log/httpd/ssl_error_log
# Apache config time WORKDIR /pgadmin4
RUN mkdir -p /templates ENV PYTHONPATH=/pgadmin4
COPY pgadmin4.conf.j2 /templates/
COPY entry.sh /
# Finally, remove packages we only needed for building # Install build-dependencies, build & install C extensions and purge deps in one RUN step
RUN yum -y remove gcc cpp glibc-devel glibc-headers kernel-headers libgomp libmpc mpfr # so that deps do not increase the size of resulting image by remaining in layers
COPY ./pgadmin4/requirements.txt /pgadmin4
RUN set -ex && \
apk add --no-cache --virtual build-deps build-base postgresql-dev && \
pip install --no-cache-dir -r requirements.txt && \
apk del --no-cache build-deps
# Default config options COPY --from=node-builder /pgadmin4/web/pgadmin/static/js/generated/ /pgadmin4/pgadmin/static/js/generated/
ENV PGADMIN_DEFAULT_EMAIL container@pgadmin.org COPY --from=docs-builder /pgadmin4/docs/en_US/_build/html/ /pgadmin4/docs/
ENV PGADMIN_DEFAULT_PASSWORD Conta1ner
ENV PGADMIN_ENABLE_TLS False
ENV PGADMIN_SERVER_NAME pgadmin4
COPY ./pgadmin4/web /pgadmin4
COPY ./run_pgadmin.py /pgadmin4
COPY ./config_distro.py /pgadmin4
RUN pip install --no-cache-dir -r requirements.txt
# Precompile and optimize python code to save time and space on startup
RUN python -O -m compileall /pgadmin4
COPY ./entrypoint.sh /entrypoint.sh
VOLUME /var/lib/pgadmin
EXPOSE 80 443 EXPOSE 80 443
# Start the service ENTRYPOINT ["/entrypoint.sh"]
ENTRYPOINT ["/bin/bash", "/entry.sh"]

View File

@ -4,14 +4,16 @@ Building
======== ========
Whilst you can just use the Dockerfile directly, it requires that various pre-configuration steps are performed, for Whilst you can just use the Dockerfile directly, it requires that various pre-configuration steps are performed, for
example, the pgAdmin web code must be copied to ./web and yarn install/yarn run bundle must be executed. example, the pgAdmin web code must be copied to `./web`, Sphinx documentation source must be copied to `./docs`
requirements.txt is also expected to be in this directory, and the pre-built docs must be in web/docs. and `requirements.txt` is also expected to be in this directory.
The recommended (and easy) way to build the container is to do: The recommended (and easy) way to build the container is to do:
```console
cd $PGADMIN_SRC/ cd $PGADMIN_SRC/
workon pgadmin-venv workon pgadmin-venv
make docker make docker
```
This will call the build script $PGADMIN_SRC/pkg/docker/build.sh which will prepare a staging directory containing all This will call the build script $PGADMIN_SRC/pkg/docker/build.sh which will prepare a staging directory containing all
the required files, then build the container and push it to your repo. the required files, then build the container and push it to your repo.
@ -21,57 +23,51 @@ Running
The container will accept the following variables at startup: The container will accept the following variables at startup:
PGADMIN_DEFAULT_EMAIL PGADMIN_SETUP_EMAIL
--------------------- -------------------
Default: container@pgadmin.org)
This is the email address used when setting up the initial administrator account to login to pgAdmin. This is the email address used when setting up the initial administrator account to login to pgAdmin.
PGADMIN_DEFAULT_PASSWORD PGADMIN_SETUP_PASSWORD
------------------------ ----------------------
Default: Conta1ner
This is the password used when setting up the initial administrator account to login to pgAdmin. This is the password used when setting up the initial administrator account to login to pgAdmin.
PGADMIN_ENABLE_TLS PGADMIN_ENABLE_TLS
------------------ ------------------
Default: False Default: unset
If set to the default, False, the container will listen on port 80 for connections in plain text. If set to True, the If not set, the container will listen on port 8080 for connections in insecure HTTP protocol.
container will listen on port 443 for TLS connections. If set to any value, the container will listen on port 8443 for TLS connections.
When TLS is enabled, a certificate and key must be provided. Typically these should be stored on the host file system When TLS is enabled, a certificate and key must be provided.
and mounted from the container. The expected paths are /certs/server.crt and /certs/server.key Typically these should be stored on the host file system and mounted from the container.
The expected paths are `/certs/server.crt` and `/certs/server.key`.
PGADMIN_SERVER_NAME You need to explicitly map these ports with `-p` option to some port at your machine.
-------------------
Default: pgadmin4
This variable allows you to specify the value used for the Apache HTTPD ServerName directive. This is commonly used to
ensure the CN of the TLS certificate matches what the server expects.
Examples Examples
======== ========
Run a simple container over port 80: Run a simple container over port 80:
docker run -p 80:80 \ ```console
-e "PGADMIN_DEFAULT_EMAIL=user@domain.com" \ docker run -p 80:8080 \
-e "PGADMIN_DEFAULT_PASSWORD=SuperSecret" \ -e "PGADMIN_SETUP_EMAIL=user@domain.com" \
-e "PGADMIN_SETUP_PASSWORD=SuperSecret" \
-d pgadmin4 -d pgadmin4
```
Run a TLS secured container using a shared config/storage directory in /private/var/lib/pgadmin on the host: Run a TLS secured container using a shared config/storage directory in /private/var/lib/pgadmin on the host:
docker run -p 443:443 \ ```console
docker run -p 443:8443 \
-v "/private/var/lib/pgadmin:/var/lib/pgadmin" \ -v "/private/var/lib/pgadmin:/var/lib/pgadmin" \
-v "/path/to/certificate.cert:/certs/server.cert" \ -v "/path/to/certificate.cert:/certs/server.cert" \
-v "/path/to/certificate.key:/certs/server.key" \ -v "/path/to/certificate.key:/certs/server.key" \
-e "PGADMIN_DEFAULT_EMAIL=user@domain.com" \ -e "PGADMIN_SETUP_EMAIL=user@domain.com" \
-e "PGADMIN_DEFAULT_PASSWORD=SuperSecret" \ -e "PGADMIN_SETUP_PASSWORD=SuperSecret" \
-e "PGADMIN_ENABLE_TLS=True" \ -e "PGADMIN_ENABLE_TLS=1" \
-e "PGADMIN_SERVER_NAME=pgadmin.domain.com" \ -d pgadmin4
-d pgadmin4 ```

View File

@ -41,60 +41,22 @@ if [ -d docker-build ]; then
rm -rf docker-build rm -rf docker-build
fi fi
mkdir docker-build mkdir -p docker-build/pgadmin4
# Create the output directory if not present
if [ ! -d dist ]; then
mkdir dist
fi
# Build the clean tree # Build the clean tree
for FILE in `git ls-files web` echo Copying source tree...
do git archive HEAD -- docs web requirements.txt | tar xvf - -C docker-build/pgadmin4
echo Adding $FILE
# We use tar here to preserve the path, as Mac (for example) doesn't support cp --parents
tar cf - $FILE | (cd docker-build; tar xf -)
done
pushd web
yarn install
yarn run bundle
rm -rf pgadmin/static/js/generated/.cache
for FILE in `ls -d pgadmin/static/js/generated/*`
do
echo Adding $FILE
tar cf - $FILE | (cd ../docker-build/web; tar xf -)
done
popd
# Build the docs
if [ -d docs/en_US/_build/html ]; then
rm -rf docs/en_US/_build/html
fi
LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 make -C docs/en_US -f Makefile.sphinx html
mkdir docker-build/web/docs
cp -R docs/en_US/_build/html/* docker-build/web/docs/
# Configure pgAdmin
echo "HELP_PATH = '../../docs/'" >> docker-build/web/config_distro.py
echo "DEFAULT_BINARY_PATHS = {" >> docker-build/web/config_distro.py
echo " 'pg': ''," >> docker-build/web/config_distro.py
echo " 'ppas': ''," >> docker-build/web/config_distro.py
echo " 'gpdb': ''" >> docker-build/web/config_distro.py
echo "}" >> docker-build/web/config_distro.py
# Copy the Docker specific assets into place # Copy the Docker specific assets into place
cp pkg/docker/Dockerfile docker-build/ cp pkg/docker/Dockerfile \
cp pkg/docker/entry.sh docker-build/ pkg/docker/entrypoint.sh \
cp pkg/docker/pgadmin4.conf.j2 docker-build/ pkg/docker/config_distro.py \
cp requirements.txt docker-build/ pkg/docker/run_pgadmin.py \
pkg/docker/.dockerignore \
docker-build/
# Build the container # Build the container
docker build docker-build -t $CONTAINER_NAME \ docker build docker-build -t $CONTAINER_NAME \
-t $CONTAINER_NAME:latest \ -t $CONTAINER_NAME:latest \
-t $CONTAINER_NAME:$APP_RELEASE \ -t $CONTAINER_NAME:$APP_RELEASE \
-t $CONTAINER_NAME:$APP_LONG_VERSION -t $CONTAINER_NAME:$APP_LONG_VERSION

View File

@ -0,0 +1,4 @@
HELP_PATH = '../../docs'
DEFAULT_BINARY_PATHS = {
'pg': '/usr/bin'
}

View File

@ -1,29 +0,0 @@
#!/usr/bin/env bash
########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
#########################################################################
export PGADMIN_SETUP_EMAIL=${PGADMIN_DEFAULT_EMAIL}
export PGADMIN_SETUP_PASSWORD=${PGADMIN_DEFAULT_PASSWORD}
if [ ${PGADMIN_ENABLE_TLS} != "True" ]; then
if [ -f /etc/httpd/conf.d/ssl.conf ]; then
mv /etc/httpd/conf.d/ssl.conf /etc/httpd/conf.d/ssl.conf.disabled
fi
else
if [ -f /etc/httpd/conf.d/ssl.conf.disabled ]; then
mv /etc/httpd/conf.d/ssl.conf.disabled /etc/httpd/conf.d/ssl.conf
fi
fi
j2 /templates/pgadmin4.conf.j2 > /etc/httpd/conf.d/pgadmin4.conf
rm -f /run/httpd/httpd.pid
/usr/sbin/httpd -D FOREGROUND

26
pkg/docker/entrypoint.sh Executable file
View File

@ -0,0 +1,26 @@
#!/bin/sh
if [ ! -f /var/lib/pgadmin/pgadmin4.db ]; then
if [ -z "${PGADMIN_DEFAULT_EMAIL}" -o -z "${PGADMIN_DEFAULT_PASSWORD}" ]; then
echo 'You need to specify PGADMIN_DEFAULT_EMAIL and PGADMIN_DEFAULT_PASSWORD environment variables'
exit 1
fi
# Set the default username and password in a
# backwards compatible way
export PGADMIN_SETUP_EMAIL=${PGADMIN_DEFAULT_EMAIL}
export PGADMIN_SETUP_PASSWORD=${PGADMIN_DEFAULT_PASSWORD}
# Initialize DB before starting Gunicorn
# Importing pgadmin4 (from this script) is enough
python run_pgadmin.py
fi
# NOTE: currently pgadmin can run only with 1 worker due to sessions implementation
# Using --threads to have multi-threaded single-process worker
if [ ! -z ${PGADMIN_ENABLE_TLS} ]; then
exec gunicorn --bind 0.0.0.0:${PGADMIN_LISTEN_PORT:-443} -w 1 --threads ${GUNICORN_THREADS:-25} --access-logfile - --keyfile /certs/server.key --certfile /certs/server.cert run_pgadmin:app
else
exec gunicorn --bind 0.0.0.0:${PGADMIN_LISTEN_PORT:-80} -w 1 --threads ${GUNICORN_THREADS:-25} --access-logfile - run_pgadmin:app
fi

View File

@ -1,43 +0,0 @@
########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
#########################################################################
ServerName {{ PGADMIN_SERVER_NAME }}
{% if PGADMIN_ENABLE_TLS|default('False') == 'True' %}
LoadModule ssl_module modules/mod_ssl.so
<VirtualHost *:443>
SSLEngine on
SSLCipherSuite HIGH:!aNULL:!MD5
SSLCertificateFile "/certs/server.cert"
SSLCertificateKeyFile "/certs/server.key"
ServerName {{ PGADMIN_SERVER_NAME }}
WSGIDaemonProcess pgadmin processes=1 threads=25
WSGIScriptAlias / /var/www/pgadmin/pgAdmin4.wsgi
<Directory /var/www/pgadmin>
WSGIProcessGroup pgadmin
WSGIApplicationGroup %{GLOBAL}
Order deny,allow
Allow from all
</Directory>
</VirtualHost>
{% else %}
<VirtualHost *:80>
WSGIDaemonProcess pgadmin processes=1 threads=25
WSGIScriptAlias / /var/www/pgadmin/pgAdmin4.wsgi
<Directory /var/www/pgadmin>
WSGIProcessGroup pgadmin
WSGIApplicationGroup %{GLOBAL}
Order deny,allow
Allow from all
</Directory>
</VirtualHost>
{% endif %}

View File

@ -0,0 +1,4 @@
import builtins
builtins.SERVER_MODE = True
from pgAdmin4 import app