Docker images are basically filesystems bundled with parameters to use at runtime. The filesystem is usually a small part of a Linux Userland, with enough files to start the desired process. Docker provides tooling to build these images, generally based on very small, preexisting base images. The tooling uses a Dockerfile as the input, which is a plain text file with directives. This file is parsed by the docker build command, and we can parse it via the docker_image module. The remaining examples will be from a CentOS 7 virtual machine using Docker version 1.13.1, with the cowsay and nginx packages added, so that running the container will provide a web server that will display something from cowsay.
First, we'll need a Dockerfile. This file needs to live in a path that Ansible can read, and we're going to put it in the same directory as my playbooks. The Dockerfile content will be very simple. We'll need to define a base image, a command to run to install the necessary software, some minimal configuration of software, a port to expose, and a default action for running a container with this image:
FROM docker.io/fedora:29 RUN dnf install -y cowsay nginx RUN echo "daemon off;" >> /etc/nginx/nginx.conf RUN cowsay boop > /usr/share/nginx/html/index.html EXPOSE 80 CMD /usr/sbin/nginx
The build process performs the following steps:
- We're using the Fedora 29 image from the fedora repository on the Docker Hub image registry.
- To install the necessary cowsay and nginx packages, we're using dnf.
- To run nginx directly in the container, we need to turn daemon mode off in nginx.conf.
- We use cowsay to generate content for the default web page.
- Then, we're instructing Docker to expose port 80 in the container, where nginx will listen for connections.
- Finally, the default action of this container will be to run nginx.
The playbook to build and use the image can live in the same directory. We'll name it docker-interact.yaml. This playbook will operate on localhost, and will have two tasks; one will be to build the image using docker_image, and the other will be to launch the container using docker_container:
--- - name: build an image hosts: localhost gather_facts: false tasks: - name: build that image docker_image: path: . state: present name: fedora-moo - name: start the container docker_container: name: playbook-container image: fedora-moo ports: 8080:80 state: started
Now, if you've been using AWX on the same host as this like I have, you will already have a few Docker containers running. Luckily, none of these are based on Fedora, so we can easily use the --filter parameters with Docker to exclude anything that doesn't have the term fedora in the image name, making the output easier to interpret, as shown in the following screenshots:
Now, let's run the playbook to build the image and start a container using that image:
The verbosity of this playbook execution was reduced to save screen space. Our output simply shows that the task to build the image resulted in a change, as did the task to start the container. A quick check of running containers and available images should reflect our work:
We can test the functionality of our container by using curl to access the web server, which should show us a cow saying boop:
In this manner, we have already shown how easy it is to interact with Docker using Ansible. However, this example is still based on using a native Dockerfile, and, as we progress through this chapter, we'll see some more advanced Ansible usage that removes the need for this.