DevOps

Build Docker Images with Packer and Ansible

Recently someone asked me where a good place is to get started learning tools like docker, packer and ansible. I did some quick googling and didn’t find what I thought were really good, in-depth tutorials so I decided to write one myself!

Getting Started

This tutorial assumes you are working with OSX although you should be able to accomplish the same results on a linux workstation by seeking out the packages required for your platform.
To get started, let’s install the following tools. If you don’t already have homebrew installed I recommend installing it first.

  • Install packer. You can install it via brew install packer.
  • Install Docker.
  • Install ansible (brew install ansible).

Our First Packer Template

The goal of this tutorial is to get a packer template together that will build a docker image using ansible to provision it. With that in mind, we’re going start by first dipping our toes into packer and use the docker builder, the local shell provisioner and finally a docker post-processor to export docker image with a single file added to /root.

{
  "builders": [{
    "type": "docker",
    "image": "ubuntu:16.04",
    "commit": "true"
  }],
 "provisioners": [
    {
      "type": "shell",
      "inline": ["echo 'hello!' > /root/foo"]
    }
  ],
  "post-processors": [
    [
      {
        "type": "docker-tag",
        "repository": "jamescarr/demo",
        "tag": "0.1"
      }
    ]
  ]
}

Save this as template.json and run packer build template.json. Once the build finishes you should be able to run the docker image and inspect it.

jamescarr@Jamess-MacBook-Pro-2 ~/Projects/docker-ansible 
[13:11:49]
> $ docker run -it jamescarr/demo:0.1 bash  
⬡ 6.2.2
root@bec87474b4a7:/# ls /root
foo
root@bec87474b4a7:/# cat /root/foo
hello!

Adding Ansible

Next, let’s swap out our shell provisioner with ansible. There are two options you can go with, ansible-local or ansible-remote. The difference here is that ansible-local will run on the target (in this case, in a docker container) while ansible remote will run on your local machine against the target (typically over ssh). Basically your needs will determine which you want to use. If you want ansible on the target image (perhaps to render templates on container start) then ansible-local is the right path to go while ansible-remote is great if we want to use ansible to provision the image but want to leave ansible off of the resulting image.
This one will be a rather big change… we need to use docker as the ansible_connection as the default will try to connect over ssh and transfer files via scp… obviously this won’t work in docker unless our container runs an ssh server! So we need to instruct ansible to use the docker connection driver to connect. This also requires us to create a consistent hostname, so we define a container name to use when building. We also use the shell provisioner again to install python since ansible will require python on the target and the docker image doesn’t have it by default.
Here’s the updated template.json:

{
  "variables": {
    "ansible_host": "default",
    "ansible_connection": "docker"
  },
  "builders": [
    {
      "type": "docker",
      "image": "ubuntu:16.04",
      "commit": "true",
      "run_command": [
        "-d",
        "-i",
        "-t",
        "--name",
        "{{user `ansible_host`}}",
        "{{.Image}}",
        "/bin/bash"
      ]
    }
  ],
  "provisioners": [
    {
      "type": "shell",
      "inline": [
        "apt-get update",
        "apt-get install python -yq"
      ]
    },
    {
      "type": "ansible",
      "user": "root",
      "playbook_file": "./playbook.yml",
      "extra_arguments": [
        "--extra-vars",
        "ansible_host={{user `ansible_host`}} ansible_connection={{user `ansible_connection`}}"
      ]
    }
  ],
  "post-processors": [
    [
      {
        "type": "docker-tag",
        "repository": "jamescarr/demo",
        "tag": "0.1"
      }
    ]
  ]
}

And the new playbook.yml file which has a single task that uses copy to generate a file similar to what we created previously with the shell provisioner.

---
- name: A demo to run ansible in a docker container
  hosts: all
  tasks:
    - name: Add a file to root's home dir
      copy:
        dest: /root/foo
        content: Hello World!
        owner: root

Run packer build template.json and once it completes, let’s test it out.

jamescarr@Jamess-MacBook-Pro-2 ~/Projects/docker-ansible   
[13:47:00]
> $ docker run -it jamescarr/demo:0.1 bash    
⬡ 6.2.2 [±master ●●]
root@2c46ddf2d657:/# cat /root/foo
Hello World!root@2c46ddf2d657:/# ^C
(failed reverse-i-search)`pa': ^C
root@2c46ddf2d657:/# exit
exit

Next Up

I hope this has been a good quick overview on getting started using packer, ansible and docker. This didn’t really produce much useful aside from introducing the pieces and providing a foundation to start with. Tomorrow’s post I’ll tackle some larger “real world” solutions and how to make this a bit more dynamic!

Reference: Build Docker Images with Packer and Ansible from our WCG partner James Carr at the James Carr blog.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button