Ansible Best Practices explained

In this post, I am going to talk about the best practices and tips working on an Ansible project. I have been using Ansible for more than a year and as the projects grow larger the infrastructure becomes more challenging to maintain. Following the best practices is important in long-term.

If you a DevOps engineer and keen on learning Ansible have a look at Mastering Ansible on Udemy. It is one of my favourite courses on Ansible, as the instructor explains in detail. By the end of this course, you can successfully provision a LAMP stack, install database server and configure a Load Balancer using Ansible.

My top tips and best practices for any Ansible project

1. Reload services instead of restarting upon configuration changes

Reloading a service does not cause the service being inactive, it simply reloads the configuration. When you edit a configuration rather creating a handler which restarts the service, make it reload.

For example, after adding a new virtual host instead of restarting, you could simply reload apache and it will not cause any service disruption.

Reload: Does not stop the service, it reloads the updated configuration

Restart: Stops and then Starts the service. Your users might experience a slight downtime.

- name: reload apache 
  service:
    name: httpd
    state: reloaded

2. Forget yum or apt-get, use the package module

RedHat loves “yum” Debian loves “apt-get”, but I wish they both loved one. Sometimes, you will have a common role which is responsible for installing packages, by using the “package” module you will make your role much more resuable. The package module will execute yum or apt-get based upon the OS it is running.

For example: To install htop on RedHat or Debian

- name: install the latest version of htop
  package:
    name: htop
    state: latest

3. Write meaningful tasks name

A bad task name is one which does not clearly indicates its purpose on the command line. If someone else runs your playbook the name should indicate the purpose and the action of the task.

For example, if you are going to install Apache

A bad task name would be: “install apache”:

- name: install apache
  yum:
    name: httpd
    state: latest

A good task name will be one which is meaningful and defines the purpose:

- name: install the latest version of apache
  yum:
    name: httpd
    state:latest

We can understand that this task will install the latest version of Apache compared to the other.

4. Use vaults to save important data (passwords, SSL certificates ..)

The more you use Ansible, more tasks become automated. You will reach a point, that things such as SSL configuration, database passwords needs to be automated.  This is where Ansible provides the vault module so you could safely save these data and automate it without any risks.

Vault lets you encrypt important data that can be safely saved in your version control system or transfer to another system.

5. Organize your project layout

As your project grows larger, maintaining a clean and maintainable directory layout is important. There are many different best practices for project layout, but I personally try to keep it simple and follow the recommendation on Ansible website its self.

These two are my favourite tips:

  1. Place environment specific variables in its own directory.
  2. Have different inventory for different environments. (this is important for multiple environment configurations)

If you want to learn more, Ross Tuck has a well-explained article on project directory and multi-stage environment management with Ansible.

6. Turn off Gather Facts

When a playbook is executed, Ansible starts collecting facts (data that is stored into variables) about the remote host prior execution. These details include variables from the remote host such as network configuration variables, hostnames so you could use it in your playbook.

This is a time-consuming process, in order to speed up the execution of the playbook we turn gathering facts off.

gather_facts: no

A playbook with gather_facts: false

---
 - hosts: all
   become: true
   gather_facts: false
   tasks:
     - name: restart mysql
       service:
        name: mysql
        state: restarted

The above would execute much faster, as Ansible would skip gathering facts and proceed to the task.

If you need to use the remote host variables in your playbook, you should not turn it off

7. Don’t Hardcode! Use Templates or Variables

Ansible provides a rich set of utilities to make roles dynamic and re-usable. Try to avoid using hardcoded variables and use Templates and variables in your roles. A simple task such as creating a directory SSL certificates can be made more reusable.

In the example below, the path is taken from the SSL_Directory variable. The variable can be passed through the command line or defined in the playbook.

- name: create website directory
  file:
    path: "{{ SSL_DIRECTORY }}"
    state: directory
    group: apache
    owner: apache

8. Separate parameters on its own line

You could write your entire ansible command in a single line, but the preferred way is to separate it. There are some modules that accept more than five parameters, and this can make your code look complex. This is why it is a best practice to write the parameters in its own distinct line.

- name: install the latest nginx
  package: name=nginx state=latest use=no

The above could be written in separate lines

- name: install the latest nginx
  package: 
    name: nginx
    state: latest
    use: no

If you are struggling with YAML, Animosity has written a YAML cheat sheet which is extremely useful for Ansible development

Conclusion

I hope you have enjoyed the post. If you are looking for roles that have been tested and ready to use, have a look at Ansible Galaxy. Ansible Galaxy is similar to Packagist for PHP developers. It hosts roles that can be reusable for any type of projects. I personally use the MySQL role for a fresh MySQL server installation. There are plenty of roles for various projects.