eGSIM deployment

The following cheatsheet covers two main aspect: Maintenance and Deployment (no docker container). It assumes that you already have access to a remote machine. The following instructions have been tested on Ubuntu 18.04 MATE. The procedure to obtain or update an SSL certificate (in order to have an https address) is covered in a separate document

Set the following variables to see meaningful paths (the document will update automatically):

The domain name of the computer, i.e. the internet address it is assigned to, e.g.: rz-vm183d.gfz-potsdam.de, www.example.com
The eGSIM Django project name where all Python packages will be placed (including the eGSIM git repository). As this also is the name of the Gunicorn service file: please provide only alphanumeric or underscore characters
The Django user (can be the same as $DJANGO_PROJECT)
the Python version of the virtual environment (major.medium or major.medium.minor). The relative Python virtual environment should be created by convention under $VENV_PATH (see below)

Maintenance

This section covers informations for an already installed program. To perform a server installation, scroll down

Important files

$EGSIM_CWD
where the cloned eGSIM project and (usually) the Python virtual environment resides. This is the place to go to perform git operations and activate the virtual environment
$VENV_PATH
The Python path (virtual environment)
$EGSIM_CONFIG_DIR/settings.py
the settings, keep confidential, root access needed
/etc/systemd/system/$DJANGO_PROJECT.service
the Gunicorn script to start the django production server
$EGSIM_LOG_DIR/gunicorn.log
inspects the django server log
Example: sudo tail -30 $EGSIM_LOG_DIR/gunicorn.log
/etc/nginx/sites-available/egsim
the nginx config for the egsim site (new deployment configurations will be added to this file)
/var/log/nginx/access.log
inspects the nginx log (access infos).
Example: sudo tail -30 /var/log/nginx/access.log
/var/log/nginx/error.log
inspects the nginx log (error infos)
Example: sudo tail -30 /var/log/nginx/error.log
/etc/nginx/sites-available/default
the default nginx config. It should be the "fallback" when all other configs are not used. We saved the original not-modified config in default.backup in the same directory
/etc/nginx/nginx.conf
the main nginx configuration (should not be modified frequently and only in rare cases)
/etc/hosts
where the maching hosts are written: should not be modified frequently, we did it only once to add the domain name (line "127.0.0.1 $DOMAIN", without quotes)

Common operations

Update the package

Warning: this assumes there are: NO changes to (=migrations to be performed on) the eGSIM database, NO Python libraries to be updated or newly installed, NO static files (e.g., images, css, javascript) to be updated

Inside $EGSIM_CWD, as root user:


(cd $EGSIM_CWD && git pull)
service $DJANGO_PROJECT restart
service nginx restart

Update the package and the static files

Warning: this assumes there are: NO changes to (=migrations to be performed on) the eGSIM database, NO Python libraries to be updated or newly installed

Same as above but we need to run Django collectstatic in the activated virtualenv


source $VENV_ACTIVATE
(cd $EGSIM_CWD && git pull)
(cd $EGSIM_CWD; export PYTHONPATH=$EGSIM_CONFIG_DIR DJANGO_SETTINGS_MODULE=settings; python manage.py collectstatic)
service $DJANGO_PROJECT restart
service nginx restart
deactivate

Update the db schema

This assumes that we created a new migration file with Django `makemigration`. Note: we usually just throw away the old migrations and re-create a new migration file, usually `api/migrations/0001_initial.py` or something similar. this means that we also delete the old database for safety.

IMPORTANT: Execute these command as $DJANGO_USER, thus FIRST type:


su $DJANGO_USER
You probably need to re-activate the Python virtual env:

source $VENV_ACTIVATE
Now update the package, remove the db (see note above), (re)create the database (`migrate`) and populate it with eGSIM data (`egsim_init`):

