Share: Facebook icon - Twitter icon - LinkedIn icon

Creating container images with Ansible (using ansible-bender)

Published Thu Mar 17 2022

Tags: cli ansible devops automation

Did you know that you can use Ansible to make container images? It's actually a very readable way to create images compared to some Dockerfiles you find online (if you don't think so, you have probably never read one that is 100+ lines). In this article we will look at building images using ansible-bender, which makes for a good way to make images with Ansible with minimal fuzz.

Need a refresher on Ansible? It was introduced in the last article, so feel free to read it to refresh the concepts. You probably won't get much use out of ansible-bender without knowing the basics of Ansible first.

In the last article, building container images with Ansible was briefly discussed. An external blog article on that very subject was linked to, and if you read it you may have seen a lot of manual steps using Docker- and Buildah-commands. This seems like a lot of boiler plate code just to make container images with Ansible, right? From that idea, ansible-bender was born! Ansible-Bender uses Buildah to build your images, and Podman to handle them (some commands like pushing images, build logs, inspecting images etc. are included into Ansible-Bender). Buildah is a container image builder that does not depend on any external programs, and can build containers for any container runtime. A very big downside is that it's only supported on GNU/Linux-based systems, which is bad news if you are using Mac OS X (or god forbid if you are one of the Windows users, ugh). To combat this issue I made a Docker image that includes Buildah and all dependencies, that can be used to run Ansible-Bender on other platforms. Might be a bit slower than just using it on a GNU/Linux-based system, but beats not being able to use it at all!

There are obviously some pros and cons relating to using such a tool compared to plain Dockerfiles:



In the end, it comes down to taste. If you really love Ansible and its expressiveness, I think you will enjoy using ansible-bender.

Using ansible-bender

So you have installed ansible-bender, either directly or by using an image like mine above. What's next? If you have installed Buildah and Podman as well, you can actually start using the tool directly! If you want a playbook with ansible-bender specific variables inserted for you, you can use the ansible-bender init command, and a playbook.yml file is created for you. In essence, that is what an ansible-bender playbook really is; a normal Ansible playbook with some special variables. Those special variables are covered in depth in the documentation.

You may wonder: what if I have ansible_bender variables in more than one play, what happens then? Only the variables from the first play is used. Beyond that you are free to have multiple plays.

Talk is cheap, let's look at some examples!

Example: Containerized Emacs setup

Continuing the trend from the last article! Let's set up an image with my Emacs setup ready to be used.

- name: Emacs setup - Containerized 
  hosts: all

      base_image: python:3.9-slim-bullseye

	name: myemacs
	cmd: bash
	user: themkat
	working_dir: /home/themkat

    - name: Update package archives (Debian-based)
	update_cache: true
	cache_valid_time: 7200
      when: ansible_os_family == "Debian"

    - name: Make sure Emacs and git is installed
	  - emacs
	  - git
	state: present

    - name: Create themkat user
	name: themkat
	create_home: true
	state: present

    # had issues with using the home symbol + become_user here. Might be an issue to look out for
    - name: Download Emacs  config
      #become_user: themkat
	dest: /home/themkat/.emacs.d

    - name: Make themkat the owner
	path: /home/themkat/.emacs.d
	owner: themkat
	recurse: true
	state: directory

NOTE: As you see in the comment above, I have experienced some minor issues with the become_user and home symbol/tilde. These might be bugs in ansible-bender or relating to containers. Have not tested enough to find the culprit yet, so presenting this as something to be aware of.

How do we build this? Use the command ansible-bender build playbook.yml. You might wonder why we have the "when Debian"-check when it will always be Debian with that base image. The reason is simple: by doing this we keep the setup flexible. If we wanted a Fedora image, we could simple override it from the command line ansible-bender build playbook.yml fedora:35.

As you can see above, the differences from a plain Ansible playbook to a ansible-bender one used to create images are not that big. Simple some image related variables like the base image, as well as resulting image name, command to be run, user etc. Standard settings we do when making container images in general.

Example: Simple Spring Boot application image

Let's do something slightly different by demonstrating the usage of volumes and Ansible roles during build. It's not unusual to want to package an application into a container, and for simplicity we put it into the same directory as the playbook itself (so we can use the playbook_dir variable). To not make this example too advanced, we simply use a Spring Boot application made from Spring Initializr without any changes (if you prefer a terminal UI, there is one for that too). We'll use geerlingguys java role to setup Java to not boggle down the example with Java setup.

- name: Containerized Spring Boot app
  hosts: all
      base_image: fedora:35
	cmd: java -jar /app/spring-app.jar
	name: spring-app-example
	- "{{ playbook_dir }}:/src"

    - role:
	- java-11-openjdk

  - name: Make app directory that will include our application
      path: /app
      state: directory

  - name: Build application
      chdir: /src
      cmd: ./mvnw clean install
      creates: /src/target/spring-app-0.0.1-SNAPSHOT.jar

  - name: Copy jar file to app directory
      remote_src: true
      src: /src/target/spring-app-0.0.1-SNAPSHOT.jar
      dest: /app/spring-app.jar

To run this one, we will have to use ansible-galaxy to install first, either with a requirements file or directly (ansible-galaxy install After that we can simply build our image like in the last example.

What happens here? We build our Maven Spring Boot project, and put the resulting jar file into a app-directory. You may wonder: Shouldn't we delete the contents of src? Won't all the source code and build files be included in the final image? No! When building is done, src is unmounted and the directory is empty. Pretty neat!

Useful commands

ansible-bender has quite a few useful commands beyond just building images. If you want to fetch earlier build logs, that can be done. Inspecting images is simple. But to me, the most useful is probably pushing images. Not necessarily to a central repository or something, but to Docker. I have to admit, I still use Docker. Let's say we have built the myemacs image as in the first example, and want to push it to our docker daemon with the tag 0.1. Then we simply run:

ansible-bender push docker-daemon:myemacs:0.1


In summary, ansible-bender seems like a promising tool that definitely have some fun and useful use cases. Making images with Ansible seems like a very readable way to make images, especially for bigger ones. Dockerfiles quickly gets messy, and Ansible might be an antidote for that issue.

Other posts that might interest you: