Docker in Action – Development to Delivery, Part 2

This is a guest post by Michael Herman from Real Python – learn Python programming and web development through hands-on, interesting examples that are useful and fun!

This three part series will teach you everything you need to know about developing with Docker – from setting up your environments and utilizing Flask on Docker to detailing a powerful development workflow that covers setting up a fully functional development environment, on your Mac, and managing continuous integration and delivery.

Last time we set up our local environment, detailing the basic process of building an image from a Dockerfile and then creating an instance of the image called a container, which runs our Flask app. This time, let’s look at a nice continuous integration workflow powered by CircleCI.

Services used: Docker Hub, Github, CircleCI

Docker Hub

Thus far we’ve worked with Dockerfiles, images, and containers. If you’re familiar with the Git workflow, then images are like Git repositories while containers are similar to a cloned repository. Sticking with that metaphor, Docker Hub, which is repository of Docker images, is akin to Github.

Then add a new automated build. Find your Github repo that you pushed to in the first tutorial. Once added, this will trigger an initial build. Make sure the build is successful.

Docker Hub as a CI server

Docker Hub, in itself, acts as a continuous integration server since you can configure it to create a build every time you push a new commit to Github. In other words, it ensures you do not cause a regression that completely breaks the build process when the code base is updated.

Keep in mind by using an automated build, you cannot use the docker push command. Builds must be triggered by committing code to your GitHub or BitBucket repository.

Bottom-line: It’s good to know that if a commit does cause a regression that Docker Hub will catch it, but since this is the last line of defense before deploying you ideally want to catch any breaks before generating a new build on Docker Hub. Plus, you also want to run your unit and integration tests from a true continuous integration server.

Enter CircleCi.

CircleCI

CircleCI is a continuous integration and delivery platform, which supports Docker. Given a Dockerfile, CircleCI builds an image, starts a new container, and then runs tests against that container.

Essentially, we create a new image, run the container, then test – first that the app is live (e.g., the web process is running) and then that our unit tests pass. With the circle.yml file created, push the changes to Github to trigger a new test. Remember: this will also trigger a new build on Docker Hub.

CircleCI does not support the caching feature discussed in Part 1, so by default the entire image is rebuilt from scratch each time. Check out the official CircleCI documentation for an alternative way to speed up builds.

If all went well, that should have passed. Before we call it quits, we need to change our workflow since we won’t be pushing directly to the master branch anymore.

Feature Branch Workflow

For these unfamiliar with the Feature Branch workflow, check out this excellent introduction.

Here’s the basic workflow that we’ll utilize:

Create a feature branch from master.

Write your code and tests on the feature branch.

Issue a pull request to merge your feature branch back to the master branch.

Update the app

Issue a Pull Request

Even before you create the actual pull request, CircleCI runs the automated tests. While the tests are running, go ahead and create the pull request, then once the tests pass, press the Merge button (with confidence!). Once merged, the build is triggered on Docker Hub.

Refactoring the workflow

If you jump back to the overall workflow at the top of this post, you’ll see that we don’t actually want to trigger a new build on Docker Hub until the tests pass against the master branch. So, let’s make some quick changes…

Open your repository on Docker Hub, and then under Settings click Automated Build.

Uncheck the Active box: “When active we will build when new pushes occur”.