Infrastructure automation is a cornerstone of modern DevOps. Ansible is a powerful, agentless tool for automating server provisioning, configuration, and application deployment. In this comprehensive guide, we’ll explore best practices, real-world examples, and advanced techniques for mastering Ansible.
Why Ansible?
Ansible has become the go-to automation tool for DevOps engineers worldwide. Here’s why:
- Agentless Architecture: Uses SSH for Linux/Unix and WinRM for Windows, no agent required on managed nodes
- Idempotent Operations: Ensures repeatable, predictable results without unintended side effects
- Modular Design: Roles and playbooks promote code reuse and maintainability
- Simple YAML Syntax: Easy to learn and read, lowering the barrier to entry
- Extensive Module Library: 5000+ modules covering cloud providers, databases, networking, and more
- Active Community: Large ecosystem with Galaxy roles and continuous improvements
Getting Started: Installation & Setup
First, let’s set up Ansible on your control node:
# Install Ansible on Ubuntu/Debian
sudo apt update
sudo apt install -y software-properties-common
sudo add-apt-repository --yes --update ppa:ansible/ansible
sudo apt install -y ansible
# Verify installation
ansible --version
# Install Ansible on CentOS/RHEL
sudo yum install -y epel-release
sudo yum install -y ansible
# Install using Python pip
pip3 install ansible
Basic Inventory Configuration
Create an inventory file to define your managed hosts:
# inventory/hosts.ini
[webservers]
web1.example.com ansible_host=192.168.1.10
web2.example.com ansible_host=192.168.1.11
[databases]
db1.example.com ansible_host=192.168.1.20
db2.example.com ansible_host=192.168.1.21
[production:children]
webservers
databases
[all:vars]
ansible_user=ansible
ansible_ssh_private_key_file=~/.ssh/id_rsa
ansible_python_interpreter=/usr/bin/python3
Real-World Example: Provisioning a Web Server
Let’s create a complete playbook to provision and configure an Nginx web server with SSL:
# playbooks/webserver.yml
---
- name: Configure Nginx Web Server with SSL
hosts: webservers
become: yes
vars:
nginx_port: 80
nginx_ssl_port: 443
domain_name: example.com
ssl_certificate_path: /etc/ssl/certs
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
when: ansible_os_family == "Debian"
- name: Install Nginx
apt:
name: nginx
state: present
notify: restart nginx
- name: Install SSL certificates
copy:
src: ""
dest: ""
mode: '0644'
loop:
- { src: 'files/.crt', dest: '/.crt' }
- { src: 'files/.key', dest: '/.key' }
notify: restart nginx
- name: Deploy Nginx configuration
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/
mode: '0644'
notify: reload nginx
- name: Enable site configuration
file:
src: /etc/nginx/sites-available/
dest: /etc/nginx/sites-enabled/
state: link
notify: reload nginx
- name: Ensure Nginx is running
service:
name: nginx
state: started
enabled: yes
- name: Configure firewall
ufw:
rule: allow
port: ""
proto: tcp
loop:
- ""
- ""
handlers:
- name: restart nginx
service:
name: nginx
state: restarted
- name: reload nginx
service:
name: nginx
state: reloaded
Nginx Configuration Template
Create a Jinja2 template for dynamic configuration:
# templates/nginx.conf.j2
server {
listen ;
listen [::]:;
server_name www.;
# Redirect HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen ssl http2;
listen [::]: ssl http2;
server_name www.;
# SSL Configuration
ssl_certificate /.crt;
ssl_certificate_key /.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Security Headers
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
root /var/www//html;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
# Logging
access_log /var/log/nginx/_access.log;
error_log /var/log/nginx/_error.log;
}
Best Practices: Organizing with Roles
For complex projects, use Ansible roles for better organization:
# Create role structure
ansible-galaxy init roles/webserver
# Directory structure
roles/webserver/
├── defaults/
│ └── main.yml # Default variables
├── files/ # Static files
├── handlers/
│ └── main.yml # Handlers
├── meta/
│ └── main.yml # Role dependencies
├── tasks/
│ └── main.yml # Main tasks
├── templates/ # Jinja2 templates
├── tests/ # Test playbooks
└── vars/
└── main.yml # Role variables
Example role-based playbook:
# playbooks/site.yml
---
- name: Configure all servers
hosts: all
roles:
- common
- security
- name: Configure web servers
hosts: webservers
roles:
- nginx
- ssl
- monitoring
- name: Configure database servers
hosts: databases
roles:
- postgresql
- backup
- monitoring
Security: Using Ansible Vault
Never store sensitive data in plain text. Use Ansible Vault:
# Create encrypted variable file
ansible-vault create group_vars/production/vault.yml
# Edit encrypted file
ansible-vault edit group_vars/production/vault.yml
# Encrypt existing file
ansible-vault encrypt secrets.yml
# Decrypt file
ansible-vault decrypt secrets.yml
# Run playbook with vault password
ansible-playbook site.yml --ask-vault-pass
# Use vault password file
ansible-playbook site.yml --vault-password-file ~/.vault_pass.txt
Example vault content:
# group_vars/production/vault.yml (encrypted)
vault_db_password: "SuperSecurePassword123!"
vault_api_key: "sk-1234567890abcdef"
vault_ssh_private_key: |
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
-----END RSA PRIVATE KEY-----
Reference vault variables in your playbooks:
---
- name: Deploy application with secrets
hosts: webservers
vars:
db_password: ""
api_key: ""
tasks:
- name: Configure application
template:
src: app_config.j2
dest: /etc/app/config.yml
Advanced Techniques
Dynamic Inventory for AWS
Use dynamic inventory to automatically discover EC2 instances:
# Install AWS plugin
pip3 install boto3 botocore
# Create inventory plugin configuration
cat > inventory/aws_ec2.yml <<EOF
plugin: aws_ec2
regions:
- us-east-1
- us-west-2
filters:
tag:Environment: production
keyed_groups:
- key: tags.Role
prefix: role
- key: tags.Environment
prefix: env
hostnames:
- private-ip-address
compose:
ansible_host: private_ip_address
EOF
# Test dynamic inventory
ansible-inventory -i inventory/aws_ec2.yml --graph
Testing with Molecule
Ensure your roles work correctly before deployment:
# Install Molecule
pip3 install molecule molecule-docker
# Initialize molecule in role directory
cd roles/webserver
molecule init scenario --driver-name docker
# Run tests
molecule test
# Test sequence breakdown
molecule create # Create test instance
molecule converge # Run playbook
molecule verify # Run verification
molecule destroy # Cleanup
Performance Optimization
Speed up playbook execution:
# ansible.cfg
[defaults]
forks = 20 # Parallel execution
gathering = smart # Smart fact gathering
fact_caching = jsonfile # Cache facts
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 3600 # Cache for 1 hour
host_key_checking = False
pipelining = True # SSH pipelining
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
Monitoring & Logging
Integrate with monitoring tools:
---
- name: Setup monitoring
hosts: all
roles:
- prometheus_node_exporter
tasks:
- name: Install Filebeat for log shipping
apt:
name: filebeat
state: present
- name: Configure Filebeat
template:
src: filebeat.yml.j2
dest: /etc/filebeat/filebeat.yml
notify: restart filebeat
- name: Enable and start Filebeat
systemd:
name: filebeat
enabled: yes
state: started
Troubleshooting Tips
Common debugging techniques:
# Check syntax
ansible-playbook playbook.yml --syntax-check
# Dry run (check mode)
ansible-playbook playbook.yml --check
# Step through playbook
ansible-playbook playbook.yml --step
# Verbose output
ansible-playbook playbook.yml -vvv
# Debug specific task
ansible-playbook playbook.yml --tags "debug" -vvv
# List all tasks
ansible-playbook playbook.yml --list-tasks
# List all hosts
ansible-playbook playbook.yml --list-hosts
Conclusion
Ansible is a powerful automation tool that scales from simple tasks to complex multi-tier applications. By following these best practices:
✅ Use roles for modular, reusable code
✅ Store secrets securely with Ansible Vault
✅ Test playbooks with Molecule before production
✅ Leverage dynamic inventory for cloud environments
✅ Implement proper error handling and idempotency
✅ Document your playbooks and roles thoroughly
You’ll build robust, maintainable automation that accelerates your infrastructure operations.
Additional Resources
Have questions or feedback? Feel free to reach out or leave a comment below!