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 all 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
kevihiiin marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# VS Code
.vscode
.venv
*.code-workspace

# Production environment variables
.env
Expand Down Expand Up @@ -193,9 +194,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
10 changes: 4 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@ 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 logs
- 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|"
65 changes: 35 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,56 @@ 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
Baschdl marked this conversation as resolved.
Show resolved Hide resolved

## 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.

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.
## Reverse Proxy

We recommend running the gunicorn server behind a reverse proxy to provide ssl and possibly run multiple services on one server.
The default configuration will make the docker container reachable on port 8000 only on 127.0.0.1.

A sample nginx configuration can be found at ./tools/nginx-sample-site.

## Setup
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 `docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build` 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
maltezacharias marked this conversation as resolved.
Show resolved Hide resolved
(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 +91,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
5 changes: 5 additions & 0 deletions backend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
**/__pycache__
**/.gitkeep
**/.gitignore
run/*
!run/log
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
RUN python3 manage.py compilemessages
RUN python3 manage.py collectstatic --no-input
# 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 rm -v run/log/* && chmod a+rwX run/log && chmod a+rwX backups

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
19 changes: 19 additions & 0 deletions backend/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/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
echo -n "Waiting for database"
while ! (< /dev/tcp/database/5432) &> /dev/null; do
echo -n .
sleep .2
done
echo " OK"
fi

echo PYTHONPATH: $PYTHONPATH
django-admin migrate
kevihiiin marked this conversation as resolved.
Show resolved Hide resolved
django-admin check

exec "$@"
2 changes: 1 addition & 1 deletion backend/gunicorn.conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
worker_class = "gevent"
worker_connections = 1000

accesslog = os.path.join(settings.RUN_DIR, "gunicorn-access.log")
accesslog = os.path.join(settings.LOG_DIR, "gunicorn-access.log")
access_log_format = '%(t)s|"%(r)s"|%(s)s|%(T)s'
2 changes: 1 addition & 1 deletion 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-30 11:18+0200\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
12 changes: 6 additions & 6 deletions backend/match4everyone/settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# add paths here and import: from django.col import settings and use settings.XXX_DIR
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
RUN_DIR = os.path.join(BASE_DIR, "run")

LOG_DIR = os.path.join(RUN_DIR, "log")

# Application definition

Expand Down Expand Up @@ -57,6 +57,7 @@
]

MIDDLEWARE = [
"whitenoise.middleware.WhiteNoiseMiddleware",
# 'cms.middleware.utils.ApphookReloadMiddleware' TODO: Not working right now "ModuleNotFoundError: No module named 'cms.middleware.utils.ApphookReloadMiddlewaredjango'; 'cms.middleware.utils' is not a package"
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
Expand Down Expand Up @@ -159,16 +160,15 @@
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
PROJECT_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
STATIC_URL = "/static/"

MEDIA_ROOT = os.path.join(RUN_DIR, "media")

MEDIA_URL = "/media/"
# TODO: Serve media files properly (http://docs.django-cms.org/en/latest/how_to/install.html#media-and-static-file-handling)

STATIC_URL = "/static/"
STATIC_ROOT = os.path.join(RUN_DIR, "static")

STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"

LEAFLET_TILESERVER = os.getenv(
"LEAFLET_TILESERVER"
Expand Down Expand Up @@ -214,15 +214,15 @@
"class": "logging.handlers.RotatingFileHandler",
"formatter": "json",
"level": "ERROR",
"filename": path.join(RUN_DIR, "match4everyone.json.error.log"),
"filename": path.join(LOG_DIR, "match4everyone.json.error.log"),
"maxBytes": 1024 * 1024 * 15, # 15MB
"backupCount": 10,
},
"auditlogfile": {
"class": "logging.handlers.RotatingFileHandler",
"formatter": "json",
"level": "INFO",
"filename": path.join(RUN_DIR, "match4everyone.json.audit.log"),
"filename": path.join(LOG_DIR, "match4everyone.json.audit.log"),
"maxBytes": 1024 * 1024 * 15, # 15MB
"backupCount": 10,
},
Expand Down
5 changes: 1 addition & 4 deletions backend/match4everyone/settings/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from match4everyone.constants.enum import Environment
from match4everyone.settings.common import * # noqa
from match4everyone.settings.common import IS_FORK, MIDDLEWARE, RUN_DIR
from match4everyone.settings.common import IS_FORK, RUN_DIR

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -41,9 +41,6 @@
}
}

MIDDLEWARE.insert(1, "whitenoise.middleware.WhiteNoiseMiddleware")

STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"

# =============== MAIL RELAY SERVER CONFIGURATION ===============
# TODO: add environment variable based detection whether we are on prod or staging # noqa: T003
Expand Down
2 changes: 0 additions & 2 deletions backend/requirements.prod.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,3 @@ gunicorn==20.0.4
psutil==5.7.0
psycopg2==2.8.5
psycopg2==2.8.5
whitenoise==5.0.1
whitenoise[brotli]
1 change: 1 addition & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ sendgrid==6.2.0
six==1.15.0
tqdm==4.46.1
whitenoise==5.0.1
whitenoise[brotli]
9 changes: 7 additions & 2 deletions backend/run/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@

# Except for the static folder
!static
!static/.gitkeep
# But its content
static/*
!static/.gitkeep

# Except for the media folder
!media
!media/.gitkeep
# But its content
media/*
!media/.gitkeep

!log
log/*
!log/.gitkeep
Empty file added backend/run/log/.gitkeep
Empty file.
23 changes: 20 additions & 3 deletions backup.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
#!/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 database sh -c "pg_dumpall -U $POSTGRES_USER> /backups/pg_backup-$(date +%F_%H%M%S).sql"
# This backup script will create a database dump from the postgres container
# the created backup will then be moved to the current directory
set -o errexit

WORKINGDIR=$(pwd)

echo "Creating PostgreSQL Dump"
docker-compose -f docker-compose.yml -f docker-compose.prod.yml exec database sh -c 'pg_dumpall -U $POSTGRES_USER> /backups/pg_backup-$(date +%F_%H%M%S).sql'

# Create a throwaway container (--rm removes after running the command) to move the backups from the DB-container volume to the local directory for further processing
# (Mounts the current working dir as /backup-bind-mount inside the container and moves the backups there)
echo "Moving the backups to $WORKINGDIR"
DB_CONTAINER="$(docker-compose -f docker-compose.yml -f docker-compose.prod.yml ps -q database)"
docker run --rm --volumes-from "$DB_CONTAINER" -v "${WORKINGDIR}:/host" alpine sh -c "mv -v backups/* /host/database/backups"

echo -e "\nCreating Django fixtures"
docker-compose -f docker-compose.yml -f docker-compose.prod.yml exec backend sh -c 'django-admin dumpdata > /backend/backups/fixture-$(date +%F_%H%M%S).json'
echo "Moving the backups to $WORKINGDIR"
BACKEND_CONTAINER="$(docker-compose -f docker-compose.yml -f docker-compose.prod.yml ps -q backend)"
docker run --rm --volumes-from "$BACKEND_CONTAINER" -v "${WORKINGDIR}:/host" alpine sh -c "mv -v /backend/backups/*.json /host/backend/backups"
3 changes: 2 additions & 1 deletion database/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
# Ignore database files
data/*
# Ignore backups
backups/
backups/*
!backups/.gitkeep
3 changes: 2 additions & 1 deletion database/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
FROM postgres
FROM postgres:12

COPY init-db.sh /docker-entrypoint-initdb.d/
COPY postgresql.conf /etc/postgresql.conf
Empty file added database/backups/.gitkeep
Empty file.
2 changes: 1 addition & 1 deletion database/postgresql.conf
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ min_wal_size = 80MB
#primary_slot_name = '' # replication slot on sending server
# (change requires restart)
#promote_trigger_file = '' # file name whose presence ends recovery
#hot_standby = on # "off" disallows queries during recovery
hot_standby = off # "off" disallows queries during recovery
# (change requires restart)
#max_standby_archive_delay = 30s # max delay before canceling queries
# when reading WAL from archive;
Expand Down
16 changes: 0 additions & 16 deletions deploy.sh

This file was deleted.

Loading