Skip to main content

Managing prebuilt OS images with Ansible

Prebuilt OS images are usually available for virtualization environments, for example see lists for OpenVZ, Vagrant, EC2, VirtualBox (also here) or Proxmox. As you can guess, some machinery is needed for building and maintaining them all. There is veewee for Vagrant, template creation guides for OpenVZ, dab for Proxmox, and so on.

The chroot connection

Ansible got support recently for executing tasks chrooted inside a local directory. The implementation was straightforward given Ansible agentless nature. chroot was added as a new connection type and that had two nice side effects:

  1. chroot directories can be added to the inventory like any other hostname.

  2. any existing playbook will potentially run chrooted by just setting the chroot connection type.

    - include: tasks/foo.yml ansible_connection=chroot
    

Putting things together

So you can now bootstrap your distro as usual, run a playbook chrooted to it and them archive the directory. That’s exactly what this example playbooks do.

$ git clone https://github.com/mmoya/ansible-playbooks.git
$ cd ansible-playbooks/image-creation
$ sudo ansible-playbook image-creation-stage1.yml
$ sudo ansible-playbook image-creation-stage2.yml

when the cow stops mooing you will have these tar.gz in /var/tmp/built-images:

  • squeeze-raw-amd64.tar.gz: this is a barely Debian bootstrap.
  • debian-6.0-64.tar.gz: this is just squeeze-raw-amd64.tar.gz plus kernel, openssh-server and other customizations.
  • 32bits versions of the above.

as you can guess from the inventory, it’s trivial to add another debootstrap’able distribution.

[images-stage1]
/var/tmp/squeeze-raw-amd64          suite=squeeze       arch=amd64
/var/tmp/squeeze-raw-i386           suite=squeeze       arch=i386

[images-stage2]
/var/tmp/debian-6.0-64              baseimage=squeeze-raw-amd64.tar.gz
/var/tmp/debian-6.0-32              baseimage=squeeze-raw-i386.tar.gz

Two stages

I’ve identified two image categories:

  1. bare image: distro bootstrap with zero or minimal configuration.
  2. custom image: bare image with needed customization.

think of bare images as one per distro (squeeze, wheezy, precise, fedora18, etc…) and custom images as the ones typically published (debian-6.0-lamp-server, precise-nagios-server, fedora18-jboss, etc…). One bare image is used as base for building multiple custom images. Each stage builts one said category, this way it’s easier to cache the distro bootstrap for later reuse.

Missing stuff

I’m missing two things from the example:

  1. Make the playbook work with non Debian distros (febootstrap vs. deboostrap, file location differences, etc…)

  2. Specify extra software to install, ideally in the inventory (see 2290). Thinking in something like:

    [images-stage2]
    /var/tmp/debian-6.0-drupal baseimage=squeeze-raw-amd64.tar.gz install=lamp,drupal
    

Why bother?

There are some advantages of using Ansible instead of a custom script:

  • You can reuse with little or zero effort existing playbooks.
  • You can separate data from code making things easier to audit.
  • … and the others things from Ansible you get for free:
    • a powerful templating system (more flexible customization of files inside image).
    • parallel execution (build images faster).
    • idempotent changes (run again and again from any point in the process).
    • more and more modules available.
  • Integrate image maintenance in your workflow if you’re already using Ansible for managing your servers.