Orchestration with Ansible

I suspect a lot of people will be expecting an Ansible versus Puppet opening to this section of the chapter. In fact, as mentioned at the end of the previous section, while the two tools have a lot of crossover, their strengths lie in doing two different jobs.

They also work in completely different ways. Rather than going into the details now, let's jump right in and install Ansible and then launch our WordPress containers using an Ansible playbook.

Preparation

Note

Note that if, for any reason, you are not able to work through this section of the chapter, I have recorded a screencast to show you what happens when you launch the Ansible playbook, which can be found at https://asciinema.org/a/39537.

Before launching our containers, we need to do a few things. The first thing is to install Ansible.

If you are running OS X, I would recommend installing Ansible using Homebrew. Homebrew is available at http://brew.sh/ and can be installed with the following single command:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Once you have followed the on-screen prompts, you should be in a position to install Ansible using the following command:

brew install ansible

Now that Ansible is installed, we need to install a certain version of the DigitalOcean Python library. To do this, we need to use the pip command. If you don't have the pip command installed, then you need to run:

sudo easy_install pip

Now that pip is installed, run the following command to install the correct version of the Python library we need:

sudo pip install dopy==0.3.5

The final thing you will need is the name of your DigitalOcean key. The Ansible playbook we are going to run will create one for you and upload it if you don't have one already configured, so if that's the case, you can skip this part.

If you do happen to have one already associated with your DigitalOcean account, then you will name the name of it to launch the two instances and then connect to them.

To find this out, log in to the DigitalOcean control panel at https://cloud.digitalocean.com/ and click on the cog icon on the top right-hand side of the screen and from the menu that pops up, click on the Settings button. Once the settings page loads, click on the Security button, you should then see a list of SSH keys, make a note of the name you want to use:

Preparation

In the preceding example, my SSH key is creatively called Russ Home.

Time to get a copy of the Ansible playbook we are going to be running. The code for this can be found in the chapter06/docker-ansible folder on the GitHub repository for this book, the complete URL is as follows:

https://github.com/russmckendrick/extending-docker/tree/master/chapter06/docker-ansible

Once you have the playbook downloaded, open your terminal and go to the docker-ansible folder. Once in there, run the following command, replacing the DigitalOcean API with your own:

echo 'do_api_token: "sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0"' > group_vars/do.yml
echo 'ssh_key_name: "Your Key Name"' >> group_vars/do.yml

We are now in a position where we can run the playbook, but before we do, remember that this playbook will connect to your DigitalOcean account and launch two instances.

To launch the playbook, run the following command and wait:

ansible-playbook -i hosts site.yml

It will take several minutes to run through the entire process, but what you should have the end of it is two Ubuntu 14.04 Droplets launched in your DigitalOcean account. Each droplet will have the latest version of both Docker and Weave installed, Weave will be configured so that the two hosts can talk to each other.

One droplet will be running our WordPress container and the second will be running our MySQL container, both containers will be talking to each using the cross-host Weave network.

Once the task completes, you will should see something similar to the following screenshot:

Preparation

As you can see, in my case, I can go to http://46.101.4.247 in my browser to start the WordPress installation.

If, for any reason, parts of the installation fail, for example, sometimes droplets can take a little longer to start and won't be available for Ansible to connect to when it tries to SSH to them, then don't worry, you will be able to rerun the Ansible playbook using the following command:

ansible-playbook -i hosts site.yml

Ansible will also work through the entire playbook again, this time, skipping anything that has already been created or actioned.

If you are not working through this example, or have problems, I have recorded an entire run-through of launching the playbook and then rerunning it, you can view this at https://asciinema.org/a/39537.

The playbook

There are quite a few parts of the playbook, as you can see from the following list of folders and files:

├── ansible.cfg
├── group_vars
│   ├── do.yml
│   └── environment.yml
├── hosts
├── roles
│   ├── docker-install
│   │   └── tasks
│   │       └── main.yml
│   ├── docker-mysql
│   │   └── tasks
│   │       └── main.yml
│   ├── docker-wordpress
│   │   └── tasks
│   │       └── main.yml
│   ├── droplet
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       └── dyn.yml.j2
│   ├── weave-connect
│   │   └── tasks
│   │       └── main.yml
│   └── weave-install
│       └── tasks
│           └── main.yml
└── site.yml

