Ansible Best Practices and Examples

Ansible is a powerful automation tool that can significantly improve your infrastructure management and application deployment processes. However, to get the most out of Ansible, it’s crucial to follow best practices. This guide will walk you through key best practices and provide practical examples.

1. Directory Structure

A well-organized directory structure is essential for maintaining a scalable Ansible project.

Best Practice:

Use a standard directory layout to organize your Ansible files.

Example:

ansible-project/
├── ansible.cfg
├── inventory/
│   ├── production
│   └── staging
├── group_vars/
│   ├── all.yml
│   └── webservers.yml
├── host_vars/
│   └── web1.example.com.yml
├── roles/
│   ├── common/
│   ├── webserver/
│   └── database/
└── playbooks/
    ├── site.yml
    ├── webserver.yml
    └── database.yml

2. Use Roles

Roles allow you to organize tasks, variables, and handlers into reusable components.

Best Practice:

Break down your automation tasks into roles for better modularity and reusability.

Example:

Creating a “webserver” role:

roles/webserver/
├── tasks/
│   └── main.yml
├── handlers/
│   └── main.yml
├── templates/
│   └── nginx.conf.j2
└── vars/
    └── main.yml

Content of roles/webserver/tasks/main.yml:

---
- name: Install Nginx
  apt:
    name: nginx
    state: present

- name: Copy Nginx config file
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  notify: Restart Nginx

- name: Ensure Nginx is running
  service:
    name: nginx
    state: started
    enabled: yes

3. Use Variables

Variables make your playbooks more flexible and reusable across different environments.

Best Practice:

Use group_vars and host_vars to manage variables for different groups of hosts or specific hosts.

Example:

In group_vars/webservers.yml:

---
nginx_port: 80
nginx_worker_processes: 4

Then in your tasks:

- name: Configure Nginx
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  vars:
    port: "{{ nginx_port }}"
    worker_processes: "{{ nginx_worker_processes }}"

4. Use Vault for Sensitive Data

Never store passwords or sensitive data in plain text.

Best Practice:

Use Ansible Vault to encrypt sensitive data.

Example:

Encrypting a file:

ansible-vault encrypt group_vars/all/vault.yml

Content of group_vars/all/vault.yml:

---
db_password: supersecret123

Using the encrypted variable in a playbook:

- name: Configure database
  mysql_user:
    name: myapp
    password: "{{ db_password }}"

Run the playbook with:

ansible-playbook site.yml --ask-vault-pass

5. Use Tags

Tags allow you to run specific parts of a playbook.

Best Practice:

Use tags to categorize and selectively run tasks.

Example:

In your playbook:

- name: Install web server
  apt:
    name: nginx
    state: present
  tags: 
    - webserver
    - installation

- name: Start web server
  service:
    name: nginx
    state: started
  tags:
    - webserver
    - service

Run only tasks tagged “webserver”:

ansible-playbook site.yml --tags webserver

6. Use Handlers for Triggering Services

Handlers are tasks that only run when notified by other tasks.

Best Practice:

Use handlers for restarting services or triggering actions that should only happen once, regardless of how many tasks notify them.

Example:

In roles/webserver/handlers/main.yml:

---
- name: Restart Nginx
  service:
    name: nginx
    state: restarted

In your tasks:

- name: Copy Nginx config file
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  notify: Restart Nginx

7. Use Playbook Includes and Imports

For complex deployments, break your playbooks into smaller, manageable files.

Best Practice:

Use include_tasks for dynamic inclusions and import_playbook for static imports.

Example:

In site.yml:

---
- name: Configure webservers
  hosts: webservers
  roles:
    - common
    - webserver

- import_playbook: database.yml

8. Use Ansible-lint

Ansible-lint helps maintain consistent style and avoid common pitfalls.

Best Practice:

Run ansible-lint regularly on your playbooks and roles.

Example:

Install ansible-lint:

pip install ansible-lint

Run it on your project:

ansible-lint playbooks/*.yml roles/*

9. Use Version Control

Always keep your Ansible code in version control.

Best Practice:

Use Git to track changes to your Ansible code.

Example:

Initialize a Git repository:

git init
git add .
git commit -m "Initial commit of Ansible code"

10. Test Your Playbooks

Always test your playbooks before running them on production systems.

Best Practice:

Use tools like Molecule for testing roles, and maintain a staging environment that mirrors production.

Example:

Initialize a Molecule test for a role:

cd roles/webserver
molecule init scenario -r webserver -d docker

This creates a basic test setup for your webserver role using Docker.

By following these best practices and examples, you can create more maintainable, efficient, and reliable Ansible playbooks. Remember, the key to successful Ansible usage is consistency, modularity, and thorough testing.