(cd $EGSIM_CWD && git pull)
rm $EGSIM_DATA_DIR/egsim.db
(cd $EGSIM_CWD; export PYTHONPATH=$EGSIM_CONFIG_DIR DJANGO_SETTINGS_MODULE=settings; python manage.py migrate)
(cd $EGSIM_CWD; export PYTHONPATH=$EGSIM_CONFIG_DIR DJANGO_SETTINGS_MODULE=settings; python manage.py egsim_init)
service $DJANGO_PROJECT restart
service nginx restart

Install system fonts (to produce and download the same plots visible on the browser)

See snippet below (change the font name and the URL in case. The font directory "egsim" should be already created, otherwise create it with the name you wish):

cd /usr/share/fonts
mkdir egsim
cd egsim
wget "https://fonts.google.com/download?family=Encode%20Sans%20Semi%20Condensed" -O ./Encode_Sans_Semi_Condensed.zip
unzip -d . ./Encode_Sans_Semi_Condensed.zip
sudo chmod -R --reference=/usr/share/fonts/opentype /usr/share/fonts/egsim
fc-cache -fv
# check if font is installed:
fc-match EncodeSansSemiCondensed
More details here (opens in new tab)

To check the font name, type fc-list (optionally with grep) and check the font name which is displayed after the first colon ":". E.g.:

fc-list | grep Encode
More details here (opens in new tab)

Server installation

System requirements

Update and upgrade

sudo apt-get update && sudo apt-get upgrade

Install required libraries


apt install git nginx-light curl libhdf5-dev gcc python3-rtree

(libhdf5-dev gcc required by openquake, see below. python3-rtree required by gmpe-smtk, see below)

Optional installations


apt install vim

Install Python desired version

Add repository (one time operation):

sudo add-apt-repository ppa:deadsnakes/ppa
Install your Python desired version Please check that the value of the <input> component on top of this document is a correct Python version (e.g. "3.11.2" or "3.10") before running the commands below

sudo apt-get update
sudo apt-get install $CURRENT_PYTHON $CURRENT_PYTHON-dev $CURRENT_PYTHON-distutils $CURRENT_PYTHON-venv
Add Django user if not present yet (click for details) [UPDATE 3/2023]: If you keep the same user as previous versions, skip this section

	adduser --system --home=$EGSIM_DATA_DIR --no-create-home --disabled-password --group --shell=/bin/bash $DJANGO_USER
	
Optional: to configure $DJANGO_USER terminal sessions (e.g. colors, completion, history, command aliases): copy the root bashrc into $DJANGO_USER, i.e. typing as root: cp $HOME/.bashrc /var/opt/$DJANGO_USER/.bashrc (where /var/opt/$DJANGO_USER is supposed to be the $HOME variable of $DJANGO_USER: in case of doubts: su $DJANGO_USER and echo $HOME)

Final note: as always, proceed installing Python packages being aware that in case of problems, the solution after googling is to apt-get install missing packages. For instance, in case of Python 3.9 installation we had to: sudo apt-get install gfortran libopenblas-dev liblapack-dev libfreetype6-dev libgeos-dev

Program requirements

Create project folder


cd /opt
mkdir $DJANGO_PROJECT
cd $DJANGO_PROJECT

Clone package(s)

eGSIM:

git clone https://github.com/rizac/eGSIM.git
Check that the path of eGSIM repository is: $EGSIM_CWD

Create the virtual environment

[UPDATE 10/2021] IMPORTANT: you need to have python-venv installed (e.g. apt-get install $CURRENT_PYTHON-venv, see above).

Installation


$CURRENT_PYTHON -m venv $VENV_PATH

Activation


source $VENV_ACTIVATE

IMPORTANT: from now on, every Python-related operation must be performed within your activated virtual env

Check correct path: activate virtualenv and type


which python
python --version

the Python path should refer to the newly installed Python in the current virtual environment (Python version should be 3.7+)

We assume hereafter that you are in $DJANGO_PROJECT

Install the repositories

For any error in any of the 'pip install' commands below, the most likely cause is some system package missing: we suggest to google the error first, then check if anybody suggests to apt install missing packages and follow the instructions. Ignore at first complicated suggestions, they are rarely the solution to your problem. This doc was created precisely following the pattern above. Eventually, we added in the apt-install command above all packages that we discovered missing, so hopefully a fresh new installation might work fine without any new error

