Ansible and Docker

Docker Container

The following describes how to setup the environment for the docker image that later serves the frosch03.de blog files through an apache2 server.

load .tar docker file

First of all one needs to get the correct docker image. This might be done by loading the docker images tarball.

docker load -i container1.tar 

create data folder for the webserver

Next a folder is needed, that contains the data for the webserver to serve. Also a location for storing .log files is needed. A folder to hold both location is created somewhere.

mkdir home/frosch03/data4webserver 

Also a location for the log files is needed:

mkdir home/frosch03/data4webserver/log 

If needed, you could create a sub structure within the log folder. The content of the log-folder will be accessible from within the docker container from the folder /var/log/apache2.

Finally a folder that contains the data to be served by the webserver is needed:

mkdir home/frosch03/data4webserver/data 

This folder will be mapped onto the containers /var/www/ folder. If you like, you could now copy the data into the data folder. Use the file structure as you will reference it from the apache's site configuration.

Set rights for apache2

In the next step, the user rights of the files must be changed, such that the apache is able to access the documents. This is typically done by setting the group ownership of the files to www-data, or if you like the decimal representation: 33.

sudo chgrp -R 33 /home/frosch03/data4webserver 

Run docker container

Now everything is at it's place such that we now could run the docker container.

docker run -ti --name frosch03_de_testing -p 8023:80 -v /home/frosch03/data4webserver/data:/var/www\\ -v /home/frosch03/data4webserver/log:/var/log/apache2 container1 

Here we give the instance the name frosch03_de_testing. Also the local port 80 is mapped to the external one 8023. Then the two volumes are mapped, the data volume to /var/www and the log one to /var/log/apache2.

Now one has to start the apache server. This is done by spawning a shell within the docker container by:

docker exec -ti frosch03_de_testing bash 

Inside the container, apache could simply be started via the command:

service apache2 start 

Ansible

There are two kinds of things to be explained within the following: 1) There is the definition of computers, that docker containers are deployed to and 2) there are the ansible playbooks that will be used to create new containers.

The machines that will be used as install targets are defined within the file /etc/ansible/hosts.

Hosts

This is a simple file within the ini file format, that just lists machines under a naming section. Here I have a computers DNS name under the section web and the localhost under the section local:

[web]
some.server

[local]
localhost 

So with that I now am able to deploy my docker stuff to web or to local, whatever that means. It does not matter, if there are 20 machines listed beneath web.

Files & Folder Structure

This section gives a rough overview on the file structure I created, in order to do things the ansible way. (Or at least, what I think the ansible way is).

Basically I have a frosch03.de playbook which references two roles, docker and frosch03. The playbook defined within YAML and is stored on the top level.

Both roles come with their own folders, that hold a files and a tasks directory. The files folder contains files, that are used within the role definition e.g. Dockerfile which contains the information on how to create the docker container.

The tasks folder contains YAML definitions that tell ansible what to do. This is the simple folder structure for the frosch03.de docker container:

frosch03.de.yml
roles/
  docker/
    files/
    tasks/
      docker.arch.yml
      main.yml
      docker.ubuntu.yml

    frosch03/
    files/
      Dockerfile
      frosch03blogdata.tar.bz2
      frosch03.de.conf
    tasks/
      main.yml

With that said, lets have a look at the different roles.

Docker

The docker role contains of just YAML definitions within the tasks folder. There is the main definition within main.yml.

The first three definitions include more definitions, depending upon the operation system that the ansible will connect to. So this depends not on the machine from where ansible is used to do something remotely, but on the os of the machine under installation.

- include: docker.ubuntu.yml when: "ansible_os_family == 'Ubuntu'"
- include: docker.ubuntu.yml when: "ansible_os_family == 'Debian'"
- include: docker.arch.yml when: "ansible_os_family == 'Archlinux'" 

There are three operation system that are discriminated, Ubuntu, Debian and Archlinux. As Ubuntu and Debian are pretty similar, they share the same docker definitions. For the Arch side, a different definition file is used. Both define their way on how to install docker with it's dependencies on the remote machine.

The remote system is equipped with docker and python-setuputils right after the os specific tasks are completed. After that, pip is installed in both cases with easyinstall and is then used to install docker-py.

- name: Install pip easy_install: name=pip state=latest executable=easy_install-2.7
- name: Install docker-py pip: name: docker-py 

Last but not least, the main.yml defines that docker is started on boot and with the last task docker is finally started.

- name: Start docker on boot service: name=docker state=started enabled=yes
- name: Make sure docker is running service: name: docker state: started 

Frosch03

1. Files

The frosch03 role contains the data needed in order to set up the frosch03.de blog. The files folder contains the Dockerfile which set's up a ubuntu based server, equipped with an apache2 webserver. It also contains the configuration file for the apache2 server called frosch03.de.conf.

Last but not least, a tarball is included, that holds the data of the webserver. In this case the tarball contains a folder that holds a data and a log folder. The data folder itself holds the files that should be served by the apache. The log folder contains the locations, where apache will put it's log files into.

2. Tasks

There is only the main task defined within main.yml. This task describes the sets up of the servers docker container, together the data/log storage locations.

- name: Create necessary directories file: path: "{{ item }}" mode: 0755 state: directory with_items:
- "/tmp/frosch03.de-ghost/frosch03_ghost"
- "/tmp/frosch03.de-ghost/data" 

With the next step, the files Dockerfile and frosch03.de.conf are copied to the storage location of where the docker image is build. (Here this is done inside the temporary folder)

- name: Copy frosch03_ghost files to /tmp/frosch03.de-ghost/frosch03_ghost copy: src: "{{ item }}" dest: "/tmp/frosch03.de-ghost/frosch03_ghost/{{ item }}" with_items:
- "Dockerfile"
- "frosch03.de.conf" 

The docker image itself is build right after that. Its done from exactly the location, the files have been copied to.

- name: Build frosch03.de-ghost container shell: docker build -t frosch03_ghost . args: chdir: /tmp/frosch03.de-ghost/frosch03_ghost 

The newly created docker image is called frosch03_ghost.

After that's done, the data for apache to serve will be copied into the folders, that are given the docker container as volumes. So the first tasks copies the tarball into that location.

Then the tarball is extracted into that folders sub folder data. Note: The group of the extracted files is set to 33 as this corresponds to www-data (or sometimes http). Apache runs typically with this group and therefore the rights are needed in order to allow apache to access the extracted data later on.

- name: Copy the data to the server copy: src: "frosch03_blog_data.tar.bz2" dest: "/tmp/frosch03.de-ghost/frosch03_blog_data.tar.bz2"
- name: Extract frosch03.de-ghost data unarchive: src=/tmp/frosch03.de-ghost/frosch03_blog_data.tar.bz2 dest=/tmp/frosch03.de-ghost/data group=33 

In the last two tasks, the docker container is run. The first one tries to restart the docker container if it exists; the second one starts the docker container if it wasn't existing.

- name: Restart frosch03.de-Ghost docker: image: frosch03_ghost name: frosch03_de ports: 8023:80 state: restarted volumes: /tmp/frosch03.de-ghost/data/data:/var/www,/tmp/frosch03.de-ghost/data/log:/var/log/apache2

- name: Make sure that frosch03.de-Ghost is running in the end docker: image: frosch03_ghost name: frosch03_de ports: 8023:80 state: running detach: True volumes: /tmp/frosch03.de-ghost/data/data:/var/www,/tmp/frosch03.de-ghost/data/log:/var/log/apache2