patg.net

home

Docker: Containers for the Masses -- Ansible and Docker

18 Jun 2014

Welcome again to the series "Docker: Containers for the Masses". Today's post will be the fourth in this series and will concern itself with management of Docker using Ansible

For the reader just joining, the previous three posts in this series are:

With the basics of Docker having been presented, the following blog posts will cover more advanced usage-cases for Docker as well as complimentary and related projects.

As mentioned before, I have had the pleasure of working with both Docker and Ansible and realized there were some features in Ansible related to Docker that I wanted to familiarize myself with and take advantage of. Additionally, the opportunity to contribute new features to Ansible presented itself. One of those features, the docker_facts Ansible module, will be covered in a later post of this series.

This first post will familiarize the reader to what Ansible is, its modular design, and introduce a simple Ansible playbook.

Ansible

There is a good chance that readers of this blog post know what Ansible is and they can skip ahead two sections.

For readers who don't know, Ansible is a tool in the same category as Puppet, Chef, and SaltStack. Ansible is an automation tool, more specifically an orchestration engine. It is very simple and manages nodes via ssh using a push model, not requiring a server or clients acting on behalf of the server as so many other automation tools that use a server and pull model do. It is modular and modules simply output JSON that Ansible engine acts upon.

Ansible is written in python and uses what it calls playbooks, written in YAML, to represent how it expects the state of a system to be. Specific tasks, called plays, occur to get a node to be in the specified state. Like SaltStack, it uses Jinja templates for files it needs to dynamically create.

Ansible uses an inventory file that contains a list of the nodes which can be grouped into different classifications depending on what purpose you want for each -- think set-theory. Furthermore you can also use multiple inventory files or even dynamic inventory plugins that take into account elastic environments where it would be useful to be able to manage resources intelligently.

Ansible Basics

To give the reader who is not familiar with Ansible an idea and context for the rest of this post, a simple playbook shows how Ansible is used to install Percona XtraDB cluster packages.

- name: Install Percona XtraDB Cluster server
  apt: pkg=
       state=present
  with_items:
    - percona-xtradb-cluster-server-5.6
    - python-mysqldb
    - xinetd
    - git

- name: Configure Percona XtraDB
  template: src=etc/mysql/my.cnf.j2
            dest=/etc/mysql/my.cnf
            mode=0640 owner=mysql group=root
  notify: restart mysql
  when: bootstrap_check.stdout == "bootstrap"

The above example is contains a snippet from a playbook consisting of two plays, the name describing what the play does.

In the case of the playbook above, the first play being run results in the 4 packages listed in with_items being installed using the apt Ansible module, the state of these to be present. Ansible modules are idempotent, hence once these modules are installed, subsequent plays will result in no action.

The next play uses template to specify a source template my.cnf.j2 to be rendered as /etc/mysql/my.cnf, set to the file attributes and ownership listed by mode, and owner and group, respectively. When this play is completed, there will be /etc/mysql/my.cnf file present and any variables in the jinja template my.cnf.j2 being interpolated in the template rendering. As mentioned above, Ansible modules are idempotent, the result being that once the template is rendered and file created, it won't be re-rendered unless the template changes.

Again, this is a snippet, and there are other great examples available including an example Ansible playbook git repository complete with numerous playbooks as well as the documentation that one can read to become an expert Ansible user.

Ansible and Docker

As I've mentioned in other blog posts on this site, my team, HP Advanced Technology Group, has taken an interest in a number of interesting and burgeoning projects and/or technologies, both internal and external to HP. Two of those projects are Ansible and Docker, and both go together well.

This and the next few blog posts will cover several Docker modules and plugins:

The Ansible docker module

The first module to be discussed is the very module one would use for launching or deleting container, the Ansible docker module. This module is part of the default Ansible installation and will only require the Docker python client library to be installed.

As you recall in using Docker by hand using the Docker CLI, you would use the Docker CLI to do these types of tasks. Ansible makes this even more simple.

