Automated deployment of RPM-packaged django applications with Red Hat Software Collections

Introduction

There has been already two (first and second) guides which covered installing django using Red Hat Software Collections (RHSCL). While very popular, none of them went far enough to show a real world deployment example. This guide will try to provide full guidance together with sample project.

python-django

I chose django 1.7 (as of time writing this guide, it’s still in RC) because in a couple of months, it will be used by many and in a year or so, it will be used widely. I also picked Red Hat Enterprise Linux 7 (RHEL 7) instead of Red Hat Enterprise Linux 6 (RHEL 6), because I feel more comfortable in our new Enterprise Linux release (I will mention differences between 6 and 7 in this guide). As a web server I chose nginx+uWSGI. I feel like this combination is very popular lately (probably because it is so fast! and flexible and easy to configure). For the interpreter I picked python 3. It’s the successor of python 2 basically in every way and since collections have it, why would you not use it?

The application I created is simple django application (it just prints in what environment it is running) and the whole project with all scripts can be found on GitHub. There is a Makefile so you can built it very easily and ansible playbook to deploy it. All you need to do is edit some variables and it should work pretty smoothly. The app itself consists of 2 rpms:

  1. application code (stored in sitelib)
  2. application configuration (nginx, uwsgi, uwsgi unit file and django configuration)

You can easily swap between different configurations with this layout: devel, staging and production.

The app itself is packaged as a collection in its own prefix.

Also, I sort of copied first part of Slavek’s guide, so I won’t describe it as succinctly as he did.

Enough background, let’s start!

Guide

I’m assuming you have set up RHSCL repositories for yum. Let’s install needed packages: python 3, postgres 9 and psycopg2, adapter for postgres:

$ yum install python33 postgresql92 python33-python-psycopg2

We will configure postgres first.

PostgreSQL

We have to initialize the database first:

$ scl enable postgresql92 'postgresql-setup initdb'

And start it:

RHEL 7

$ systemctl start postgresql92-postgresql

RHEL 6

$ service postgresql92-postgresql start

Is it working?

RHEL 7

$ systemctl status postgresql92-postgresql

postgresql92-postgresql.service - PostgreSQL database server
Loaded: loaded (/usr/lib/systemd/system/postgresql92-postgresql.service; disabled)
Active: active (running) since Ut 2014-08-12 13:33:54 BST; 5s ago
Process: 1415 ExecStart=/opt/rh/postgresql92/root/usr/bin/scl-service $POSTGRESQL92_SCLS_ENABLED /opt/rh/postgresql92/root/usr/bin/pg_ctl start -D ${PGDATA} -s -o -p ${PGPORT} -w -t 300 (code=exited, status=0/SUCCESS)
Process: 1405 ExecStartPre=/opt/rh/postgresql92/root/usr/bin/scl-service $POSTGRESQL92_SCLS_ENABLED /opt/rh/postgresql92/root/usr/bin/postgresql-check-db-dir ${PGDATA} (code=exited, status=0/SUCCESS)
Main PID: 1423 (postgres)
CGroup: name=systemd:/system/postgresql92-postgresql.service
├─1423 /opt/rh/postgresql92/root/usr/bin/postgres -D /opt/rh/postgresql92/root/var/lib/pgsql/data -p 5432
├─1424 postgres: logger process
├─1427 postgres: checkpointer process
├─1428 postgres: writer process
├─1429 postgres: wal writer process
├─1430 postgres: autovacuum launcher process
└─1431 postgres: stats collector process

RHEL 6

service postgresql92-postgresql status

We will now create a user role, django, for our application:

$ su - postgres
# you have to enable postgresql92 collection so your shell environment is set up correctly
$ scl enable postgresql92 bash
$ createuser -P django
Enter password for new role:
Enter it again:
$ createdb -O django djangodb

Time to configure pg_hba.conf so we can actually connect to our database with our new django role. By default, postgres is asking the operating system for your username if you try to connect locally using unix socket. Let’s configure postgres to require a password and use it for authentication:

