Skip to content

How To Deploy

Kiarash Golezardi edited this page Feb 6, 2019 · 1 revision

We assume that the server user is set up based on the previous wiki page.

Update Packages

$ sudo apt-get update
$ sudo apt-get upgrade

Install and Create Virtualenv

$ sudo apt-get install python-virtualenv
$ export LC_ALL="en_US.UTF-8"
$ export LC_CTYPE="en_US.UTF-8"
$ sudo dpkg-reconfigure locales
$ virtualenv ~/bidiloenv -p python3

Install RabbitMQ

$ sudo apt-get install rabbitmq-server

Install and Configure PostgreSQL

$ sudo apt-get install libpq-dev python3-dev
$ sudo apt-get install postgresql postgresql-contrib

$ sudo -u postgres psql
postgres=# CREATE DATABASE bidilo;
postgres=# CREATE USER bidilouser WITH PASSWORD 'PASSWD';
postgres=# ALTER ROLE bidilouser SET client_encoding TO 'utf8';
postgres=# ALTER ROLE bidilouser SET default_transaction_isolation TO 'read committed';
postgres=# ALTER ROLE bidilouser SET timezone TO 'Asia/Tehran';
postgres=# GRANT ALL PRIVILEGES ON DATABASE bidilo TO bidilouser;
postgres=# \q

Change Django Configurations

The requirements should now be composed of these files:

requirements/
    base.py
    development.py
    production.py

The we add gunicorn and psycopg2-binary to the production requirements.

We then add the following at the end of settings.py:

try:
    from .local_settings import *
except ImportError:
    pass

And in last, we create a local_settings.py for the project:

SECRET_KEY = 'i#%ew3j&g9fg*p++n$5=f*=ko)2=2=m-e)(7fl@pjg$c=wb=w%'

DEBUG = False

ALLOWED_HOSTS = ['IP', 'localhost']

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'bidilo',
        'USER': 'bidilouser',
        'PASSWORD': 'PASSWD',
        'HOST': 'localhost',
        'PORT': '',
    }
}

Now we are ready to clone the project:

$ git clone https://github.com/kgolezardi/SAD-Project.git
$ source ~/bidiloenv/bin/activate
$ cd SAD-Project/bidilo
$ pip install -r requirements/production.txt
$ # Change local settings
$ ./manage.py collectstatic
$ ./manage.py migrate
$ ./manage.py createsuperuser
$ sudo ufw allow 8123
$ # We can now test the server with django runserver or gunicorn

Creating systemd Socket and Service Files for Gunicorn

$ sudo vim /etc/systemd/system/gunicorn.socket:

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

sudo vim /etc/systemd/system/gunicorn.service:

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=bidilo
Group=www-data
WorkingDirectory=/home/bidilo/SAD-Project/bidilo
ExecStart=/home/bidilo/bidiloenv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          bidilo.wsgi:application

[Install]
WantedBy=multi-user.target

Now we have created the systemd socket and service file for gunicorn. We can now start and enable the Gunicorn socket. This will create the socket file at /run/gunicorn.sock now and at boot. When a connection is made to that socket, systemd will automatically start the gunicorn.service to handle it:

$ sudo systemctl start gunicorn.socket
$ sudo systemctl enable gunicorn.socket

Install and Configure NGINX

Installation is typically done by sudo apt-get install nginx.

$ sudo vim /etc/nginx/sites-available/bidilo:

server {
    listen 80;
    server_name IP;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/bidilo/SAD-Project/bidilo;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

Now, we can enable the file by linking it to the sites-enabled directory:

$ sudo ln -s /etc/nginx/sites-available/bidilo /etc/nginx/sites-enabled

Test your Nginx configuration for syntax errors by typing:

$ sudo nginx -t

Restart Nginx by typing sudo systemctl restart nginx.

We can now remove out previous firewall rule: sudo ufw delete allow 8000 and a new one just for NGINX:

$ sudo ufw allow 'Nginx Full'

The server is now ready to serve!

Daemonization of Celery

$ sudo vim /etc/systemd/system/celery.service:

[Unit]
Description=Celery Service
After=network.target

[Service]
Type=forking
User=bidilo
Group=www-data
EnvironmentFile=/etc/conf.d/celery
WorkingDirectory=/home/bidilo/SAD-Project/bidilo
ExecStart=/bin/sh -c '${CELERY_BIN} multi start ${CELERYD_NODES} \
  -A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} \
  --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS}'
ExecStop=/bin/sh -c '${CELERY_BIN} multi stopwait ${CELERYD_NODES} \
  --pidfile=${CELERYD_PID_FILE}'
ExecReload=/bin/sh -c '${CELERY_BIN} multi restart ${CELERYD_NODES} \
  -A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} \
  --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS}'

[Install]
WantedBy=multi-user.target

$ sudo vim /etc/conf.d/celery:

# Name of nodes to start
# here we have a single node
CELERYD_NODES="w1"
# or we could have three nodes:
#CELERYD_NODES="w1 w2 w3"

# Absolute or relative path to the 'celery' command:
CELERY_BIN="/home/bidilo/bidiloenv/bin/celery"

# App instance to use
# comment out this line if you don't use an app
CELERY_APP="bidilo"
# or fully qualified:
#CELERY_APP="proj.tasks:app"

# How to call manage.py
CELERYD_MULTI="multi"

# Extra command-line arguments to the worker
CELERYD_OPTS="--time-limit=300 --concurrency=8"

# - %n will be replaced with the first part of the nodename.
# - %I will be replaced with the current child process index
#   and is important when using the prefork pool to avoid race conditions.
CELERYD_PID_FILE="/var/run/celery/%n.pid"
CELERYD_LOG_FILE="/var/log/celery/%n%I.log"
CELERYD_LOG_LEVEL="INFO"

Now we should create the directories for our logs:

$ sudo mkdir /var/log/celery
$ sudo chown bidilo /var/log/celery
$ sudo chgrp www-data /var/log/celery
$ sudo mkdir /var/run/celery
$ sudo chown bidilo /var/run/celery
$ sudo chgrp www-data /var/run/celery

Then we run these commands to start celery daemon:

$ sudo systemctl daemon-reload
$ sudo systemctl restart celery

Notes

If some changes were made to the Django code, you should restart Gunicorn socket by:

$ sudo systemctl restart gunicorn.socket

If you make changes to the /etc/systemd/system/gunicorn.service file, reload the daemon to reread the service definition and restart the Gunicorn process by typing:

$ sudo systemctl daemon-reload
$ sudo systemctl restart gunicorn

This also gets the job done after changing Django code.

Most of this wiki has came to life with the help of this DigitalOcean page.

Also this is a similar document.

Clone this wiki locally