This is a tool which bends containers using Ansible playbooks and turns them into container images. It has a pluggable builder selection — it is up to you to pick the tool which will be used to construct your container image. Right now the only supported builder is buildah. More to come in the future. Ansible-bender (ab) relies on Ansible connection plugins for performing builds.
tl;dr Ansible is the frontend, buildah is the backend.
The concept is described in following blog posts:
You may be asking: why not ansible-container? Ansible bender is actually heavily inspired by ansible-container: the main distinction is that ansible-container covers the complete lifecycle of a containerized application while ab takes care of image builds only.
Status: ready to be used
Ansible-bender was recently moved to the ansible-community organization. \o/
- You can build your container images with buildah as a backend.
- Ansible playbook is your build recipe.
- You are able to set various image metadata via CLI or as specific Ansible vars:
- working directory
- environment variables
- labels
- user
- default command
- exposed ports
- You can do volume mounts during build.
- Caching mechanism:
- Every task result is cached as a container image layer.
- You can turn this off with
--no-cache
. - You can disable caching from a certain point by adding a tag
no-cache
to a task.
- You can stop creating new image layers by adding tag
stop-layering
to a task. - If an image build fails, it's committed and named with a suffix
-failed
(so you can take a look inside and resolve the issue). - The tool tries to find python interpreter inside the base image.
- You can push images you built to remote locations such as:
- a registry, a tarball, docker daemon, ...
podman push
is used to perform the push.
Ansible-bender has these commands:
Command | Description |
---|---|
build |
build a new container image using selected playbook |
list-builds |
list all builds |
get-logs |
display build logs |
inspect |
provide detailed metadata about the selected build |
push |
Push images you built to remote locations. |
$ pip3 install ansible-bender
If you are brave enough, please install bender directly from git master:
$ pip3 install git+https://github.com/TomasTomecek/ansible-bender
If pip3
command is not available on your system, you can run pip like this:
$ python3 -m pip install ...
Pip takes care of python dependencies, but ansible-bender also requires a few binaries to be present on your host system:
Last two requirements can be pretty tough: you can always run bender in a privileged container.
If you run ansible-bender as root, you don't need to do anything. Just install the packages and you are good to go.
On the other hand, if you want to utilize the rootless mode, you need to set up the UID mapping. It is documented in podman's documentation. All you need to do is to add an entry into /etc/subuid and /etc/subgid:
$ sudo sh -c "printf \"\n$(whoami):100000:65536\n\" >>/etc/subuid"
$ sudo sh -c "printf \"\n$(whoami):100000:65536\n\" >>/etc/subgid"
You should consult podman's troubleshooting guide if you are running into issues.
- python interpreter — ansible-bender will try to find it (alternatively you
can specify it via
--python-interpreter
).- It can be python 2 or python 3 — on host, you have to have python 3 but inside the base image, it doesn't matter — Ansible is able to utilize python 2 even if it's invoked with python 3 on the control machine.
None.
Bender copies the playbook you provide so that it can be processed. hosts
variable is being overwritten in the copy and changed to the name of the
working container — where the build happens. So it doesn't matter what's the
content of the hosts variable.
You can configure ansible-bender and set metadata on your final image, in order to do that, please check out docs/configuration.md.
If you are using roles in your playbook and they are in a non-standard place,
you can utilize ANSIBLE_ROLES_PATH
environment variable to tell ansible where
your roles lives. Bender does not tamper with environment variables, all are
passed to ansible-playbook.
It may happen there is something wrong in your setup or the tooling Bender is relying on is not in the best shape. The best way to find the root cause is to run Bender in debug mode and run ansible as verbosely as possible:
$ ansible-bender --debug build --extra-ansible-args='-vvvvvv'
Typing ansible-bender
can take some time, consider adding an alias into your
shell rc file:
alias ab="ansible-bender"
There is a simple playbook present in the root of this repository to showcase the functionality:
$ ansible-bender build ./simple-playbook.yaml
PLAY [Demonstration of ansible-bender functionality] ****************************************
TASK [Gathering Facts] **********************************************************************
ok: [a-very-nice-image-20190302-153257279579-cont]
TASK [Run a sample command] *****************************************************************
changed: [a-very-nice-image-20190302-153257279579-cont]
caching the task result in an image 'a-very-nice-image-20193302-153306'
TASK [Stat a file] **************************************************************************
ok: [a-very-nice-image-20190302-153257279579-cont]
caching the task result in an image 'a-very-nice-image-20193302-153310'
PLAY RECAP **********************************************************************************
a-very-nice-image-20190302-153257279579-cont : ok=3 changed=1 unreachable=0 failed=0
Getting image source signatures
Skipping blob 767f936afb51 (already present): 4.46 MiB / 4.46 MiB [=========] 0s
Skipping blob b211a7fc6e85 (already present): 819.00 KiB / 819.00 KiB [=====] 0s
Skipping blob 8d092d3e44bb (already present): 67.20 MiB / 67.20 MiB [=======] 0s
Skipping blob 767f936afb51 (already present): 4.46 MiB / 4.46 MiB [=========] 0s
Skipping blob b211a7fc6e85 (already present): 819.00 KiB / 819.00 KiB [=====] 0s
Skipping blob 8d092d3e44bb (already present): 67.20 MiB / 67.20 MiB [=======] 0s
Skipping blob 492c5c55da84 (already present): 4.50 KiB / 4.50 KiB [=========] 0s
Skipping blob 6f55b6e55d8a (already present): 6.15 MiB / 6.15 MiB [=========] 0s
Skipping blob 80ea48511c5d (already present): 1021.00 KiB / 1021.00 KiB [===] 0s
Copying config 6b6dc5878fb2: 0 B / 5.15 KiB [----------------------------------]
Copying config 6b6dc5878fb2: 5.15 KiB / 5.15 KiB [==========================] 0s
Writing manifest to image destination
Storing signatures
6b6dc5878fb2c2c10099adbb4458c2fc78cd894134df6e4dee0bf8656e93825a
Image 'a-very-nice-image' was built successfully \o/
This is how the playbook looks:
---
- name: Demonstration of ansible-bender functionality
hosts: all
vars:
ansible_bender:
base_image: python:3-alpine
working_container:
volumes:
- '{{ playbook_dir }}:/src'
target_image:
name: a-very-nice-image
working_dir: /src
labels:
built-by: '{{ ansible_user }}'
environment:
FILE_TO_PROCESS: README.md
tasks:
- name: Run a sample command
command: 'ls -lha /src'
- name: Stat a file
stat:
path: "{{ lookup('env','FILE_TO_PROCESS') }}"
As you can see, the whole build processed is configured by the variable
ansible_bender
.
If we rerun the build again, we can see that ab loads every task from cache:
$ ansible-bender build ./simple-playbook.yaml
PLAY [Demonstration of ansible-bender functionality] ****************************************
TASK [Gathering Facts] **********************************************************************
ok: [a-very-nice-image-20190302-153526013757-cont]
TASK [Run a sample command] *****************************************************************
loaded from cache: '7c69668c42987446cc78adbf6620fc2faf90ad10c3497662fe38940dd6de998f'
skipping: [a-very-nice-image-20190302-153526013757-cont]
TASK [Stat a file] **************************************************************************
loaded from cache: '4a4f54285928c03eea65745ee9feead88026c780a40126d94e79d5842bcdbe62'
skipping: [a-very-nice-image-20190302-153526013757-cont]
PLAY RECAP **********************************************************************************
a-very-nice-image-20190302-153526013757-cont : ok=1 changed=0 unreachable=0 failed=0
Getting image source signatures
Skipping blob 767f936afb51 (already present): 4.46 MiB / 4.46 MiB [=========] 0s
Skipping blob b211a7fc6e85 (already present): 819.00 KiB / 819.00 KiB [=====] 0s
Skipping blob 8d092d3e44bb (already present): 67.20 MiB / 67.20 MiB [=======] 0s
Skipping blob 492c5c55da84 (already present): 4.50 KiB / 4.50 KiB [=========] 0s
Skipping blob 767f936afb51 (already present): 4.46 MiB / 4.46 MiB [=========] 0s
Skipping blob 6f55b6e55d8a (already present): 6.15 MiB / 6.15 MiB [=========] 0s
Skipping blob b211a7fc6e85 (already present): 819.00 KiB / 819.00 KiB [=====] 0s
Skipping blob 80ea48511c5d (already present): 1021.00 KiB / 1021.00 KiB [===] 0s
Skipping blob 8d092d3e44bb (already present): 67.20 MiB / 67.20 MiB [=======] 0s
Skipping blob 5f70bf18a086 (already present): 1.00 KiB / 1.00 KiB [=========] 0s
Skipping blob 492c5c55da84 (already present): 4.50 KiB / 4.50 KiB [=========] 0s
Skipping blob 6f55b6e55d8a (already present): 6.15 MiB / 6.15 MiB [=========] 0s
Skipping blob 80ea48511c5d (already present): 1021.00 KiB / 1021.00 KiB [===] 0s
Skipping blob 5f70bf18a086 (already present): 1.00 KiB / 1.00 KiB [=========] 0s
Copying config 354752b97084: 0 B / 5.26 KiB [----------------------------------]
Copying config 354752b97084: 5.26 KiB / 5.26 KiB [==========================] 0s
Writing manifest to image destination
Storing signatures
354752b97084fcf349a28a2f66839d270e728559883dd1edb5ec22e8c9c6adb9
Image 'a-very-nice-image' was built successfully \o/
We can list builds we have done:
$ ansible-bender list-builds
BUILD ID IMAGE NAME STATUS DATE BUILD TIME
---------- ----------------- -------- -------------------------- --------------
1 a-very-nice-image done 2019-03-02 16:07:47.471912 0:00:12.347721
2 a-very-nice-image done 2019-03-02 16:07:58.858699 0:00:06.242378
Wanna check build logs sometime later? No problem!
$ ansible-bender get-logs 2
PLAY [Demonstration of ansible-bender functionality] *********************************
TASK [Gathering Facts] ***************************************************************
ok: [a-very-nice-image-20190302-160751828671-cont]
TASK [Run a sample command] **********************************************************
loaded from cache: 'cd27cfb71a161f3333232b97cc6b2a89354ff52de71bce9058c52cdf536735f9'
skipping: [a-very-nice-image-20190302-160751828671-cont]
TASK [Stat a file] *******************************************************************
loaded from cache: '89ba4efc31358d688f035bf8159d900f1552314f0af6bf6c338b4897da593ccf'
skipping: [a-very-nice-image-20190302-160751828671-cont]
PLAY RECAP ***************************************************************************
a-very-nice-image-20190302-160751828671-cont : ok=1 changed=0 unreachable=0 failed=0
Once they are built, you can use them with podman right away:
$ podman images a-very-nice-image
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/a-very-nice-image latest 5202048d9a0e 2 minutes ago 83.5 MB
If you are familiar with podman and buildah, you know that you can configure these tools. Ansible-bender doesn't change this configuration in any way so it's up to you how you set up buildah and podman. The same applies for running ansible-bender as root or not: buildah allows you to utilize rootless containers.
My suggestion is to use the overlay storage backend. Vfs backend is slow and inefficient.
Ansible bender has a caching mechanism. It is enabled by default. ab caches task results (=images). If a task content did not change and the base image is the same, the layer is loaded from cache instead of being processed again. This doesn't work correctly with tasks which process file: ab doesn't handle files yet.
You are able to control caching in two ways:
- disable it completely by running
ab build --no-cache
- or adding a tag to your task named
no-cache
— ab detects such tag and will not try to load from cache
When building your image by default, every task (except for setup) is being cached as an image layer. This may have bad consequences on storage and security: there may be things which you didn't want to have cached nor stored in a layer (certificates, package manager metadata, build artifacts).
ab allows you to easily disable layering mechanism. All you need to do is to
add a tag stop-layering
to a task which will disable layering (and caching)
for that task and all the following ones.
Recently I started experimenting with running ab inside OpenShift origin — imagine that you'd be able to build images in your cluster, using Ansible playbooks as definitions.
Openshift by default runs its pods in a restrictive environment. In the proof of concept I was forced to run ab in a privileged pod. In the end, the whole test suite is passing in that privileged pod.