Production deployment using Docker¶
Get started¶
Check out the example on your development machine.
In this example you will create a docker image for your example app with all its runtime dependencies installed along with the required config using docker-compose on your local machine to simulate a production environment.
The web server (Nginx) and database (PostgreSQL) are run in separate containers.
The example assumes you need a PostgreSQL database, however it can be changed to be MySQL fairly easily.
Follow the instructions for your chosen platform to install docker, and also docker-compose.
Create docker images¶
In the checked out example directory, build the required images.
Note
docker-compose will reference the provided docker-compose.yml file.
docker-compose build
Verify that the required images built successfully:
docker images | grep hellodockernginx
Expect output similar to:
hellodockernginx_web latest e41c79d7ca1a 8 seconds ago 153MB
hellodockernginx latest a351ff871b20 15 seconds ago 270MB
Understanding docker-compose.yaml file¶
The docker-compose.yaml file in this example configures 3 services:
- database
This runs a PostgreSQL server, in a container named postgresql, with super user and password as specified in the given environment variables.
- app
The hellodockernginx image runs your app in a uwsgi server. Your app’s config points to the database service using the default ports. See prod/etc/reahl.config.py:
# In production this has to be set, to the name of the egg of your application: reahlsystem.root_egg = 'hellodockernginx' # If using PostgreSQL: reahlsystem.connection_uri = 'postgresql://hellodockernginx:hellodockernginx@database/hellodockernginx' # If using MySQL: #reahlsystem.connection_uri = 'mysql://hellodockernginx:hellodockernginx@database/hellodockernginx' reahlsystem.debug = False
Since this example uses services defined for docker-compose, this configuration can hard-code the service name database.
The uwsi config ensures the hellodockernginxwsgi module is run by uwsgi:
[uwsgi] socket = :8080 module = hellodockernginxwsgi:application venv = /app/venv master = 1 processes = 4 plugin = python3 uid = www-data gid = www-data
The hellodockernginxwsgi module is part of your application and hard-codes where the Reahl configuration is read from:
from reahl.web.fw import ReahlWSGIApplication application = ReahlWSGIApplication.from_directory('/etc/app-reahl', start_on_first_request=True)
Locations in the built image to take note of:
- App is installed in a venv
/app/venv
- App’s Reahl config directory
/etc/app-reahl
- App static file location
/app/www
- wsgi config
/etc/app-wsgi.ini
- This image is built using prod/Dockerfile which works in two stages base and build:
base In this stage, development dependencies are installed, and a wheel is built for your app.
build In this stage, a venv is created, and your built wheel is installed along with its static files.
- web
The web service runs nginx in a container named hellodockernginx_web. Nginx is configured in prod/nginx/app.conf to reverse-proxy to your app using the uwsgi_pass directive. Note that since this is using services defined for docker-compose, this configuration can hard-code the service name app:
server { listen 80; server_name _; location / { include uwsgi_params; uwsgi_param HTTPS off; uwsgi_pass app:8080; uwsgi_ignore_headers Set-Cookie; } } server { listen 443 ssl; server_name _; #ssl_certificate /etc/ssl/certs/app.pem; #ssl_certificate_key /etc/ssl/private/app.key; ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem; ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key; ssl_session_timeout 5m; ssl_protocols SSLv3 TLSv1.1 TLSv1.2; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP; ssl_prefer_server_ciphers on; location / { include uwsgi_params; uwsgi_param HTTPS on; uwsgi_pass app:8080; uwsgi_ignore_headers Set-Cookie; } }
In order to be able to copy this config into the built image, this service also builds its container from prod/nginx/Dockerfile:
FROM nginx:1.19 RUN apt-get update && \ apt-get install ssl-cert && \ rm -rf /var/cache/apt/* COPY prod/nginx/app.conf /etc/nginx/conf.d/default.conf
Note
This configuration uses the insecure snakeoil certificates shipped in the ssl-cert package. You will install your own, mounting the actual certificates via a volume external to the image itself.
Locations in the built image to take note of:
- nginx config
/etc/nginx/conf.d/default.conf
Test the image locally using docker-compose¶
Spin up containers¶
Before deploying the images in your production environment, you can test them locally. Spin up containers for the built images and connect to your app.
docker-compose up -d
Expect:
Creating network "hellodockernginx_default" with the default driver
Creating hellodockernginx_database_1 ... done
Creating hellodockernginx_app_1 ... done
Creating hellodockernginx_web_1 ... done
List the running containers:
docker container list
Expect output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ca0dd108aa59 hellodockernginx_web "/docker-entrypoint.…" 2 hours ago Up 2 hours 0.0.0.0:8080->80/tcp, 0.0.0.0:8443->443/tcp hellodockernginx_web_1
1e91b70b24c7 hellodockernginx "uwsgi --ini /etc/ap…" 2 hours ago Up 2 hours 8080/tcp hellodockernginx_app_1
26c5e89f5fee postgres:12.3 "docker-entrypoint.s…" 2 hours ago Up 2 hours 5432/tcp hellodockernginx_database_1
Create and initialise the database¶
Prepare the database for your app by executing:
# If using PostgreSQL:
docker-compose exec -T -e PGPASSWORD=reahl app /app/venv/bin/reahl createdbuser -U developer /etc/app-reahl
docker-compose exec -T -e PGPASSWORD=reahl app /app/venv/bin/reahl createdb -U developer /etc/app-reahl
docker-compose exec -T app /app/venv/bin/reahl createdbtables /etc/app-reahl
# If using MySQL:
#docker-compose exec -T -e MYSQL_PWD=reahl app /app/venv/bin/reahl createdbuser -U root /etc/app-reahl
#docker-compose exec -T -e MYSQL_PWD=reahl app /app/venv/bin/reahl createdb -U root /etc/app-reahl
#docker-compose exec -T app /app/venv/bin/reahl createdbtables /etc/app-reahl
Check the database:
docker-compose exec -T app /app/venv/bin/reahl sizedb /etc/app-reahl
Expect output like:
Database size: 8177 kB
Inspect running app container¶
To inspect the app container, step into it with:
docker exec -ti hellodockernginx_app_1 bash -l
View the logs for the app container:
docker logs hellodockernginx_app_1
Test your app being served by nginx¶
Open a browser tab to localhost:8080 and expect to see Hello World!
Or test it from a command line:
python3 -c "from urllib.request import urlopen; import re; print(re.search(r'<p>.*?</p>', urlopen('http://localhost:8080').read().decode('utf-8')).group(0))"
Similarly, expect:
`<p>Hello World!<p>`
Changes for a MySQL database¶
Modify these files that have been annotated with references to MySQL:
- pyproject.toml
Replace the dependency on “reahl-postgresqlsupport” with “reahl-mysqlsupport”
- prod/etc/reahl.config.d
Modify the config to contain the MySQL required settings
- prod/Dockerfile
Change the ENV variables to cater for MySQL dependencies
- scrips/setup_database.sh
Use the commands to connect to MySQL database container
- docker-compose.yml
Replace the Postgres database section with the MySQL section
Note
run `docker-compose down`
to stop and discard running containers.
Build and run the docker images again by following similar instructions given above.