Deploying a Django application¶
This chapter tells the basics of deploying a django application using gunicorn, nginx and supervisord.
Prerequisites¶
Knowledge¶
- Django basics
- (Optional) finish the book about django-rest-apis .
Resources¶
A Unix server for deploying the app , connected with a SSH(preferred).
Django is a free and open source web application framework, written in Python. Django has a lot of inbuilt set of components that helps you to develop websites faster and easier.
Gunicorn is a simple, light-weight Python WSGI HTTP Server for UNIX. WSGI is the Web Server Gateway Interface. It is a specification that describes how a web server communicates with web applications, and how web applications can be chained together to process one request.
Nginx is a high-performance HTTP server, reverse proxy, load balancer and static files loader.
Supervisord is a process-control system which allows us to monitor and control a number of processes on UNIX operating system.
Let’s start with our server¶
Once we create our server and let’s login to the server via SSH,
$ ssh root@IP_ADDRESS_OF_SERVER
Now we have to install the prerequisites, run these commands
$ sudo apt-get update
$ sudo apt-get install git python-pip python-dev virtualenv virtualenvwrapper
$ sudo apt-get install postgresql postgresql-contrib
$ pip install --upgrade pip
Now let’s configure the virtual-env wrapper
After setting-up the virtualenvwrapper, create a virtualenv
$ mkvirtualenv env-name
From within our virtual-env, install:
(env-name) $ pip install django gunicorn psycopg2
Let’s clone the repo in home folder, pull the application from Git, we use this repo https://github.com/anmolakhilesh/django-polls-rest
$ cd ~
$ git clone https://github.com/anmolakhilesh/django-polls-rest
Now we have to add permissions to the manage.py
file
$ cd /django-polls-rest/
$ chmod 755 manage.py
Now install the requirements
(env-name) $ pip install -r requirements.txt
Now set up PostgreSQL
Create a file .env
and add these lines in that
$ export POSTGRES_DB = pollsdb
$ export POSTGRES_USER = polls_admin
$ export POSTGRES_PASSWORD = polls_password
$ export POLLSAPI_PG_HOST = 127.0.0.1
Create a postgres Database
$ sudo -u postgres psql
After running the above command, we will be logged inside PostgreSQL terminal, now lets create our db and user
> CREATE DATABASE pollsdb;
> CREATE USER polls_admin WITH PASSWORD 'polls_password';
> ALTER ROLE polls_admin SET client_encoding TO 'utf8';
> ALTER ROLE polls_admin SET default_transaction_isolation TO 'read committed';
> ALTER ROLE polls_admin SET timezone TO 'UTC';
> ALTER USER polls_admin CREATEDB;
> GRANT ALL PRIVILEGES ON DATABASE pollsdb TO polls_admin;
> \q # to quit the shell
Make sure that these details match the details in the .env
file. Exit the PostgreSQL shell by typing \q
.
Now as the DB is ready , we can run migrations command inside the repo folder.
# migrations
(env-name) $ python manage.py migrate
# Create a supervisor, let's
(env-name) $ python manage.py createsuperuser
Now postgres-db is setted, now we have to set up the server
Using gunicorn¶
(env-name) $ pip install gunicorn
After installing gunicorn , now run it
# starts the server
(env-name) $ gunicorn polls_rest.wsgi
It will run the app , we can check IP_ADDRESS_OF_SERVER:8000
, IP_ADDRESS_OF_SERVER:8000/admin
.
It will not have any css , as the gunicorn only serves the application. We will be serving static files using nginx .
To exit it press Ctrl+C
.
# starts the server by binding it to a specific port
(env-name) $ gunicorn --bind 0.0.0.0:8888 polls_rest.wsgi
# running with a config file
(env-name) $ gunicorn -c /path/to/config/file polls_rest.wsgi
# running in daemon mode
(env-name) $ gunicorn --daemon polls_rest.wsgi
If it is in daemon-mode, then exit it with pkill gunicorn
, which will kill the gunicorn process.
To have a gunicorn config file for gunicorn , we write the config file in a .py
.
Using nginx¶
By using gunicorn, we were able to run the application, but without styles as the gunicorn only runs the application and does not serve the static files django does not serve static file except in development.
We will use nginx
to serve the static files , nginx will first get the request, and it will send it to gunicorn.
To install nginx
$ sudo apt-get install nginx
let’s configure nginx
So, create a file /etc/nginx/sites-available/pollsapp
and add the following
server {
listen 80; #L1
server_name SERVER_DOMAIN_OR_IP_ADDRESS_OF_SERVER; #L2
location = /favicon.ico { access_log off; log_not_found off; } #L3
location /static/ { #L4
root /home/django-polls-rest;
}
location / { #l5
include proxy_params;
proxy_pass http://unix:/home/django-polls-rest/polls_rest.sock;
}
}
- #L1 and #L2 lines defines where our nginx server should run.
- #L3 line ignores any errors related to the favicon.
- #L4 block
location /static/
defines the location of static files. - #L5 block
location /
tells the socket(gunicorn socket) to communicate.
After this, we have to enable this config file by linking with the sites-enabled
folder.
$ ln -s /etc/nginx/sites-available/pollsapp /etc/nginx/sites-enabled
We link the above file to sites-enabled
, so that it will be included in the main nginx settings file /etc/nginx/nginx.conf
After enabling the config file , we can check nginx configuration by
$ sudo nginx -t
If the configuration file is correct , then we should see this
Now we have to mention the static files directory of our app in settings.py
file . So add this line in settings.py
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
After adding this line, we have to perform run collectstatic
command
(env-name) $ python manage.py collectstatic
Let’s run the app
(env-name) $ gunicorn --daemon --workers 3 --bind unix:/home/django-polls-rest/polls_rest.sock polls_rest.wsgi
The /home/django-polls-rest/polls_rest.sock
file is a unix-socket file which will be created automatically.
And this file will enable Gunicorn and Nginx to communicate with each other.
Now Restart Nginx for changes to take effect.
$ sudo service nginx restart
This will run our app in the http://IP_ADDRESS
Point to remember , checkALLOWED_HOSTS
insettings.py
to have you host name or ip address of server.
Configuring Gunicorn with Supervisord¶
Supervisor is a process monitoring tool, which can restart any process if the process dies or gets killed for some reason.
At present we are manually starting gunicorn in daemon to run our app, Suppose if this gunicorn process closes or gets killed due to some reason then we have to manually start it again. To monitor our processes we use Supervisord, So that supervisor controls the gunicorn process.
To install supervisord
$ sudo apt-get install supervisor
Let’s add a configuration file pollsapi.conf
for our application in /etc/supervisor/conf.d/
folder,
the conf.d
folder will have all our config files.
[program:pollsapi] #L1
directory=/home/django-polls-rest/polls_rest #L2
command=/home/.virtualenvs/demo-polls-1/bin/gunicorn --workers 3 --bind unix:/home/django-polls-rest/polls_rest.sock polls_rest.wsgi #L3
autostart=true #L4
autorestart=true #L5
stderr_logfile=/var/log/pollsapi.err.log #L6
stdout_logfile=/var/log/pollsapi.out.log #L7
Let’s understand the config file we have written,
- #L1 line
[program:pollsapi]
names the program( or process ) as pollsapi, which can be used as
$ sudo supervisorctl start pollsapi
- #L2 line
directory
is the path to our project. - #L3 line
command
is the command to start our project - #L4 lines
autostart
tells the script to start on system boot. - #L5 line
autorestart
tells the script to restart when it closes for some reason - #L6
stderr_logfile
which will store the error logs & #L7stdout_logfile
will store the non-error logs.
Now lets save this file and update supervisor
$ sudo supervisorctl reread
$ sudo supervisorctl update
$ sudo supervisorctl reload
Check the supervisor status .
$ sudo supervisorctl status
This will show
To check gunicorn processes
$ ps ax | grep gunicorn
This command lists all the processes running with gunicorn
To check if the app is running , let’s do curl
$ curl 0.0.0.0:8000
After configuring gunicorn with supervisor, let’s restart our nginx
$ systemctl restart nginx
Now our app should be running on http://IP_ADDRESS_OF_SERVER