Skip to content

πŸš€ Ansible: architecture and practical techniques

πŸ—οΈ Ansible architecture

What I like in Ansible is simplicity and agentless architecture:

  • πŸ“‘ agentless model - uses existing SSH (Linux/Unix) or WinRM (Windows) protocols to communicate with managed hosts
  • 🧩 modular structure - everything is based on declarative YAML files: playbooks, roles and inventory
  • ♾️ idempotency - operations can be safely repeated without risk of duplicate changes

πŸ“‚ Role-based project organization

Example of role based structure:

ansible-project/
β”œβ”€β”€ πŸ“ inventory/
β”‚   β”œβ”€β”€ πŸ“„ development.yml      # Dev environment definition
β”‚   └── πŸ“„ production.yml       # Production environment definition
β”œβ”€β”€ πŸ“ group_vars/
β”‚   β”œβ”€β”€ πŸ“„ all.yml              # Variables common to all groups
β”‚   β”œβ”€β”€ πŸ“„ webservers.yml       # Variables for webservers group
β”‚   └── πŸ“„ dbservers.yml        # Variables for dbservers group
β”œβ”€β”€ πŸ“ host_vars/
β”‚   └── πŸ“„ specific-host.yml    # Variables for specific host
β”œβ”€β”€ πŸ“ roles/
β”‚   β”œβ”€β”€ πŸ“ common/              # Common role for all servers
β”‚   β”‚   β”œβ”€β”€ πŸ“ tasks/
β”‚   β”‚   β”‚   └── πŸ“„ main.yml     # Main role tasks
β”‚   β”‚   β”œβ”€β”€ πŸ“ handlers/
β”‚   β”‚   β”‚   └── πŸ“„ main.yml     # Handlers
β”‚   β”‚   β”œβ”€β”€ πŸ“ templates/
β”‚   β”‚   β”‚   └── πŸ“„ ntp.conf.j2  # Configuration file templates
β”‚   β”‚   β”œβ”€β”€ πŸ“ files/
β”‚   β”‚   β”‚   └── πŸ“„ banner.txt   # Static files
β”‚   β”‚   β”œβ”€β”€ πŸ“ vars/
β”‚   β”‚   β”‚   └── πŸ“„ main.yml     # Internal role variables
β”‚   β”‚   └── πŸ“ defaults/
β”‚   β”‚       └── πŸ“„ main.yml     # Default variable values
β”‚   β”œβ”€β”€ πŸ“ webserver/           # Role for web servers
β”‚   └── πŸ“ database/            # Role for database servers
β”œβ”€β”€ πŸ“„ site.yml                 # Main playbook
β”œβ”€β”€ πŸ“„ webservers.yml           # Playbook for web servers
└── πŸ“„ dbservers.yml            # Playbook for database servers

πŸ’‘ Practical techniques

πŸ”„ Task delegation

Task delegation allows executing specific operations on a different host than the currently processed one:

- name: Register new server in load balancer
    shell: /usr/local/bin/register_node.sh {{ inventory_hostname }}
    delegate_to: load_balancer8
    # This task will execute on the load_balancer8 host

πŸ—ƒοΈ Collections instead of simple playbooks

Don't reinvent the wheel - use ready-made collections from Ansible Galaxy:

ansible-galaxy collection install community.docker

# Usage in playbook
- name: Run PostgreSQL container
    community.docker.docker_container:
        name: db
        image: postgres:13
        ports:
            - "5432:5432"

πŸ” Ansible Vault for sensitive data

# Encrypting password files
ansible-vault encrypt group_vars/all/vault.yml

# Running playbooks with encrypted data
ansible-playbook site.yml --ask-vault-pass

⚑ Performance optimization

- hosts: all
    gather_facts: no  # disable fact gathering when unnecessary
    # or
    gather_subset: network  # gather only selected facts

πŸ•΅οΈ Diff mode

ansible-playbook webservers.yml --diff --check

Combining --diff with --check allows seeing what changes will be made without actually making them.

πŸ”„ Execution strategies

- hosts: webservers
    strategy: free  # parallel task execution without synchronization
    # alternatives: linear (default), debug (interactive)
    tasks:
        # tasks...

πŸ”„ Error handling and loops

- name: Start services
    service:
        name: "{{ item }}"
        state: started
    loop:
        - nginx
        - redis
        - postgresql
    ignore_errors: yes  # continue despite errors
    register: service_result

- name: Notify about errors
    mail:
        to: "admin@foobar.com"
        subject: "Service startup error"
        body: "There was a problem starting services"
    when: service_result.failed

🌟 Advanced role techniques

πŸ“‹ role dependencies

# roles/webserver/meta/main.yml
dependencies:
    - role: common
    - role: security
        vars:
            security_level: high

🧩 Reusable roles with parameters

# playbook.yml
- hosts: webservers
    roles:
        - role: nginx
            vars:
                nginx_port: 80
                ssl_enabled: true

        - role: nginx
            vars:
                nginx_port: 8080
                server_name: "api.foobar.com"

πŸ” Dynamic inventory

Instead of static host lists, use dynamic inventory for cloud infrastructure:

# AWS EC2
ansible-playbook -i inventory_aws_ec2.yml site.yml

# Azure
ansible-playbook -i inventory_azure_rm.yml site.yml

🚦 Control flow - conditional handling

- name: Install distribution-specific packages
    include_tasks: "{{ ansible_os_family | lower }}.yml"
    # automatically loads debian.yml or redhat.yml depending on system

πŸ“ˆ Monitoring and debugging

πŸ” Variable debugging

- name: Display host information
    debug:
        var: ansible_facts
        verbosity: 1  # displays only with -v or higher level

πŸ“Š Callback plugins

# ansible.cfg
[defaults]
callback_whitelist = profile_tasks, timer

This will enable displaying execution time for individual tasks.

πŸ† Best practices

  1. πŸ“¦ maintain small, specialized roles - each role in general should have one specific purpose
  2. πŸ”„ test roles in isolation - use molecule for role testing
  3. πŸ“ document variables - use comments in defaults/main.yml files
  4. πŸ”’ never store credentials in plain text - always use ansible vault or other vault derivative
  5. πŸ”§ use tags for selective execution - ansible-playbook site.yml --tags "configuration,packages"
  6. πŸ§ͺ check changes before deployment - use --check and --diff

πŸš€ Summary

Ansible enables infrastructure automation without excessive complexity. Role-based approach provides modularity, code reuse, and easier management. With task delegation, collections, and execution strategies, you can create flexible and efficient automation solutions.


πŸ”— Useful resources: