diff --git a/.docker/entrypoint.sh b/.docker/entrypoint.sh new file mode 100644 index 0000000000000000000000000000000000000000..0ecff72338fc62dc9b9c1be69222c6a16fa4c061 --- /dev/null +++ b/.docker/entrypoint.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +function wait_for_db() +{ + while ! ./manage.py sqlflush > /dev/null 2>&1 ;do + echo "Waiting for the db to be ready." + sleep 1 + done +} + +if [ "$SECRET_KEY" == "" ] ;then + echo "Need the environment variable SECRET_KEY." + exit 1 +fi + +echo "" > ./AKPlanning/settings_secrets.py # Reset file in case we ran before +echo "SECRET_KEY = '$SECRET_KEY'" >> ./AKPlanning/settings_secrets.py +echo "HOSTS = $HOSTS" >> ./AKPlanning/settings_secrets.py +echo "DB_NAME = '$DB_NAME'" >> ./AKPlanning/settings_secrets.py +echo "DB_USER = '$DB_USER'" >> ./AKPlanning/settings_secrets.py +echo "DB_PASSWORD = '$DB_PASSWORD'" >> ./AKPlanning/settings_secrets.py +echo "DB_HOST = '$DB_HOST'" >> ./AKPlanning/settings_secrets.py + +if [ "$AUTO_MIGRATE_DB" == "true" ] ;then + wait_for_db + echo "Applying DB migrations" + ./manage.py migrate +fi + +if [ "$DJANGO_SUPERUSER_PASSWORD" != "" ] ;then + wait_for_db + echo "Trying to create superuser." + ./manage.py createsuperuser --noinput +fi + +env | while IFS= read -r line; do + value=${line#*=} + name=${line%%=*} + case $name in EXTRA_DJANGO_SETTING*) + echo -e "$value" > "./AKPlanning/settings/$name.py" + esac +done + +./manage.py collectstatic --noinput +./manage.py compilemessages -l de_DE +uwsgi --ini .docker/uwsgi.ini diff --git a/.docker/extra_requirements.txt b/.docker/extra_requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..b64ab47dac5a3f12d7817e2c402f71d83f5c1102 --- /dev/null +++ b/.docker/extra_requirements.txt @@ -0,0 +1 @@ +uwsgi==2.0.19.1 diff --git a/.docker/uwsgi.ini b/.docker/uwsgi.ini new file mode 100644 index 0000000000000000000000000000000000000000..7115f719b2439ceb4e2a882fa49cc8b80d5d56d2 --- /dev/null +++ b/.docker/uwsgi.ini @@ -0,0 +1,6 @@ +[uwsgi] +socket = 0.0.0.0:3035 +wsgi-file = AKPlanning/wsgi.py +env = DJANGO_SETTINGS_MODULE=AKPlanning.settings_production +processes = 4 +threads = 2 diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..ba3357efd35d688947d54565fb72214acbe627c9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +apache-akplanning.conf +CODE_OF_CONDUCT.md +CONTRIBUTING.md +CONTRIBUTORS.md +.git +.gitignore +.gitlab-ci.yml +INSTALL.md +LICENSE.md +README.md +uwsgi-akplanning.ini diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..32041dcb86e187e2803dac267f672bc8f4339806 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3-alpine + +RUN apk add --no-cache gcc python3-dev musl-dev libffi-dev mariadb-connector-c-dev gettext + +ADD . /app +WORKDIR /app + +RUN pip install -r requirements.txt -r .docker/extra_requirements.txt + +ENV DJANGO_SETTINGS_MODULE=AKPlanning.settings_production + +RUN mkdir /app/AKPlanning/settings + +EXPOSE 3035 +CMD ["sh", "/app/.docker/entrypoint.sh"] diff --git a/INSTALL.md b/INSTALL.md index 407945e82631fc8b733940804cd5ac0c5075561f..f242088a0f8ae324465347de2848f236d4902b3c 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -104,6 +104,140 @@ is not stored in any repository or similar, and disable DEBUG mode (``settings.p 1. restart uwsgi ``sudo systemctl restart uwsgi`` 1. execute the update script ``./Utils/update.sh --prod`` + +## Deployment Setup using Docker +This project also provides a docker file for easy deployment. + +The container described by the docker file only contains the project itself. +Additional containers for the database and webserver are needed to use it. + +The following [docker-compose](https://docs.docker.com/compose/) file shows a typical usage: +``` +version: "3" + +networks: + akplanning: + external: false + +volumes: + static-files: + +services: + mariadb: + image: mariadb:10 + restart: always + environment: + MYSQL_ROOT_PASSWORD: supermegasecrey + MYSQL_DATABASE: akplanning + MYSQL_USER: akplanning + MYSQL_PASSWORD: secret + TZ: Europe/Berlin + networks: + - akplanning + + akplanning-server: + image: neumantm/akplanning:2021-03-10 + restart: always + environment: + SECRET_KEY: superlongandsupersecret + DB_HOST: mariadb + DB_USER: akplanning + DB_NAME: akplanning + DB_PASSWORD: secret + HOSTS: "['akplanning.example.net', 'akplanning.example.de']" + TZ: Europe/Berlin + AUTO_MIGRATE_DB: 'true' + DJANGO_SUPERUSER_USERNAME: admin + DJANGO_SUPERUSER_EMAIL: admin@example.com + DJANGO_SUPERUSER_PASSWORD: supersecret + EXTRA_DJANGO_SETTING_FOO: DJANGO_FOO = True\nDJANGO_BAR = False + depends_on: + - mariadb + networks: + - akplanning + volumes: + - static-files:/app/static + + web-server: + image: nginx + restart: always + volumes: + - /path/to/nginx.conf:/etc/nginx/nginx.conf:ro + - static-files:/var/www/akplanning-static + ports: + - "8080:80" + depends_on: + - akplanning-server + networks: + - akplanning +``` + +The `nginx.conf` would look like this: + +``` +user nginx; +worker_processes 1; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log /var/log/nginx/access.log main; + sendfile on; + keepalive_timeout 65; + server { + listen 80; + server_name localhost:8080; + + location /static/ { + alias /var/www/akplanning-static/; + } + + location / { + include /etc/nginx/uwsgi_params; + uwsgi_pass uwsgi://akplanning-server:3035; + } + } +} +``` +### Initializing and migrating database +On the first start, the database must be initialized (the Tables created and so on). +When updating the project the database must be migrated. +Both are done using the `migrate` command. + +This can be done manually by running the following command after the container has started: +`docker-compose exec -it akplanning-server ./manage.py migrate` + +It can also be done automatically on each container start by setting `AUTO_MIGRATE_DB` to the string `true` +(as shown in the docker-compose file above). + +Database migration may lead to the corruption or loss of data in some cases. +Make sure you have a backup before running the command and be very careful with enabling auto migration. + +### Creating initial superuser +There are two ways to create the initial superuser when using the docker container. +For both the database must have been intialized before. + +The first way is already shown in the docker-compose file above: +Using the environment variables `DJANGO_SUPERUSER_{USERNAME,EMAIL,PASSWORD}`. + +The second way is to run the following command after the container has started: +`docker-compose exec -it akplanning-server ./manage.py createsuperuser` + +### Extra django settings +For simple cases you can pass environment variables starting with `EXTRA_DJANGO_SETTING`. +The content of such variables is written into python files, which are loaded as settings. + +For more complex scenarios you can also mount a docker volume to `/app/AKPlanning/settings` and add any number of python files to the volume. + ## Updates To update the setup to the current version on the main branch of the repository use the update @@ -111,3 +245,7 @@ script ``Utils/update.sh`` or ``Utils/update.sh --prod`` in production. Afterwards, you may check your setup by executing ``Utils/check.sh`` or ``Utils/check.sh --prod`` in production. +### Updating when using docker + +To update when using docker, just switch the tag of the image for `akplanning-server`. +Then (if `AUTO_MIGRATE_DB` is not enabled), do a database migration as described in [Initializing and migrating database](#initializing-and-migrating-database)