This repository serves as our configuration management repository. In order to keep things in order and maintain consistency, there are some conventions which should be followed.
- Keep this repository client-agnostic. Do not include any environment or client-specific information in this repository. Store all of your environment and client-specific information in the appropriate inventory repository. This allows us to utilize this code-repository across multiple clients, projects and initiatives.
- Write your Ansible roles and playbooks to be idempotent. This means that when you run your Ansible multiple times in succession it will not perform any unnecessary actions repeatedly (0 changes).
- Always test your Ansible in Vagrant or a disposable environment before issuing a merge request, or merging your branch into master. This ensures that we are not "developing in production" and gives us stability and reliability across our production systems.
- Use feature branching when pushing. When you push to the Ansible source repository, push your changes to a feature branch, i.e.
gitlab
and then issue a merge request when you are ready to have your code merged. This allows at least 2 sets of eyes on everything that gets added to the Ansible code base. - Always tag your role tasks with the name of the role. This allows fine-grained targeting of systems with specific roles and playbooks.
- Never target production systems from your local machine, Vagrant or any non-assigned Ansible control nodes or from the command line of a control node. This helps us to ensure that all actions taken against production systems are logged/audited properly; as we orchestrate the execution of Ansible from tools which retain the logs and activities of Ansible.
- Give each task a unique name so that the Ansible execution output is easier to read by humans. Uniqueness of the task name simplifies troubleshooting.
- Never use
ignore_errors
unless you absolutely have to. - Avoid using bare
shell
module as it is always non-idempotent. - Add padding to your variables. For example,
{{hard_to_read}}
and{{ easier_to_read }}
. - Use Pascal Case for boolean. Instead of
true
useTrue
.
inventory-vagrant/
-- Directory of static inventory files and variable files forVagrant
inventory-vagrant/group_vars/
-- Group variablesinventory-vagrant/inventory/
-- Directory of static inventory files forVagrant
library/
-- CustomAnsible modules
libs/
-- Custom libraries and supporting scriptslibs/rundeck-vagrant.yml
-- Example rundeck job for executing Ansible with Rundeck in Vagrantplaybooks/
-- Recipes for configuration and appropriate related actionsplaybooks/roles/
-- Roles and modules under them for which we act upon within Ansiblevagrant-libs/
-- Required files forVagrant
provisioning.gitignore
-- Git ignore file.ansible.cfg
-- Ansible program configuration variables. -- This file should not be modified under most circumstances.localhost
-- Inventory file for running local plays.README.md
-- This file.site.yml
-- Global cookbook for all recipes to be played automatically.Vagrantfile
-- Specification file for Vagrant.
- Execute
ansible-playbook site.yml -e "env=dev"
from the Ansible control node to run Ansible againstdev
environment
env
-- Host environment to target- Check Ansible documentation for additional global variables
The combination of HostType and env is an internal organization scheme that I have developed by utilizing the flexibility of Ansible in order to assist the grouping of hosts together as well as targeting Ansible playbook execution to a limited scope of hosts. This allows us to run the same site.yml
playbook every time, however we can granularly target hosts all the way down to specific tasks if desired. The result of this is much easier automation of Ansible playbook executions as well as removing the burden of needing to know which exact playbook you need to run to perform your desired actions against the infrastructure.
HostType refers to the playbook that you want executed against that host each and every time Ansible is targeted against that host. (i.e. HostType-gitlab
)
env refers to the environment in which you want to target.
The way it works is, when Ansible executes it creates an intersection between those 2 host groups (HostType
and env
) and only executes playbooks where that intersection occurs. For example, if you wanted to install Gitlab on a host in dev
you would simply add your host (i.e. dev1.example.com 10.10.10.10
) to the following host groups in Ansible inventory: gitlab
and dev
. From there you could execute Ansible using the following command: ansible-playbook site.yml -e "env=dev" -t "gitlab"
and the result would be that Ansible would install Gitlab on the dev1.example.com
host in the dev
environment, as well as any other hosts which are defined both in the gitlab
and dev
host groups simultaneously.
There are 2 types of Ansible playbooks which we are currently developing:
- Standard playbooks which perform actions directly on a host
- Docker container based playbooks which manage Docker containers on a host
The reasoning for the distinction between standard
and Docker container
based playbooks is because we may have a service which is installed 2 different ways.
- As a standard service running on a host
- As a Docker container running on a host
The result of this is, we may have 2 roles and playbooks defined for the same service; one for the standard service and another one for the Docker service. If that is the case, we would have a collision of role names. This methodology avoids such scenarios.
- Create a new playbook in
playbooks
prefixed with one of the following prefixes:HostType-
for standard playbooks (i.e.HostType-gitlab.yml
)Docker-
for Docker container based playbooks (i.e.Docker-gitlab.yml
)
- Structure your playbook content like the following examples:
- For standard playbooks:
--- - name: Ansible HostType gitlab Play hosts: "{{ env }}:&gitlab" become: True roles: - gitlab
- For Docker container based playbooks (note the role name is
docker_gitlab
):
--- - name: Ansible HostType docker_gitlab Play hosts: "{{ env }}:&docker_gitlab" become: True roles: - gitlab
- For standard playbooks:
- Include your new playbook at the end of
playbooks/main.yml
- include: HostType-gitlab.yml
- Duplicate the
playbooks/roles/role-skel
directory and follow the instructions inplaybooks/roles/role-skel/README.md
to create your role
- Install Virtualbox (https://www.virtualbox.org/wiki/Downloads) and Vagrant (https://www.vagrantup.com/)
- Inside the root of the repository, on your command line, type:
vagrant up
to bring up the Vagrant machine - After the Vagrant machine comes up, type:
vagrant ssh
to ssh into the Vagrant machine - Run the following command inside the Vagrant machine:
cd /vagrant; ansible-playbook site.yml -e "env=vagrant"
to execute Ansible. You can execute this command each time you want to test your changes. You may also run Ansible in "check mode" by adding the--check
parameter to the end, like so:cd /vagrant; ansible-playbook site.yml -e "env=vagrant" --check
- You can modify the
Vagrantfile
inside the repository to add any additional ports you need forwarded, after doing so you must reload your Vagrant machine (vagrant reload
) to pick up the new changes. Please see the Vagrant docs for further information
Uncomment vagrant
in the inventory file (inventory-vagrant/inventory/vagrant) under any product you'd like to install
- Add
-vvvv
to your Ansible execution to get extremely verbose output - You are able to store your custom roles (for testing purposes) in
/etc/ansible/roles
and they will be automatically picked up (if you don't want to commit them to the repository)