Docker-compose in Jenkins pipelines: keep builds separate

Tags: python, django

We use Jenkins with the pipelines plugin as our continuous integration server to test our software. The pipelines plugin means all the steps are in a Jenkinsfile inside your repo instead of as manually entered commands in a web interface (like it used to be). Nice and clean.

Also nice and clean: using dockers (with docker-compose) to run the tests in. The Jenkins machine used to be one big huge mess of installed software. Now it is clean and tidy: everything you need to run tests, you’d better install it inside your docker.

Recent problem. Recently Jenkins changed the “workspace” location: the place where the project is checked out and where your docker-compose is run. It used to be /var/lib/jenkins/workspaces/longdirectoryname. “longdirectoryname” would include (abbreviated) your project’s name, your branch/PR’s name and some hash. Any networks/volumes/etc created by docker-compose would include that unique name.

Recently, that location became /var/lib/jenkins/jobs/ORG_NAME/jobs/PROJECT_NAME/... and some more, ending with .../workspace. So suddenly every build is inside a directory called workspace so docker-compose will by default start prefixing networks/volumes with workspace instead of with a string that’s unique per project/branch!

So if you run a branch and pull request at the same time, both docker-compose runs will start tripping over each other as they use the same database server and so… Errors like “django test database already exists”….

The solution is to tell docker-compose to use a different name. The setting is COMPOSE_PROJECT_NAME. I set it to the name of the job + the job ID/number.

There are two ways you can do it. The first is to pass it along in an environment variable:

pipeline {
    agent any
    environment {
        COMPOSE_PROJECT_NAME = "${env.JOB_NAME}-${env.BUILD_ID}"
    }
    stages {
        ....  your stages go here ....
    }
    post {
        always {
            sh "docker-compose down -v"
        }
    }
}

I’m putting a docker-compose down -v as a sort of try..finally right at the end to make sure as much of docker’s stuff gets cleaned up.

The second way is to write the variable to a .env file. One of your steps should then look like this:

sh "echo 'COMPOSE_PROJECT_NAME=${env.JOB_NAME}-${env.BUILD_ID}' > .env"

Update:: I had problems with the “environment” approach. The .env way at least functions without any problems.

Now docker-compose and Jenkins are happy again.

 
vanrees.org logo

Reinout van Rees

My name is Reinout van Rees and I program in Python, I live in the Netherlands, I cycle recumbent bikes and I have a model railway.

Weblog feeds

Most of my website content is in my weblog. You can keep up to date by subscribing to the automatic feeds (for instance with Google reader):