Manage Docker Containers using CoreOS — Part 3

Mohit Arora
4 min readMay 27, 2014

--

This is part 3 of the series on “How to manage Docker containers using CoreOS”. Please read Part 1 and Part 2 before reading this one and make sure you understand the basic concepts. In Part 2 of this series, we used docker images (sample-app and sample-app-httpd) which were already uploaded for us in docker central images repository. This article will mainly focus on “How to build and maintain docker images easily”. Before we start, I would like to introduce two open source projects (Packer and Ansible) that we will use to build docker images.

Packer 101: Packer is a tool for creating identical machine images for multiple platforms from a single source configuration. It is designed to allow the same provisioners, whether that be Puppet, Chef, Ansible or plain shell scripts, to spit out images for EC2, Docker, Digital Ocean or Vagrant boxes.

Why not Dockerfiles? Dockerfiles can also be used to build Docker images but you can configure Packer to build both “Docker images” and “Vagrant Boxes for your developers”, which is a great win. If your developer environment is provisioned in the same way as your production environment, you will never get any surprises.

Ansible 101: Ansible is a simple, but powerful, server and configuration management tool. With Ansible you don’t have to write scripts or custom code to provision your infrastructure. Ansible is very similar to tools like Puppet, Chef, Salt etc but it is much simpler in design.

Ansible documentation is pretty good, please familiarize yourself with ansible-playbook and ansible roles concepts.

We will use Packer to create our docker images and Ansible to provision our docker images.

Image Hierarchy:

Another very important concept that i always propose is that we should always develop a hierarchy of images (as shown below) whose base layers are the most stable, and where changes occur as close to the leaf nodes of the tree as possible.

We have total 5 docker images involved in the sample-app that we launched in part 2 of this series.

  1. centos-base: The centos-base image is simply an updated centos image available in docker index. Only additions are some common tools (wget, tar, vi etc), ansible and confd. You can explore it here. All other docker images will be built on top of this. Anytime we need to add a tool that is needed for all the images, we will be rebuilding this image.
  2. jdk7-base: jdk7-base is an image based on centos-base with Java 7 installed. You can explore it here. This image can be used to build docker images of any java based application. This image will be automatically re-built when new version of centos-base image is built.
  3. httpd-base: httpd-base is an image based on centos-base with apache http server installed. You can explore it here. This image can be used to build docker images of any application’s apache http server. This image will be automatically re-built when new version of centos-base image is built.
  4. sample-app-httpd: sample-app-httpd is an image on top of httpd-base image with customization targeted for sample app web server. One of the sample app specific customization it has is confd files targeted for sample app. You can explore it here.
  5. sample-app: sample-app is an image on top of jdk7-base image. This image has application binary and application configuration file. Every time there is a change checked in by developer in version control system, Continuous Integration system will be building new version of docker image. We will use the appropriate version to deploy on our CoreOS cluster. You can explore it here. (Note: In real world application, configuration file itself will not be part of the application docker image because config file changes according to environment. Confd template of configuration file will be bundled in the image and configuration file will be generated by confd when docker container starts. Config values will be managed in etcd cluster of every environment. You can even use zuul instead of etcd.)

We will be working on the pipeline of image building in next part of this series. As of now, let’s focus on building and versioning one image using Packer and Ansible. We will take jdk7-base image as an example:

Following is the Packer file used to build jdk7-base image. It is kind of self explanatory. We are asking packer to build a docker image based on centos-base image and provision it using ansible. Ansible playbook and roles files are also checked in parallel to packer configuration file.

{
"variables": {
"version": "",
"base-image-version": ""
},
"builders":[{
"type": "docker",
"image": "mohitarora/centos-base:{{user `base-image-version`}}",
"pull": true,
"export_path": "jdk7-base-{{user `version`}}.tar"
}],
"provisioners":[{
"type": "ansible-local",
"playbook_file": "playbooks/local.yml",
"role_paths":[
"roles/java"
]
}],
"post-processors": [{
"type": "docker-import",
"repository": "mohitarora/jdk7-base",
"tag": "{{user `version`}}"
}]
}

Note: Ansible is already installed on base image so all other images gets it for free.

How to run packer:

packer build -var ‘version=v1.0.0' -var ‘base-image-version=v1.0.0' jdk7-base.json
# Using base-image-version argument you can choose what version of base image will be used as base of this image.

You can read more details about Packer’s ansible-local provisioner here. It is pretty easy to crack.

Who will trigger packer to build docker images and When?:

Our Continuous Integration system will trigger packer to build docker images whenever there is a change in corresponding repository. (Will be explained in detail in next part).

If you prefer Puppet or Chef over Ansible, you can use them with Packer instead of Ansible.

In next part of this series, i will be bringing concepts explained in part 1, part2 and part3 together to build a continuous delivery system.

--

--