Skip to main content

Overview

Currently the structure of Ansible is a mess and no-one seems to have a clue. The idea of this is to create a working example of how the structure can be defined, and hopefully 'automated' to create some consistency.

The Role of the Business Service

The reason that Ansible is being used is to manage a system that provides some service to the business. It is this business service that generates money to pay the bills, or is a cost sink-hole that needs to be managed. Using the business service as the focal point should simplify the project structure and remove any ambiguity. It is either a part of the solution, or not.

As an example, The Static Content Hosting business service consists of the following components:

  • Web Server
  • GitLab Runner
  • NFS Server

Each of these components map to an ansible role:

  • web_server
  • gitlab_runner
  • nfs_server
tip

Replacing whitespaces with underscores (_) is recommended, as galaxy uses underscores and would convert them regardless. Keep it consistent.

As a directory listing, it would look something like:

static_content_hosting/
├── collections/
│ └── requirements.yml
├── inventory/
│ └── hosts
├── roles/
│ ├── web_server/
│ │ ├── defaults/
│ │ │ └── main.yml
│ │ ├── handlers/
│ │ │ └── main.yml
│ │ ├── meta/
│ │ │ └── main.yml
│ │ ├── tasks/
│ │ │ ├── main.yml
│ │ │ └── configure_firewall.yml
│ │ ├── templates/
│ │ │ └── nginx.conf.j2
│ │ └── vars/
│ │ └── main.yml
│ ├── gitlab_runner/
│ │ ├── defaults/
│ │ │ └── main.yml
│ │ ├── handlers/
│ │ │ └── main.yml
│ │ ├── meta/
│ │ │ └── main.yml
│ │ ├── tasks/
│ │ │ ├── main.yml
│ │ └── vars/
│ │ └── main.yml
│ └── nfs_server/
│ ├── defaults/
│ │ └── main.yml
│ ├── handlers/
│ │ └── main.yml
│ ├── meta/
│ │ └── main.yml
│ ├── tasks/
│ │ ├── main.yml
│ └── vars/
│ └── main.yml
├── group_vars/
│ ├── all.yml
│ └── web_servers.yml
│ └── gitlab_runners.yml
├── hosts
├── site.yml
└── README.md

Breaking It Down

Alot of Ansibles structures and conventions have been borrowed from other automation tools. In puppet land, this project is similar to a control repository:

AnsiblePuppet
site.ymlmanifests/site.pp
rolessite/roles/**.pp
collections/requirements.ymlPuppetfile
hostsdata/hosts/
group_varsdata/hostgroups/

A major difference is with the structure of configuration data. Puppet allows the data/** directory structure to be defined by the user throught the hiera.yaml file. However, Ansible inventory has a static structure that is exensible via dynamic inventory (inventory/**). Dynamic inventory is similar to Puppet's External Node Classifier, with both being able to provide additional information about the hosts.

Unlike the mono-repo style control repository typically used by Puppet, the business service based structure for Ansible does not share any configuration. Technically it is possible, but it would cause more problems than it would solve. Testing the configuration data of a Puppet control repository is relatively trivial, whereas testing Ansible host and group variables are configured correctly is basically impossible. Hence, Ansible projects need to be completely independent, otherwise bad things will happen.

site.yml

The site.yml is the mapping of the server groups to the roles happen.

site.yml
---
- name: Apply a common base configuration to all hosts
hosts: all
become: yes
roles:
- base

- name: Configure and deploy the web servers
hosts: web_servers
become: yes
roles:
- web_server

- name: Configure and deploy the gitlab runners
hosts: gitlab_runners
become: yes
roles:
- gitlab_runner

- name: Configure and deploy the NFS servers
hosts: nfs_servers
become: yes
roles:
- nfs_server

Web Server Role

The web_server role leaves the heavy lifting to the hccp collections nginx_plus role. In this example there is only a single included role, but additional roles could be added if needed. It is similar to a puppet role class, however puppet has the concept of roles and profiles, which helps with separation of concerns for the code.

roles/web_server/tasks/main.yml
---
- name: Install and configure Nginx
ansible.builtin.include_role:
name: hccp.nginx_plus
danger

There are some terms in Ansible that are overloaded and can be confusing, or at least to me. The term role is one that takes on many meanings. A role makes sense within the site.yml file, as it is describing the role of a server in a given site. But a role being made up of other roles, and then roles are often described as actions, verbs. Huh? I suppose that Ansible follows the Judge, Jury and Executioner idiom. It can do whatever it likes.

Re-using code through Collections

The collections/requirements.yml file can pull in collections from git,URL, or galaxy. Again, this is similar to Puppets Puppetfile definition that contains reusable Puppet modules.

Using Ansible collections is relatively straight forward.

collections/requirements.yml
---
collections:
- name: https://github.com/org/repo.git
type: git
version: 1.0.0

The downside of git based collections is that the git URL may not indicate what collection is being referenced. Instead the galaxy.yml file contained within the git project defines the name and version details that will be used by Ansible when installing the collection.

tip

Currently, Execution Environments are being ignored for standalone Ansible runs as they add complexity without any real benefit. Given the gitlab-runner can execute containers, there may already be some examples of using execution environments with GitLab.