The main file we called when launching the playbook was the site.yml file, this defines the order which tasks in defined in the roles folder are executed. Let's take a look at the content of this file and the roles that are being called.

Section one

The file itself is split into four sections, the following first section deals with connecting to DigitalOcean's API from your local machine and launching the two Droplets:

- name: "Provision two droplets in DigitalOcean"
  hosts: localhost
  connection: local
  gather_facts: True
  vars_files:
    - group_vars/environment.yml
    - group_vars/do.yml
  roles:
    - droplet

It loads the both the main environment.yml variables file, this is where we define things such as which region the droplet is being launched in, name of the droplets, size to use, and also which image should be launched.

It also loads the do.yml file which contains your DigitalOcean API key and SSH keyname. If you look into the role task file in the droplet folder, you will see that along with launching the two droplets, it also creates the following three host groups:

  • dockerhosts: This group contains both droplets
  • dockerhost01: This contains our first droplet
  • dockerhost02: This group contains the second droplet

The final action that is taken at this stage is that a file is written to the group_vars folder, which contains the public IP addresses of our two droplets.

Section Two

The next section of the site.yml file deals with the installation of some basic prerequisites, Docker, and Weave on the droplets within the dockerhosts group:

- name: "Install Docker & Weave on our two DigitalOcean hosts"
  hosts: dockerhosts
  remote_user: root
  gather_facts: False
  vars_files:
    - group_vars/environment.yml
  roles:
    - docker-install
    - weave-install

The first role deals with the installation of Docker, let's take a look at what's going within the task file for this role.

First of all, we will install curl using the apt package manager as we will need this later:

- name: install curl
  apt: pkg=curl update_cache=yes

Once curl has been installed, we will start configuring the official Docker APT repository by first adding the keys for the repo:

- name: add docker apt keys
  apt_key: keyserver=p80.pool.sks-keyservers.net id=58118E89F3A912897C070ADBF76221572C52609D

Then, we'll add the actual repository:

- name: update apt
  apt_repository: repo='deb https://apt.dockerproject.org/repo ubuntu-trusty main' state=present

Once the repository has been added, we can do the actual installation of Docker, making sure that we update the cached repository list before the package is installed:

- name: install Docker
  apt: pkg=docker-engine update_cache=yes

Now that Docker is installed, we need to ensure that the Docker daemon has started:

- name: start Docker
  service: name=docker state=started

Now we need to install the tools that Ansible will use to interact with the Docker daemon on our hosts, like Ansible, this is a Python program. To make sure that we can install it, we need to ensure that pip, the Python package manager, is installed:

- name: install pip
  apt:
    pkg: "{{ item }}"
    state: installed
  with_items:
    - python-dev
    - python-pip

Now that we know that pip is installed, we can install the docker-py package:

- name: install docker-py
  pip:
    name: docker-py

This package is a Docker client written in Python and supplied by Docker itself. More details on the client can be found at https://github.com/docker/docker-py.

This ends the first role that is called in the second section of the site.yml file. Now that Docker is installed, it's time to install Weave, this is handled by the weave-install task.

First of all, we download the weave binary from the URL defined in the environment.yml file to the filesystem path that is also defined in the environment.yml file:

- name: download and install weave binary
  get_url: url={{ weave_url }} dest={{ weave_bin }}

Once we have the binary downloaded, we need to see the correct read, write, and execute permissions on the file so that it can be executed:

- name: setup permissions on weave binary  
  file: path={{ weave_bin }} mode="u+rx,g+rx,o+rwx"

Finally, we need to start weave and also pass it a password to enable encryption, the password is also defined in the environment.yml file:

- name: download weave containers and launch with password
  command: weave launch --password {{ weave_password}}
  ignore_errors: true

As you can see, at the end of this part of the task, we are telling Ansible to ignore any errors generated here. This is because, if the playbook was to be launched for a second time and weave was already running, it would complain saying that the weave router was already active. This will stop playbook from progressing any further, as Ansible interprets this message as a critical error.

Due to this, we have to tell Ansible to ignore what it thinks is a critical error here for the playbook to progress pass this stage.