This is a very simple module to use and essentially a play describes itself, which really is the whole idea behind ansible:

- name: docker image control
  local_action:
    module: docker
    docker_url: "tcp://somehost:4243"
    image: "capttofu/percona_xtradb"
    name: "db"
    state: "present"
    publish_all_ports: yes

In the above example, this play would result in running locally (local_action) the launching of a container with the name of db with all ports specified in image (Dockerfile) to be published. Furthermore, the parameter docker_url specifies which Docker daemon to issue this to, from the local action. This is important to note, because otherwise it would assume localhost. In real-world situations, one may have the need to manage Docker containers across multiple servers running the Docker daemon and would need to be able to connect to Docker on those hosts. Under the hood, the Ansible docker module is using the Ansible client python module that talks to the daemon on the current host specified in docker_url.

This makes for an interesting topography: managing systems (VM or bare metal) that run Docker and in turn, managing those containers, all with Ansible.

A more practical example is one that can be used to run multiple containers

- hosts: localhost
  tasks:
    - name: run docker containers
      local_action:
        module: docker
        docker_url: tcp://127.0.0.1:4243
        image: capttofu/apache
        name: container_
        state: present
      with_sequence: count=5

The example above is a simple playbook that has one task: run 5 docker containers. This playbook uses local_action to run docker locally against the Docker daemon running on port 4243 -- which could be any docker host but in this case is simplified to 127.0.0.1 -- to connect to to run these containers. The image used is capttofu/apache, a simple container that runs Apache. The name of the container passed utilizes the the variable item which is whatever value is being iterated over using the ineger sequence with_sequence, in this example 1 through 5.

Running this playbook results in the follow output from Ansible:

$ ansible-playbook -i hosts sitex.yml

PLAY [localhost] **************************************************************

GATHERING FACTS ***************************************************************
ok: [localhost]

TASK: [run docker containers] *************************************************
changed: [localhost] => (item=1)
changed: [localhost] => (item=2)
changed: [localhost] => (item=3)
changed: [localhost] => (item=4)
changed: [localhost] => (item=5)

PLAY RECAP ********************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0

Upon Ansible running this playbook, there should be 5 containers running:

$ docker ps
CONTAINER ID  IMAGE                   COMMAND               CREATED        STATUS        PORTS                    NAMES
cd0401b8027e  capttofu/apache:latest  /usr/local/sbin/entr  3 seconds ago  Up 3 seconds  22/tcp, 80/tcp, 443/tcp  container_5
aacf1df11a19  capttofu/apache:latest  /usr/local/sbin/entr  3 seconds ago  Up 3 seconds  22/tcp, 80/tcp, 443/tcp  container_4
29925f68b768  capttofu/apache:latest  /usr/local/sbin/entr  4 seconds ago  Up 3 seconds  22/tcp, 80/tcp, 443/tcp  container_3
125f6c6bbfc0  capttofu/apache:latest  /usr/local/sbin/entr  4 seconds ago  Up 4 seconds  22/tcp, 80/tcp, 443/tcp  container_2
82d714bf1fa3  capttofu/apache:latest  /usr/local/sbin/entr  4 seconds ago  Up 4 seconds  22/tcp, 80/tcp, 443/tcp  container_1

This is a very simple example of using the [Ansible Docker plugin][ansible_docker_plugin] to introduce the reader to how simple it is to use Ansible for provisioning Docker containers. A more thorough and interesting example will be demonstrated on a later post that will revisit some of the material covered in the presentation the author recently gave at AnsibleFest NYC 2014.

Summary

This blog post introduced the reader to Ansible, describing the various Ansible Docker modules and plugins, and provided a demonstration of the basics of using Ansible with Docker with the Ansible Docker module. Subsequent blog posts will cover the Ansible docker_image module, docker_facts module and the Ansible docker dynamic inventory plugin.

comments powered by Disqus