Continuous Delivery for Django projects with Bitbucket Pipelines and Ansible


This post was written by Bitbucket user Augusto Destrero


Introduction

Ansible is a powerful tool for the automation of your server administration. It can be used to install and configure the software needed for your application and also to deploy a new version of your application and make the required steps to make it available.

Bitbucket Pipelines are basically Docker containers, hosted in the Bitbucket infrastructure, that you can launch automatically to build or deploy your code. They can be attached to “events” happening in your repository, usually a push on one branch.

These tools are complementary to each other, because on Ansible you can define exactly what you want to do on your target server to deploy your application, while on Bitbucket Pipelines you can configure and launch a container to perform the actual deploy.

On my blog I described in more detail how to deploy a Django project using Ansible. Here I explain how to take a step further and deploy a Django project automatically when some code is pushed on the master branch of a Bitbucket repository, leveraging Bitbucket Pipelines and Ansible.

The use-cases for deploying a Django project automatically are many. Even if your Django project has a simple architecture, there are many steps you have to perform in the right order to complete a successful deploy in the general case. As a bare minimum you'll have to: update the application code, install or update project dependencies, migrate the database, collect static files, restart the application server.

Automating all these steps in a reproducible and formal way will save you a lot of time in the long run and will help you to avoid human errors. Think about how many times you'll deploy a new version of your application in the future.

If your architecture is more complex, the number of steps to perform will be larger and the deploy process will be more error-prone, so the benefits of an automated deploy will be even greater.

Let the automation begin!

You can clone or fork this Bitbucket repository to follow along with this tutorial.

First thing first, add a file named bitbucket-pipelines.yml to the root of your repository. The content of the file should be similar to this:

# use the official Python 2.7.16 docker image
image: python:2.7.16

pipelines:
  branches:
    # deploy only when pushing to the master branch
    master:
      - step:
          name: Deploy to prod
          # this is the name of the Bitbucket Deployment
          deployment: Production
          caches:
            # cache the Ansible installation
            - pip
          script:
            # install Ansible
            - pip install ansible==2.8.2
            # go into the ansible directory
            # in this same repository
            - cd ansible
            # perform the actual deploy
            - ansible-playbook -i ./hosts deploy.yaml

Let’s examine the configuration line by line:

  • On line 2 you define the Docker image you want to use;
  • On line 7 you define the branch that will trigger the deploy (master in this example);
  • On line 11 you define the deployment, that is basically an environment (managed by Bitbucket) where you can define some variables. In this way you can have different deployments (staging, production, …) with different variables that you can use in your build/deploy steps. In this example I do not use this feature, and I merely use one of the default Bitbucket deployments (named Production) without configuring any variable in this environment;
  • On line 14 you are asking Bitbucket to cache the installation of packages installed by pip. This is useful to avoid to reinstall every time the Python libraries you need for your deployment (Ansible 2.8.0 in this example) from scratch. Having the libraries already installed save you some build/deploy time, which is important because the build time for Bitbucket Pipelines is limited to 50 minutes/month in the free plan.
  • From line 16 to line 22 you define the actual steps for deployment: install Ansible (if not already cached), go into the ansible directory, make the deploy. 

The actual deploy is made by Ansible pulling the master branch from your Git repository, installing all needed production requirements, running Django migrate and collectstatic commands and finally restarting the application server uwsgi.
Please check the Ansible playbook ansible/deploy.yaml to see what it will actually do on the target server in details.

Commit and push the bitbucket-pipelines.yml file on the root of your Bitbucket repository.

Now go on your repository settings and click Settings under the section PIPELINES:

Now click on Enable Pipelines:

If you have read my post on how to deploy a Django project using Ansible, you should know that Ansible work through an SSH connection made between the client host (the Bitbucket Pipeline Docker container in this case) and the target server where you want to deploy your code.

For this SSH connection to work you have to setup an SSH public/private keypair on the Bitbucket Pipeline, and allow the public key on your server authorized_keys.

Go on the settings of your repository, under PIPELINES navigate to SSH keys:

Now click on “Generate keys” to let Bitbucket generate a new SSH keypair for you.

Now you can copy the generated public key and paste it in the file .ssh/authorized_keys file of your deploy target server.

This file should be copied in the .ssh sub-directory under the home directory of the user that will be used by Ansible to perform the deploy task, usually the root user of the target server.

You can refer to the Bitbucket documentation about SSH keys, if you need more details.

You can test that the deploy is working simply by pushing some changes to the master branch of the repository

If you want to push some changes on master, but you do not want to deploy the change automatically on the server, you can put the string [skip-ci] in your commit message.

Conclusion

In this tutorial I explained a simple yet powerful setup for the continuous delivery of a Django project. This is only a little example of what you can do with Bitbucket Pipelines and Ansible to automate your workflow, but I hope that this tutorial helped you to understand the great potential of these technologies.

I think that learning and implementing this kind of automation in your projects is always worth the effort for the time saved in the long run and the less probability of human errors in the deploy process.


Author Bio: Augusto is a freelance developer and sysadmin from Italy. He likes to develop web and mobile applications by writing code on the full stack. You can find more info about his projects and his preferred technologies on his website.

Love sharing your technical expertise? Learn more about the Bitbucket writing program.