Skip to content
This repository has been archived by the owner on Mar 27, 2023. It is now read-only.

Easier deployment #21

Merged
merged 49 commits into from
Jul 8, 2020
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
df5f732
Use docker entrypoint instead of deploy.sh
Baschdl Jun 15, 2020
4f37956
Use docker-compose directly instead of deploy.sh in travis.yml
Baschdl Jun 15, 2020
8b762fb
Correct formatting
Baschdl Jun 15, 2020
bc4943b
Set POT-Creation-Date to a constant
maltezacharias Jun 27, 2020
24258ce
Merge branch 'staging' into easier-deploy
maltezacharias Jun 27, 2020
2df0642
Fixing translations, people use POEDIT!!!
maltezacharias Jun 27, 2020
8641b2a
Remove app bind mount on production, README update
maltezacharias Jun 27, 2020
51662ca
Set user for backend via .env
maltezacharias Jun 27, 2020
859bc36
Move DB connectivity check to entrypoint
maltezacharias Jun 27, 2020
2ce2100
Remove fixed backend container name
maltezacharias Jun 27, 2020
aa34fd6
Persist logs even after removal of containers
maltezacharias Jun 27, 2020
a890598
Move collectstatic to entry point
maltezacharias Jun 27, 2020
3323a38
Fix permissions when not running backend as root
maltezacharias Jun 27, 2020
b954276
Move non-root settings to development docker
maltezacharias Jun 27, 2020
9ce23ec
Enable django-admin on dev docker
maltezacharias Jun 27, 2020
834f580
Rename backend folder to same name as in repo
maltezacharias Jun 27, 2020
288cdbe
Add tty to backend, some error messages will be hidden otherwise
maltezacharias Jun 27, 2020
ac7170a
Incorporate changes into travis config
maltezacharias Jun 27, 2020
a1b11e2
fix bash script syntax error
maltezacharias Jun 27, 2020
6de896b
Update Travis Website availability check script
maltezacharias Jun 27, 2020
792565a
fix backup script
maltezacharias Jun 27, 2020
0f57882
Merge branch 'staging' into easier-deploy
maltezacharias Jun 27, 2020
934d963
Update README.md
maltezacharias Jul 2, 2020
a94a48c
Fix backup script
maltezacharias Jul 2, 2020
c458215
Disable hot_standby
maltezacharias Jul 2, 2020
cc96713
Merge branch 'easier-deploy' of https://github.com/match4everyone/mat…
maltezacharias Jul 2, 2020
e640346
Switch database container to volumes and remove bind mounts
maltezacharias Jul 2, 2020
ae6bde3
Move log files to subfolder of run
maltezacharias Jul 2, 2020
4ca5504
Move collectstatic to docker build process
maltezacharias Jul 2, 2020
8a22197
Fix .gitignore order for .gitkeep entries
maltezacharias Jul 2, 2020
1be0688
Add empty log directory
maltezacharias Jul 2, 2020
3c64dd5
Merge branch 'staging' into easier-deploy
maltezacharias Jul 2, 2020
2fe1c1d
Update log location in travis scripts
maltezacharias Jul 2, 2020
e264e16
Add sample gateway error pages & nginx configuration (copy from m4h P…
maltezacharias Jul 2, 2020
1c9f998
Travis: Show logs after test rather than inbetween
maltezacharias Jul 2, 2020
da54151
Wait for backend check for connectivity
maltezacharias Jul 2, 2020
19b9e67
Wait_for_backend check log files
maltezacharias Jul 2, 2020
f2b1478
Make docker container bind to localhost (see m4h PR 540)
maltezacharias Jul 1, 2020
e8d741f
New backup script for volumes
maltezacharias Jul 2, 2020
ec620d5
Merge branch 'staging' into easier-deploy
kevihiiin Jul 4, 2020
b2e3422
Update backend/run/.gitignore
kevihiiin Jul 5, 2020
3cd4bf6
Update backend/Dockerfile
kevihiiin Jul 5, 2020
73a78ea
Move Travis environmental variables to docker-compose.prod.yml
kevihiiin Jul 5, 2020
f17a5a4
Use backend.dev.env file in development mode
Baschdl Jul 8, 2020
cb9735c
Create also a backend.dev.env file on travis
Baschdl Jul 8, 2020
6ed9e48
Add django backup to backup.sh and improve documentation
maltezacharias Jul 8, 2020
fdf39d1
Move whitenoise to common config
maltezacharias Jul 8, 2020
4d2036c
Ignore VSCode workspace
maltezacharias Jul 8, 2020
959c23d
Merge branch 'staging' into easier-deploy
maltezacharias Jul 8, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Use backend_user to specify a user to run the backend container as.
# If none is specified root will be used
BACKEND_USER=user:group
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,6 @@ psd
thumb
sketch

# Ignore local docker-compose file, in case you make a copy for local changes
docker-compose.yml

# End of https://www.gitignore.io/api/react,django,python

# pycharm settings
Expand Down
7 changes: 3 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@ services:

before_script:
- ./scripts/write_envs_to_file.sh
- ./deploy.sh
- docker-compose -f docker-compose.yml -f docker-compose.prod.yml up --build -d
- ./scripts/wait_for_backend.sh
- docker logs $(docker ps --format '{{.Names}}' | grep backend)
- docker logs $(docker ps --format '{{.Names}}' | grep database)


script:
- bash scripts/check_website_availability.sh
- docker exec backend python3 manage.py test
- docker-compose -f docker-compose.yml -f docker-compose.prod.yml exec backend python3 manage.py test

after_script:
- docker-compose down
- docker-compose -f docker-compose.yml -f docker-compose.prod.yml down

env:
- POSTGRES_DB=match4everyone POSTGRES_PASSWORD="BuZK@HPB_FDpGx3gvnnB9@eypozpC8PeesGP7PUC*DDgbbj-Zpv3kRwCkmU6FQoL" POSTGRES_USER=match4everyone SECRET_KEY="QP=14DkrovY9qrqXmZk3sjgakmy@x#6Log7EiW-9K4g5W&xY)_yN3a*rsinLYP|"
54 changes: 25 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,47 @@ Originally developed as [match4healthcare](https://github.com/match4everyone/mat

## Quick install
- Copy `backend.prod.env.example` and `database.prod.env.example` to `backend.prod.env` and `database.prod.env` and fill in appropriate values
- Run `./deploy.sh` (uses Docker) and visit `http://localhost:8000/`
- Run `docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build` (uses Docker) and visit `http://localhost:8000/`

## Environment variables

TODO

## Install by hand
### Development
- Build images and run containers
`docker-compose -f docker-compose.dev.yml up --build`
`docker-compose up --build`
- Start previously built containers in background
`docker-compose start`
- Apply migrations
`docker exec backend python3 manage.py migrate`
- Collect static files
`docker exec backend python3 manage.py collectstatic`
- Load test data:
`docker exec backend python3 manage.py loaddata fixture.json`
- Load test data (file needs to exist inside backend folder):
`docker-compose exec backend django-admin loaddata fixture.json`

File changes in python files trigger an auto-reload of the server.
Migrations have to be executed with `docker exec backend python3 manage.py migrate`.
Migrations are automatically executed when the container starts.

After changes to the Docker configuration, you have to restart and build the containers with `docker-compose -f docker-compose.dev.yml up --build`.
After changes to the Docker configuration, you have to restart and build the containers with `docker-compose up --build`.

### Production
Set `SECRET_KEY`, `SENDGRID_API_KEY` and `MAPBOX_TOKEN`in `backend.prod.env` for Django
`POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD` inside `database.prod.env` for postgres on your host machine.
Also add a `SLACK_LOG_WEBHOOK` to enable slack logging.
Copy `backend.prod.env.example` to `backend.prod.env` and set variables as documented in the example file for Django
Copy `database.prod.env.example` to `database.prod.env` and set variables as documented in the example file for postgres on your host machine.

To run a container in production and in a new environment execute the `deploy.sh` script which builds the containers, runs all configurations and starts the web service.

If you want to deploy manually follow these steps closly:

1. Build the containers
(Run `export CURRENT_UID=$(id -u):$(id -g)` if you want to run the backend as non-root)
`docker-compose -f docker-compose.dev.yml -f docker-compose.prod.yml up -d --build`
2. Make messages
`docker exec --env PYTHONPATH="/match4everyone-backend:$PYTHONPATH" backend django-admin makemessages --no-location`
3. Compile messages
`docker exec --env PYTHONPATH="/match4everyone-backend:$PYTHONPATH" backend django-admin compilemessages`
4. Collect static
`docker exec backend python3 manage.py collectstatic --no-input`
5. Migrate
`docker exec backend python3 manage.py migrate`
5. Check if all the variables are correct
`docker exec backend python3 manage.py check`
6. Restart the backend container (important, whitenoise does not reload static files after it has started)
`docker-compose -f docker-compose.dev.yml -f docker-compose.prod.yml down && docker-compose -f docker-compose.dev.yml -f docker-compose.prod.yml up -d`
#### Build the containers
(Copy `.env.example` to `.env` and adjust variables if you want to run the backend as non-root)
`docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build`

Building containers will run a number of django tasks automatically:
- make messages (`django-admin makemessages --no-location`)
- compile messages (`django-admin compilemessages`)
- collect static files (`django-admin collectstatic --no-input`)

Starting the containers will run the following django tasks on every backend startup:
- Perform migrations (`django-admin migrate`)
- Perform system check (`django-admin check`)


## Helpful management commands

Expand Down Expand Up @@ -86,7 +82,7 @@ In order to run pre-commit checks every time, please run `pre-commit install` on
- Add translatable strings in python with `_("Welcome to my site.")` and import `from django.utils.translation import gettext as _` ([Documentation](https://docs.djangoproject.com/en/3.0/topics/i18n/translation/#internationalization-in-python-code))
- Add translatable strings in templates with `{% blocktrans %}This string will have {{ value }} inside.{% endblocktrans %}` or alternatively with the `trans` block and include `{% load i18n %}` at the top ([Documentation](https://docs.djangoproject.com/en/3.0/topics/i18n/translation/#internationalization-in-template-code))
- Update the translation file
`django-admin makemessages -l de --no-location --ignore 00_old_m4h_matching_code` (line numbers omitted to allow nicer merges)
`django-admin makemessages -l de --no-location` (line numbers omitted to allow nicer merges)
- Edit translations in `backend/locale/de/LC_MESSAGES/django.po`

### Testing
Expand Down
2 changes: 2 additions & 0 deletions backend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
**/__pycache__
run/*
18 changes: 15 additions & 3 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
FROM ubuntu:18.04

RUN apt-get update && apt-get install -y python3 python3-pip libpq-dev gettext
WORKDIR /match4everyone-backend
COPY requirements.txt /match4everyone-backend/requirements.txt

WORKDIR /backend
COPY requirements.txt /backend/requirements.txt
RUN pip3 install -r requirements.txt
COPY requirements.prod.txt /match4everyone-backend/requirements.prod.txt
COPY requirements.prod.txt /backend/requirements.prod.txt
RUN pip3 install -r requirements.prod.txt

COPY . .

RUN python3 manage.py makemessages --no-location --ignore 00_old_m4h_matching_code
RUN python3 manage.py compilemessages

# Change permissions on run/ in case app is later run by non-root user
# and delete log files as these are created during the above commands when django loads the configuration
# and will be created non writeable by others than root
RUN chmod -Rc a+rwX run && rm -v run/*log
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better solution gosu or su-exec, this should be changed to:

  1. ENV variable with uid and gid to be used by container
  2. run container as root (remove user param from yml)
  3. create user and group inside of container with fixed names (e.g. backend)
  4. execute the processes using gosu/su-exec with user from 3


EXPOSE 8000
ENTRYPOINT ["./entrypoint.sh"]
CMD ["python3", "manage.py", "runserver", "0.0.0.0:8000"]
23 changes: 23 additions & 0 deletions backend/apps/matching/management/commands/makemessages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import fileinput
import re

from django.core.management.commands.makemessages import Command as MakeMessageCommand


class Command(MakeMessageCommand):

help = ( # noqa
MakeMessageCommand.help
+ "Modified behaviour: Will reset creation date in all po files to 1900-01-01 00:00+0000"
"to prevent diffs from being created when the only change would be a rerun of makemessages"
)

def build_potfiles(self):
potfiles = super().build_potfiles()
print("Using constant POT-Creation-Date: 1900-01-01 00:00+0000")
pattern = re.compile(r"POT-Creation-Date: .*\\n")
for line in fileinput.input(files=potfiles, inplace=True):
line = pattern.sub(r"POT-Creation-Date: 1900-01-01 00:00+0000\\n", line)
print(line, end="")

return potfiles
22 changes: 22 additions & 0 deletions backend/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -e -o pipefail

if ! [[ -z "${PRODUCTION}" ]]; then
echo Running in production mode, waiting for database to come up
# Moved from docker-compose, as this is needed for migrate
while ! (< /dev/tcp/database/5432) &> /dev/null; do
echo "Waiting for database..."
sleep 1
done
fi

# While collectstatic should rather run in the Dockerfile build
# it depends on the right DJANGO_SETTINGS_MODULE as it is subject to the
# STATICFILES_STORAGE setting. Whitenoise is only configured in production as of now
# so we would need to build the image in production mode, this would require setting a DJANGO_SECRET key
echo PYTHONPATH: $PYTHONPATH
django-admin collectstatic --no-input
django-admin migrate
django-admin check

exec "$@"
11 changes: 7 additions & 4 deletions backend/locale/de/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-06-21 07:40+0000\n"
"POT-Creation-Date: 1900-01-01 00:00+0000\n"
"PO-Revision-Date: 2020-06-21 09:42+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
Expand Down Expand Up @@ -351,7 +351,7 @@ msgid ""
"\n"
" Approve\n"
" "
msgstr ""
msgstr ""
"\n"
" Freischalten\n"
" "
Expand All @@ -361,7 +361,8 @@ msgstr "Diese Teilnehmer müssen noch freigeschaltet werden."

msgid "These participants are approved, but you can disapprove them any time."
msgstr ""
"Diese Teilnehmer sind freigeschaltet, aber Sie können die Freischaltung jederzeit wieder rückgängig machen."
"Diese Teilnehmer sind freigeschaltet, aber Sie können die Freischaltung "
"jederzeit wieder rückgängig machen."

msgid "Staff"
msgstr "Administrator"
Expand Down Expand Up @@ -393,7 +394,9 @@ msgstr "Sie haben den Teilnehmer mit der E-Mail-Adresse '{name}' gelöscht."

#, python-brace-format
msgid "You changed the approval of the participant with email '{name}'."
msgstr "Sie haben für den Teilnehmer mit der E-Mail-Adresse '{name}' den Status geändert."
msgstr ""
"Sie haben für den Teilnehmer mit der E-Mail-Adresse '{name}' den Status "
"geändert."

msgid "Access stats"
msgstr "Website Zugriffe"
Expand Down
2 changes: 1 addition & 1 deletion backup.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
source database.prod.env
docker-compose -f docker-compose.dev.yml -f docker-compose.prod.yml exec backend sh -c 'python3 manage.py dumpdata > /match4everyone-backend/backups/fixture-$(date +%F_%H%M%S).json'
docker-compose -f docker-compose.dev.yml -f docker-compose.prod.yml exec backend sh -c 'python3 manage.py dumpdata > /backend/backups/fixture-$(date +%F_%H%M%S).json'
docker-compose -f docker-compose.dev.yml -f docker-compose.prod.yml exec database sh -c "pg_dumpall -U $POSTGRES_USER> /backups/pg_backup-$(date +%F_%H%M%S).sql"
16 changes: 0 additions & 16 deletions deploy.sh

This file was deleted.

13 changes: 13 additions & 0 deletions docker-compose.override.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: '3.7'

# This file is automatically merged with docker-compose.yml when docker-compose is called without -f parameter
# when invoked using docker-compose -f ... -f ... it is not automatically merged into the configuration
#
# Options declared here only apply for development mode or when explicitly added to the docker-compose call

services:
backend:
volumes:
- './backend:/backend' # bind mount the code directory
env_file:
- backend.dev.env
10 changes: 5 additions & 5 deletions docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:

backend:
volumes:
- './backend:/match4everyone-backend'
- backend-run:/backend/run
ports:
- '80:8000'
networks:
Expand All @@ -14,8 +14,8 @@ services:
- database.prod.env
environment:
- DJANGO_SETTINGS_MODULE=match4everyone.settings.production
user: ${CURRENT_UID:-0}
command: bash -c 'while ! (< /dev/tcp/database/5432) &> /dev/null; do echo "Waiting for database..."; sleep 1; done; gunicorn -c /match4everyone-backend/gunicorn.conf.py match4everyone.wsgi'
- PRODUCTION=True
command: gunicorn -c /backend/gunicorn.conf.py match4everyone.wsgi
depends_on:
- database

Expand All @@ -39,5 +39,5 @@ services:
networks:
network:

#volumes:
# postgres-data:
volumes:
backend-run:
7 changes: 4 additions & 3 deletions docker-compose.dev.yml → docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ version: '3.7'
services:

backend:
container_name: backend
build:
context: ./backend
dockerfile: Dockerfile
volumes:
- './backend:/match4everyone-backend'
ports:
- '8000:8000'
tty: true
environment:
- DJANGO_SETTINGS_MODULE=match4everyone.settings.development
- CI
- TRAVIS
- TRAVIS_PULL_REQUEST_SLUG
- PYTHONPATH=/backend # needed for django-admin to find the module
user: ${BACKEND_USER:-0}
13 changes: 8 additions & 5 deletions scripts/check_website_availability.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ failed=False
URL=localhost:8000

# Log file location
ERR_LOG_PATH=backend/run/match4everyone.json.error.log
ERR_LOG_PATH=./run/match4everyone.json.error.log

# docker-compose to run command in backend
DC_EXEC="docker-compose -f docker-compose.yml -f docker-compose.prod.yml exec backend bash -c "

# Execute command and print status
function test() {
Expand Down Expand Up @@ -61,8 +64,8 @@ function check_website_up() {
function check_error_log_empty() {
# Check if log file exists
for i in $(seq 1 10); do
if [ -f "$ERR_LOG_PATH" ]; then
break
if $DC_EXEC "[ -f \"$ERR_LOG_PATH\" ]"; then
break
else
if [[ i -eq 10 ]]; then
printf "Log file not found\t"
Expand All @@ -74,9 +77,9 @@ function check_error_log_empty() {

curl --silent --output /dev/null localhost:8000
sleep 5
if [ -s "$ERR_LOG_PATH" ]; then
if $DC_EXEC "[ -s \"$ERR_LOG_PATH\" ]"; then
printf "\n"
cat "$ERR_LOG_PATH"
$DC_EXEC "cat \"$ERR_LOG_PATH\""
printf "\n"
return 1
fi
Expand Down
2 changes: 1 addition & 1 deletion scripts/wait_for_backend.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
while ! [ $(docker ps | grep backend | wc -l) -gt 0 ]; do
while [ -z "$(docker-compose -f docker-compose.yml -f docker-compose.prod.yml ps --services --filter "status=running"|grep backend)" ]; do
sleep 1
done
22 changes: 14 additions & 8 deletions scripts/write_envs_to_file.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
#!/usr/bin/env bash

echo "POSTGRES_DB=${POSTGRES_DB}" > database.prod.env
echo "POSTGRES_USER=${POSTGRES_USER}" >> database.prod.env
echo "POSTGRES_PASSWORD=${POSTGRES_PASSWORD}" >> database.prod.env

echo "SECRET_KEY=${SECRET_KEY}" > backend.prod.env
echo "SENDGRID_API_KEY=${SENDGRID_API_KEY}" >> backend.prod.env
echo "RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER}" >> backend.prod.env
echo "RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER}" >> backend.prod.env
DATABASE_ENV_VARS="POSTGRES_DB POSTGRES_USER POSTGRES_PASSWORD"
BACKEND_ENV_VARS="SECRET_KEY SENDGRID_API_KEY SLACK_LOG_WEBHOOK LEAFLET_TILESERVER MAPBOX_TOKEN"

# Write env variables into env file
# This way you can set them in the travis configuration and they
# will be written into the appropriate env files by this script

for ENVVAR in ${DATABASE_ENV_VARS}; do
echo "${ENVVAR}=${!ENVVAR}" >> database.prod.env
done

for ENVVAR in ${BACKEND_ENV_VARS}; do
echo "${ENVVAR}=${!ENVVAR}" >> backend.prod.env
done