Django is a popular Python-based web application framework that I’ve been using to write sample applications for my OpenStack testing. Jenkins is a great continuous integration tool for automated builds and testing. Why not combine the two to make my life a little easier?
My Django application development is being done on my laptop. For Jenkins, I wanted to run the server in a self-contained environment that would not be affected by versions of software on my local desktop.
Vagrant would normally be my go-to solution for a quick dev environment, but I’m overseas using questionable broadband services: a few vagrant up/vagrant provision/vagrant destroy cycles would bring my connection to a halt. I opted, instead, to use a Docker container for Jenkins. This way, if I mess up any package dependencies, it’s a simple matter of adding another layer to my existing container instead of building from scratch each iteration.
Before I go into detail regarding my build process, here is the TL;DR version:
- The official Jenkins container is a great starting point, but it does not support Django applications out-of-the-box
- I extended the Jenkins container (GitHub Repo) definition to include the Jenkins plugins that the django-jenkins Python module requires.
- I added the django-jenkins module to my Django application and modified my settings.py file to include directives for django-jenkins to follow during build steps.
- I deployed my Jenkins container and successfully performed unit tests for much win.
- Software versions:
- Python 3.4.3
- Django 1.8.4
- Jenkins 1.625.3
Still with me? Alright, let’s get into the build…
Django Application Changes
First, on my development machine, I installed the django-jenkins module.
pip3 install django-jenkins
Next, I edited my application’s settings.py to include the new django-jenkins module and to specify tasks that I want Jenkins to perform above and beyond my unit tests.
#application-root/application/settings.py INSTALLED_APPS = ( 'django.contrib.admin', ... 'django.contrib.staticfiles', 'my-app', 'django_jenkins', ) JENKINS_TASKS = ( 'django_jenkins.tasks.run_pylint', )
NOTE: For the pylint task to run, the pylint PyPI module needs to be installed on the Jenkins master or slave that will be performing the Django testing.
Jenkins Container Modifications
Out-of-the-box, Jenkins supports the Java programming language and the SVN version control system. However, as I covered in the DevOps for VMware Administrators book, there are many plugins available to extend a Jenkins server’s functionality to suit your application.
First, for my custom container specification, I created the following Dockerfile:
FROM jenkins # Change to root user to install required packages USER root RUN apt-get update -qq && apt-get install -qqy python3 python3-pip chromedriver RUN pip3 install -q pymongo coverage jinja2 django==1.8.4 RUN pip3 install --upgrade selenium # Change to the jenkins user for jenkins-specific modifications USER jenkins COPY plugins.txt /usr/share/jenkins/ref/ RUN /usr/local/bin/plugins.sh /usr/share/jenkins/ref/plugins.txt # Back to root again?!?? USER root RUN pip3 install django-jenkins RUN pip3 install pylint # Update per (Jason) Voorhees - Thanks! # switch back to jenkins user instead of # continuing to run as root! USER jenkins
In the container build, I first switch to the root user to install the required versions of Python, the Python Package Index utility (pip), Chrome web driver for selenium GUI tests, and numerous python packages.
Next, I switch to the jenkins user to perform Jenkins-specific modifications including specifying the Jenkins plugins that will be required to support Django applications. The Jenkins container will not resolve plugin dependencies automatically. So, you will need to list out the pre-requisite packages in addition to three packages that are actaully required to support Django (violations and cobertura) and Git (git).
Before moving on, note that I switched back to the root user to install a couple of python packages that I realized were missing during my testing. Because I am using Docker, my image update takes a relatively short time since I only add new layers for the binaries that were missing.
Within the directory that the Dockerfile resides, I build the container image and then (optionally) push it to the Docker Hub:
docker build -t vmtrooper/jenkins:0.0.4 . docker push vmtrooper/jenkins:0.0.4
I can specify my current image as the latest version and then push out this tag to the Docker Hub as follows:
docker tag -f vmtrooper/jenkins:0.0.4 vmtrooper/jenkins:latest docker push vmtrooper/jenkins:latest
My First Django Build Job in Jenkins
Now, I can run my jenkins container and start building out my build job:
docker run -d -p 8080 -p 50000 vmtrooper/jenkins:0.0.4
I monitor the Jenkins container’s logs using its container ID as follows:
docker ps docker logs <container id>
When the container is running, I access the Jenkins container using my Docker-Machine VM’s IP and the port mapped to the default 8080 Jenkins port:
Click on “New Item” or “create new jobs”, enter any name for your django appplication, and select the “Freestyle project” option
On the next screen, specify the Git repository where your code resides.
Next, specify how often to poll the repository for changes. I specified every 10 minutes.
Then, specify your Build Step using the “Execute Shell” option.
Here, I verify that Jenkins is in the workspace directory that contains the source code cloned from my Git repository, then I execute my Django tests using the jenkins keyword:
python3 manage.py jenkins --enable-coverage
Finally, specify the Post-Build Step to determine the folder that test results will be written to.
Click Save, and you’ll be taken to the project home screen. From here, you can click the Build Now link or wait for the new scheduled Git poll. When the build is complete, you can examine the test results to understand why the build succeeds or fails.
Do you have any tips for continuous integration of Django or other types of web applications? Share them below!