How to deploy django with uwsgi and nginx using Ansible play book.

Reading Time : ~ .

Ansible is a scripting language mostly used to automate installing applications, deploying apps etc..

Why Ansible:

You may think why to learn a new language if I can do the same using shell scripts, well that's ok but in my opinion ansible doesn't take much time to learn and shell scripts could become complex as the projects scale up. You can read further details over here(https://news.ycombinator.com/item?id=6431552).

Before we start it's important that you have an overview of uwsgi and Nginx configurations to deploy a project. You can look over this(http://uwsgi-docs.readthedocs.org/en/latest/tutorials/Django_and_nginx.html) beautiful and simple docs.

 

Lets start:

1. Install ansible:

Recomended: pip install ansible

Archlinux: pacman -S ansible
  Note: archlinux uses python3 as default so you need to create a file /etc/ansible/group_vars/all and paste:
        ---
        
ansible_python_interpreter: /usr/bin/python2
Ubuntu
sudo apt-add-repository ppa:ansible/ansible
sudo apt-get update
sudo apt-get install ansible

 

2. Check if it's working:

Create /etc/ansible/hosts file with

[server1]
your_server_ip_address_here

and execute: ansible all -m ping
you will get a response as pong. The above command executes ping over all servers to specify any group(specific servers) then execute: ansible server1 -m ping

 

3. Example ansible playbook.

Note: Here I assume Ubuntu 14.04 server and ssh permission to it.

Let's install vim over the server.
First provide the IP address in /etc/ansible/hosts file

[my_server]
my_server_ip_address

Now create a task file. Ansible uses yml language.
create script.yml and paste the following

---
- name: install vim package
  apt: name=vim state=latest force=yes

Now create a playbook which runs above created role over specified hosts:
create playbook.yml with the following

---
- hosts: my_server
  remote_user: root
  roles:
    - script

 

Now run:
ansible-playbook playbook.yml

The above script installs latest vim package with apt-get package manager.

 

4. Update your ubuntu server:

First, before updating we have to create grub file as some packages require it. So create update_ubuntu.yml file with

---
- name: check grub file  # task name
  shell: test -f  /boot/grub/menu.lst  # shell module is no different than executing a command in shell eg: /bin/sh
  register: grub_file  # store the result of a shell command in grub_file
  failed_when: False  # silently fail when there are errors
- name: update grub
  command: "sudo update-grub -y"  # different from shell module as this does not have access to variables like $HOME etc..
  environment:
    DEBIAN_FRONTEND: noninteractive  # suppress debconf questions
  when: grub_file.rc != 0
- name: update apt-packages
  apt: "upgrade=dist update_cache=yes"  # uses apt module to upgrade distribution

Now as before creating a playbook with required configs:
In playbook.yml:

---
- hosts: my_server
  remote_user: root
  roles:
    - update_ubuntu

 

5. Create Django project:

Create file django_project.yml and paste: 
Note: project_name is provided in the playbook.yml file.

---
- name: Install required apt-packages
  apt: "name='{{ item }}' state=present force=yes"  # this is like looping through all the items provided and install each respectively.
  with_items:
    - python-dev
    - python-virtualenv
    - python-pip
    - openjdk-7-jre-headless
    - libjpeg8-dev
    - zlib1g-dev
    - libfreetype6-dev
    - liblcms2-dev
    - libwebp-dev
    - libtiff-dev
    - tcl-dev
    - tk-dev
    - python-tk
    - redis-server
    - git
    - ruby
- name: Creates directory
  file: "path=/home/{{project_name}} state=directory"  # similar to mkdir this creates directory given by project_name
- name: create virtualenv and install django in it
  pip: "name=django virtualenv=/home/{{project_name}}/env virtualenv_command=virtualenv"
- name: install a django project
  command: "/home/{{project_name}}/env/bin/django-admin.py startproject {{project_name}} chdir=/home/{{project_name}}/"

Now create playbook.yml file:

---
- hosts: my_server
  remote_user: root
  roles:
    - { role: django_project, project_name: 'mysite' }

run ansible-playbook playbook.yml
That's it, your django project is created.

 

6. Create templates for uwsgi, Nginx and supervisor

The supervisor is just a process control system like it starts or stops an instance. Here we control uwsgi service using su

Things that we need here are the configuration files, ansible uses jinja2 template for configurations.
First create uwsgi template:
create uwsgi.j2 file and paste:

[uwsgi]
chdir           = /home/{{project_name}}/{{project_name}}/
module          = {{project_name}}.wsgi
home            = /home/{{project_name}}/env
master          = true
processes       = 4
socket          = /home/{{project_name}}/{{project_name}}.sock
chmod-socket    = 666
vacuum          = true
harakiri        = 60
max-requests    = 5000

For running uwsgi in emperor mode we use supervisor.
Create configuration for supervisor, so create uwsgi_server.j2 jinja2 template and paste:

[program:uwsgi-runner]
stdout_logfile = /var/log/supervisor/uwsgi-runner-out.log
stderr_logfile = /var/log/supervisor/uwsgi-runner-err.log
logfile_maxbytes = 50MB
logfile_backups = 10
command = /usr/local/bin/uwsgi --emperor /etc/uwsgi/vassals

Now create nginx templates:
In nginx.j2 paste:

upstream {{project_name}} {
    server unix:///home/{{project_name}}/{{project_name}}.sock;
}
server {
    listen       {{ server_port }};
    server_name  {{ server_name }};
    charset     utf-8;
    client_max_body_size 75M;
    error_log {{nginx_error_log}};
    location / {
        uwsgi_pass  {{project_name}};
        include     uwsgi_params;
    }
    location /static {
        alias /home/{{project_name}}/{{project_name}}/static;
        expires 365d;
    }

    location /media {
        alias /home/{{project_name}}/{{project_name}}/media;
        expires 365d;
    }
    location /player/private/media/ {
       alias /home/{{project_name}}/{{project_name}}/media/;
    }
location /robots.txt { root /home/{{project_name}}/{{project_name}}/static/; }
location /favicon.ico { root /home/{{project_name}}/{{project_name}}/static/; }
}

In step 7 we create roles using these templates.

7. uWSGI and Nginx roles:

Create uwsgi_django_setup.yml file and include:

---
- name: Install python-dev
  apt: "name='python-dev' state=present force=yes"
- name: Install python-pip
  apt: "name='python-pip' state=present force=yes"
- name: install uwsgi
  pip: name=uwsgi
- name: Install supervisor
  apt: name=supervisor state=present force=yes
  when: is_installed.rc != 0
- name: Create UWSGI vassals directory
  file: "path={{vassals_dir}}  state=directory"
- name: setup uwsgi in supervisor
  template: "src=uwsgi-server.j2 dest={{supervisor_conf_dir}}/uwsgi-runner.conf"
- name: update supervisor
  supervisorctl: name=uwsgi-runner state=restarted
- name: setup project uwsgi configuration file
  template: "src=uwsgi.j2 dest=/home/{{project_name}}/{{project_name}}_uwsgi.ini"
- name: create a symlink of uwsgi in vassals
  file: "src=/home/{{project_name}}/{{project_name}}_uwsgi.ini dest=/etc/uwsgi/vassals/{{project_name}}_uwsgi.ini state=link"
- name: touch the symlink file
  command: "touch /etc/uwsgi/vassals/{{project_name}}_uwsgi.ini"

 

Nginx Django setup role:
create file nginx_django_setup.yml and paste:

---
- name: Install nginx

  apt: "name='nginx' state=present force=yes"
- name: copy nginx config of project
  template: "src=nginx.j2 dest={{nginx_conf_dir}}/{{project_name}}.conf"
- name: copy nginx conf from sites-available to sites-enabled
  file: "src={{nginx_conf_dir}}/{{project_name}}.conf dest={{nginx_sites_dir}}/{{project_name}}.conf state=link"
- name: create project log directory
  file: "path=/home/{{project_name}}/logs state=directory"
- name: start nginx
  service: "name=nginx state=started"

Finally, create a playbook.yml:

---
- hosts: my_server
  remote_user: root
  roles:
      - { role: uwsgi_django_setup, project_name: 'mysite'}
      - { role: nginx_django_setup, project_name: 'mysite' }

Run ansible-playbook playbook.yml, your project will be deployed.

 

Note: We have hosted these scripts over ansible galaxy. You can pull using:
ansible-galaxy pull micropyramid.django

 

 

 

 

 

    By Posted On
SENIOR DEVELOPER at MICROPYRAMID

Need any Help in your Project?Let's Talk

Latest Comments
Related Articles
Daemonizing any command with SUPERVISOR Ashwin Kumar

Daemonizing a command means to make it run as a background process.

so when we have a command that we want to daemonize there are ...

Continue Reading...
Elastic Search Security Measures Jagadeesh V

After Installing Elastic Search in production, many new developers leave it unconfigured. In this Post, we will look into important config file changes and its ...

Continue Reading...
Continuous Integration And Continuous Delivery With Gitlab And Docker Jagadeesh V

In this blog post, we will learn how to setup and use gitlab container registry to implement, test build and deploy your code automatically with ...

Continue Reading...

Subscribe To our news letter

Subscribe to our news letter to receive latest blog posts into your inbox. Please fill your email address in the below form.
*We don't provide your email contact details to any third parties