Innovate anywhere, anytime withruncode.io Your cloud-based dev studio.
Server Management

How to Deploy Django with Uwsgi and Nginx using Ansible Play Book

2022-07-21

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