Install eGSIM: activate virtualenv, then


cd ./eGSIM/
pip install -r ./requirements.dev.txt

Create project structure

Data directory with correct ownership


mkdir $EGSIM_DATA_DIR
chown $DJANGO_USER $EGSIM_DATA_DIR

Log directory with correct ownership


mkdir $EGSIM_LOG_DIR
chown $DJANGO_USER $EGSIM_LOG_DIR

Config directory. Change group ownership (with the flag --group added to the command adduser above we created a group with the same name as $DJANGO_USER). Change also permissions (750 means the current user, i.e. root, can read, write, and execute while the group and others cannot write)


mkdir $EGSIM_CONFIG_DIR
chgrp $DJANGO_USER $EGSIM_CONFIG_DIR
chmod 750 $EGSIM_CONFIG_DIR

Setup the project

Create the settings file: Create (or copy from a previous deployment file):

$EGSIM_CONFIG_DIR/settings.py

For reference, see the template below, which is automatically filled according to the variables set in this page:

"""
Django settings for eGSIM project.

Copied from https://djangodeployment.com/wp-content/uploads/2017/03/Django-deployment-cheatsheet.pdf

This file should be copied from previous deployments and modified accordingly
Listed below are the Django variables that are most likely to be edited. Note that this module
inherits from the debug settings under the egsim git repository
"""

from egsim.settings_debug import *

DEBUG = False  # DO NOT CHANGE THIS!
ALLOWED_HOSTS = ['$DOMAIN']
# SECRET_KEY SHOULD BE UNIQUE FOR EACH SETTINGS FILE AND CAN BE GENERATED ON THE TERMINAL (WITHIN THE DJANGO VIRUAL ENV)  WITH THE COMMAND:
# python -c "from django.core.management.utils import get_random_secret_key;print(get_random_secret_key())"
# COPY THE OTUPUT STRING HERE BELOW
SECRET_KEY = ''
DATABASES = {
	'default': {
		'ENGINE': 'django.db.backends.sqlite3',
		'NAME': '$EGSIM_DATA_DIR/egsim.db',
	}
}
# static files root (path on the server)
STATIC_ROOT = '/var/cache/$DJANGO_PROJECT/static/'
# static files url (already set in settings debug, here for ref.):
# STATIC_URL = '/static/'

# media root (path on the server):
MEDIA_ROOT = '$EGSIM_DATA_DIR/media/'
# static files url (already set in settings debug, here for ref.):
# MEDIA_URL = '/media/'

CSRF_TRUSTED_ORIGINS = ["https://$DOMAIN"]  # required in Django 4+. Https or http depends on your SSL certificate
Then, inside $EGSIM_CWD:

Run collectstatic: copy static files to the new location. Activate virtualenv and:


(cd $EGSIM_CWD; export PYTHONPATH=$EGSIM_CONFIG_DIR DJANGO_SETTINGS_MODULE=settings; python manage.py collectstatic)

(the PYTHONPATH simply makes the option --settings=settings to point to the newly created settings.py). You should see a message like: '175 static files copied to '/var/cache/$DJANGO_PROJECT/static' (the number of files might be huge but there are also Django css and so on

Migration and data initialization scripts

IMPORTANT: Execute these command as $DJANGO_USER, thus FIRST type:


su $DJANGO_USER
You probably need to re-activate the Python virtual env:

source $VENV_ACTIVATE
With the Python virtualenv activated, execute:

Django migration


(cd $EGSIM_CWD; export PYTHONPATH=$EGSIM_CONFIG_DIR DJANGO_SETTINGS_MODULE=settings; python manage.py migrate)

Initialize our database (OpenQuake GSIMs IMTs and TRTs)


(cd $EGSIM_CWD; export PYTHONPATH=$EGSIM_CONFIG_DIR DJANGO_SETTINGS_MODULE=settings; python manage.py egsim_init)

