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.
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:
- application code (stored in sitelib)
- 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.
Last updated: November 2, 2023