$ vim /opt/rh/postgresql92/root/var/lib/pgsql/data/pg_hba.conf
--- <snip> ---
local all postgres peer
local all all md5

We will proceed now with virtualenv.

virtualenv

We have to enable python33 collection so our virtualenv is correctly hooked to python 3.

$ scl enable python33 bash
# I put my virtualenv to /var/lib/, put yours wherever you want
$ virtualenv --system-site-packages /var/lib/simple-django-project
$ source /var/lib/simple-django-project/bin/activate

Time to install packages from PyPI.

# uwsgi is written in C, so you have to compile it (unless you install binary wheel packages)
$ yum install gcc
$ pip install uwsgi

Lets install nginx webserver now (as of time writing this guide, there is nginx 1.6 in EPEL 7, but let’s just stick with RHSCL rather).

$ yum install nginx14-nginx

We have to enable firewall if we want to access the app remotely:

RHEL 7

$ firewall-cmd --add-service=http
success
$ firewall-cmd --permanent --add-service=http
success

RHEL 6

$ iptables -A INPUT -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT

This part may be done from your workstation (I was deploying from my Fedora box).

Deployment

# mock is tool for building rpms in chroot
$ yum install git mock ansible

We will clone the project and build it:

$ git clone https://github.com/TomasTomecek/simple-django-project.git
$ cd simple-django-project

But first, we need to update configuration files for mock (just write URLs of yum repos of RHEL 7, RHEL 7 optional and RHSCL)

$ vim rhscl-python3.cfg
$ vim rhscl-python3-sdp.cfg
$ sudo cp rhscl-python3.cfg rhscl-python3-sdp.cfg /etc/mock/

Firing build…

$ make

…if still not there, ansible is not configured…

$ vim /etc/ansible/hosts

# rhel7-sdp is target in ansible playbook, update your credentials accordingly
rhel7-sdp ansible_ssh_user=root ansible_ssh_host=192.168.122.62

And deploy!

$ ansible-playbook ansible.yml

One command deployments. It can’t be easier.

Let’s get back to the server.

Configuration for nginx is stored in our collection, we have to configure nginx to load our file:

$ vim /opt/rh/nginx14/root/etc/nginx/nginx.conf
--- <snip> ---
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /opt/rh/nginx14/root/etc/nginx/conf.d/*.conf;
include /opt/example_provider/sdp/root/etc/simple-django-project/nginx/*.conf;

And finally…

RHEL 7

$ systemctl start nginx14-nginx uwsgi

RHEL 6

$ service nginx14-nginx start
$ uwsgi --ini /opt/example_provider/sdp/root/etc/simple-django-project/uwsgi/uwsgi.ini

Hopefully, everything is set up correctly. We may proceed with writing schema to the database. syncdb is deprecated in django 1.7, we’ll use migrate instead:

$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, sessions, auth, contenttypes
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying sessions.0001_initial... OK

It didn’t ask us to create a superuser. Weird. Lets create it:

$ python manage.py createsuperuser

Grand finale!

$ yum install elinks
$ elinks -dump http://localhost

* Python version: 3.3.2 (default, Mar 20 2014, 18:55:17) [GCC 4.8.20140120 (Red Hat 4.8.2-16)]
* django version: 1.7c3
* django path:
/var/lib/simple-django-project/lib/python3.3/site-packages/django
* kernel: 3.10.0-123.el7.x86_64
* distribution: ('Red Hat Enterprise Linux Server', '7.0', 'Maipo')

And that’s it! You have your django application packaged as an RPM (so you can easily manage it) and deployed in python 3. Aren’t collections just wonderful?

I would like to thank the python maintenance team (especially Slavek) for helping me with writing this blog post.


Join the Red Hat Developer Program (it’s free) and get access to related cheat sheets, books, and product downloads.

Take advantage of your Red Hat Developers membership and download RHEL today at no cost.

Share
  • Calvin Locklear

    This blog post is awesome. Thanks for posting.