Server configuration

NGINX is the main application server, however, it delegates Gunicorn for serving the Python code (everything except static and media files).

In the following, we will configure Gunicorn and Nginx.

IMPORTANT: remember that After any modification to the code or the config below, before checking the results online, is better to restart the services


service $DJANGO_PROJECT restart
service nginx restart

Gunicorn

Become root:


su root

The command above should get you out of the activated virtual environment. (Re)activate virtualenv and install Gunicorn:


pip install gunicorn

OPTIONAL, test: start Gunicorn and go to $DOMAIN (from the server or another external computer) to test:


export PYTHONPATH=$EGSIM_CONFIG_DIR; export DJANGO_SETTINGS_MODULE=settings
gunicorn --workers=4 --log-file=$EGSIM_LOG_DIR/gunicorn.log --bind=127.0.0.1:$GUNICORN_PORT --bind=[::1]:$GUNICORN_PORT egsim.wsgi:application

Create the systemd task to start automatically egsim django server


su $DJANGO_USER

Save the content below in /etc/systemd/system/$DJANGO_PROJECT.service. (or copy a previous service file and use the template below as reference. Remember that $DJANGO_PROJECT cannot contain dots)

Important: in the file below, note the --bind argument: it is typed twice to bind our Django application on localhost both on ipv6 and ipv4 interfaces. We will then tell Nginx (see below) to proxy each request to localhost (see proxy_pass argument) delegating Gunicorn for everything that is not a static file (everything not in media or static directories)

The URL port of the --bind arguments is set as , you can change the value in the field to automatically update the templates below. Changing the port is useful if you want to run several Gunicorn instances from Nginx (e.g., previous versions alongside the current)


[Unit]
Description=$DJANGO_PROJECT

[Service]
User=$DJANGO_USER
Group=$DJANGO_USER
Environment="PYTHONPATH=$EGSIM_CONFIG_DIR:$VENV_PATH"
Environment="DJANGO_SETTINGS_MODULE=settings"
WorkingDirectory=$EGSIM_CWD
ExecStart=$VENV_PATH/bin/gunicorn \
	--workers=4 \
	--log-file=$EGSIM_LOG_DIR/gunicorn.log \
	--bind=127.0.0.1:$GUNICORN_PORT --bind=[::1]:$GUNICORN_PORT \
	egsim.wsgi:application

[Install]
WantedBy=multi-user.target

To restart the service (second line is mandatory only if the script above has been changed):


service $DJANGO_PROJECT restart

To restart the service if the script above has been changed:


service $DJANGO_PROJECT stop
systemctl daemon-reload
service $DJANGO_PROJECT start

Optional. To start the service automatically on boot:


systemctl enable $DJANGO_PROJECT

NGINX

Create /etc/nginx/sites-available/$DJANGO_PROJECT (or edit the existing one). See template below with the relevant variables to be overwritten. NOTE: proxy_pass below must point to the Gunicorn URL just configured (see above).

The line proxy_pass http://localhost:$GUNICORN_PORT; is the line that forwards to Gunicorn all locations starting with '/' and not matching '/static/' or '/media/'


server {
	listen 80;
	listen [::]:80;
	server_name $DOMAIN www.$DOMAIN;
	location / {
		proxy_pass http://localhost:$GUNICORN_PORT;
	}
	location /static/ {
		alias /var/cache/$DJANGO_PROJECT/static/;
	}
	location /media/ {
		alias $EGSIM_DATA_DIR/media/;
	}
}
Add symbolic link to sites-enabled and $DOMAIN to known hosts (click for details) [UPDATE 3/2023]: The operations below should be already performed and thus unnecessary

Create symbolik link in sites-enabled:


	cd /etc/nginx/sites-enabled
	ln -s ../sites-available/$DJANGO_PROJECT .
	

Add $DOMAIN to known host (This should be investigated: it is not mentioned in any documentation, but we needed to do it to make the site work): open /etc/hosts and add the line


	127.0.0.1	$DOMAIN