Section three

The next section of the site.yml file performs one last piece of configuration before launching the containers that go to make up our WordPress installation. All of these roles are run on our first droplet:

- name: "Connect the two Weave hosts and start MySQL container"
  hosts: dockerhost01
  remote_user: root
  gather_facts: False
  vars_files:
    - group_vars/environment.yml
  roles:
    - weave-connect
    - docker-mysql

The first role, which is called, connects the two weave networks on the two hosts together:

- include_vars: group_vars/dyn.yml
- name: download weave containers and launch with password
  command: weave connect {{ docker_host_02 }}

As you can see, the variable file that contains the IP address of our two droplets is loaded for the first time here and is used to get the IP address of the second droplet; this file, called dyn.yml, was created by the role that originally launched the two droplets.

Once we have the IP address of the second droplet, the weave connect command is executed and the configuration of the weave network is completed. We can now launch the containers.

The first container that we need to launch is the database container:

- name: start mysql container
  docker:
    name: my-wordpress-database
    image: mysql
    state: started
    net: weave
    dns: ["172.17.0.1"]
    hostname: mysql.weave.local
    env:
      MYSQL_ROOT_PASSWORD: password
    volumes:
       - "database:/var/lib/mysql/"

As you can see, this is quite a similar syntax to Docker Compose files; however, there may be slight differences, so double-check the Docker pages on the Ansible core module documentation site to ensure that you are using the right syntax.

Once the my-wordpress-database container has been started, it means that all the tasks we need to execute on dockerhost01 are completed.

Section four

The final section of the site.yml file connects to our second droplet and then launches the WordPress container:

- name: "Start the Wordpress container"
  hosts: dockerhost02
  remote_user: root
  gather_facts: False
  roles:
    - docker-wordpress

All this role does is launch the WordPress container, again the file has close resemblance to the Docker Compose file:

- include_vars: group_vars/dyn.yml
- name: start wordpress container
  docker:
    name: my-wordpress-app
    image: wordpress
    state: started
    net: weave
    dns: ["172.17.0.1"]
    hostname: wordpress.weave.local
    ports:
      - "80:80"
    env:
      WORDPRESS_DB_HOST: mysql.weave.local:3306
      WORDPRESS_DB_PASSWORD: password
    volumes:
       - "uploads:/var/www/html/wp-content/uploads/"
- debug: msg="You should be able to see a WordPress installation screen by going to http://{{ docker_host_02 }}"

The final debug line prints the message at the end of the playbook run that contains the IP address of the second droplet.

Ansible and Puppet

Like Puppet, Ansible, when used with a playbook like the one we have discussed, can be used as a replacement for Docker Machine and Docker Compose.

However, one thing you may have noticed is that unlike Puppet, we did not install an agent in the target machine.

When you run an Ansible playbook, it is compiled locally, and then the compiled script is pushed to your target servers using SSH and then executed.

This is one of the reasons why, during our playbook run, we have to install the Docker Python library on our two droplets, without which the compiled playbook would not have been able to launch the two containers.

Another important difference between the two tools is that Ansible executes the tasks in the order you define in the playbook.

The Puppet example we worked through wasn't complex enough to really demonstrate why this can be an issue when it comes to running Puppet manifests, but Puppet works using an eventual consistency concept, meaning that it may take a few manifest runs for your configuration to be applied.

It is possible to add requirements to Puppet manifests, for example, requiring XYZ to be executed after ABC has run. However, this can start to cause performance issues if your manifest is quite large; also, you could find yourself in a position where the manifest stops working altogether as Puppet is not able to successfully execute the manifest in the order you are defining.

This is why, in my opinion, Ansible is a lot better when it comes to orchestration than Puppet.

It's situations like this where it really matters that the tasks you have defined are executed in the exact order you need them to run in rather than leaving it up to the tool you are using to figure out the most efficient way of applying the tasks.

To me, this is the reason you should not approach any task with an attitude of "I need to choose one tool and only use that for everything," you should always choose the tool that works for the job you want to do.

This can probably be said for a lot of the tools we are looking at in this chapter; rather than assessing a tool in a "this versus that" manner, we should be asking "this or that" or even "this and that" and not limit ourselves.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset