Building container images from scratch using Ansible
This blog post is a guide coming from ansible-bender#49 issue.
Okay, let’s start.
Building from scratch, what does it mean? It’s very simple: your base image is an empty directory:
$ buildah from scratch
working-container
$ mountpoint=$(buildah mount working-container)
$ ls -lha $mountpoint
total 0
drwxr-xr-x. 1 root root 6 Sep 23 19:55 .
drwx------. 6 root root 69 Sep 23 19:55 ..
If we want to use Ansible for all of this, we may need 2 plays:
- A play to run on localhost which populates the container
- A play which is executed inside the container (optional)
For the second one, we would need python or a shell inside the container. But don’t worry, we can install it using the first step. In the end it still depends on your container image: you may not need to run commands within the container env.
I am using Fedora OS all over the place, so in the first step, we’ll install python3 and bash Fedora packages inside the container using dnf, the Fedora OS package manager.
Let’s do that:
$ cat 1.yaml
---
- hosts: all
tasks:
- dnf:
name:
- python3
- bash
installroot: '{{ container_root }}'
$ ansible-playbook -c local -i localhost, -e container_root=$mountpoint -e ansible_python_interpreter=/usr/bin/python3 1.yaml
PLAY [all] *******************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************
ok: [localhost]
TASK [dnf] *******************************************************************************************************
changed: [localhost]
PLAY RECAP *******************************************************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
(the need for the ansible_python_interpreter
variable is that on Fedora,
/usr/bin/python
points to python2 - and we want python3)
How does our container look now?
$ ls -lha $mountpoint
total 8.0K
dr-xr-xr-x. 1 root root 224 Sep 23 20:07 .
drwx------. 6 root root 69 Sep 23 19:55 ..
lrwxrwxrwx. 1 root root 7 Feb 11 2019 bin -> usr/bin
dr-xr-xr-x. 4 root root 30 Sep 23 20:07 boot
drwxr-xr-x. 2 root root 18 Sep 23 20:07 dev
drwxr-xr-x. 51 root root 4.0K Sep 23 20:07 etc
drwxr-xr-x. 2 root root 6 Feb 11 2019 home
lrwxrwxrwx. 1 root root 7 Feb 11 2019 lib -> usr/lib
lrwxrwxrwx. 1 root root 9 Feb 11 2019 lib64 -> usr/lib64
drwxr-xr-x. 2 root root 6 Feb 11 2019 media
drwxr-xr-x. 2 root root 6 Feb 11 2019 mnt
drwxr-xr-x. 2 root root 6 Feb 11 2019 opt
dr-xr-xr-x. 2 root root 6 Feb 11 2019 proc
dr-xr-x---. 2 root root 6 Feb 11 2019 root
drwxr-xr-x. 14 root root 190 Sep 23 20:07 run
lrwxrwxrwx. 1 root root 8 Feb 11 2019 sbin -> usr/sbin
drwxr-xr-x. 2 root root 6 Feb 11 2019 srv
dr-xr-xr-x. 2 root root 6 Feb 11 2019 sys
drwxrwxrwt. 2 root root 6 Feb 11 2019 tmp
drwxr-xr-x. 12 root root 144 Sep 23 20:07 usr
drwxr-xr-x. 18 root root 235 Sep 23 20:07 var
$ du -sh $mountpoint
552M /var/lib/containers/storage/overlay/941e27b8da40fa9ac59e5f34ce2b68154844c32b0e85de09d22386dabf932f10/merged
Huh, that’s a lot, python is pretty beefy.
And now we can run commands inside the container, the same way I described here.
Let’s do it, just for sake of completeness.
$ cat 2.yaml
---
- hosts: all
tasks:
- command: python3 -c "print(\"python3 seems to be installed\")"
- command: bash -c "echo \"and bash as well\""
$ ansible-playbook -vv -c buildah -i working-container, -e ansible_python_interpreter=/usr/bin/python3 2.yaml
ansible-playbook 2.8.5
config file = /etc/ansible/ansible.cfg
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3.7/site-packages/ansible
executable location = /usr/bin/ansible-playbook
python version = 3.7.4 (default, Jul 9 2019, 16:32:37) [GCC 9.1.1 20190503 (Red Hat 9.1.1-1)]
Using /etc/ansible/ansible.cfg as config file
PLAYBOOK: 2.yaml ********************************************************************************************
1 plays in 2.yaml
PLAY [all] **************************************************************************************************
TASK [Gathering Facts] **************************************************************************************
task path: /root/2.yaml:2
ok: [working-container]
META: ran handlers
TASK [command] **********************************************************************************************
task path: /root/2.yaml:4
changed: [working-container] => {"changed": true, "cmd": ["python3", "-c", "print(\"python3 seems to be installed\")"], "delta": "0:00:00.011830", "end": "2019-09-23 18:15:45.791582", "rc": 0, "start": "2019-09-23 18:15:45.779752", "stderr": "", "stderr_lines": [], "stdout": "python3 seems to be installed", "stdout_lines": ["python3 seems to be installed"]}
TASK [command] **********************************************************************************************
task path: /root/2.yaml:5
changed: [working-container] => {"changed": true, "cmd": ["bash", "-c", "echo \"and bash as well\""], "delta": "0:00:00.002401", "end": "2019-09-23 18:15:47.838128", "rc": 0, "start": "2019-09-23 18:15:47.835727", "stderr": "", "stderr_lines": [], "stdout": "and bash as well", "stdout_lines": ["and bash as well"]}
META: ran handlers
PLAY RECAP **************************************************************************************************
working-container : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
That’s it, this was a quick post. It would be nice to implement this workflow inside ansible-bender, but I’m still not sure how exactly.