Mastering Ansible: Complete Guide to Installation and Configuration on Ubuntu

Ansible has become the DevOps tool of choice for automating the configuration management and deployment of Linux infrastructure. This step-by-step guide will teach you how to install the latest version of Ansible on an Ubuntu control node, configure Ansible to manage nodes, and use playbooks to simplify IT orchestration.

Overview

We will cover the essentials of Ansible including:

  • Installing and upgrading Ansible on Ubuntu 20.04/18.04
  • Managing nodes by setting up inventories, SSH keys, and privilege escalation
  • Executing ad-hoc commands to quickly administer tasks on remote nodes
  • Creating playbooks to automate entire multi-tier infrastructure deployments
  • Troubleshooting connectivity issues, syntax errors, and other problems

By the end, you’ll be able to leverage Ansible to simplify and standardize the configuration, management and deployment of a typical IT environment spanning web servers, databases, load balancers, networks, storage systems, and more.

Prerequisites

Before installing Ansible, let‘s go over the base requirements for smooth performance:

Control Node

The control node is the central management server where Ansible will be installed and hosted.

  • Operating System:
    • Ubuntu 20.04 LTS (Focal Fossa) recommended
    • Ubuntu 18.04 LTS (Bionic Beaver) also supported
    • RHEL/CentOS 7/8 and Debian also compatible
  • Hardware:
    • 2 GB of RAM minimum
    • 2 vCPU cores minimum
    • Storage: 8 GB SSD
  • Python:
    • Python 3.8 or higher
    • Python 3.6+ also works
    • pip installed

Managed Nodes

The managed nodes are the remote Linux servers you wish Ansible to connect to and configure.

  • OS Support:
    • Ubuntu, Debian
    • RHEL, CentOS, AlmaLinux, Rocky Linux
    • Arch Linux, Amazon Linux also supported
  • Hardware:
    • 1 GB of RAM minimum
    • Any wired/wireless network
  • Python 2 (version 2.7 or higher) or Python 3 (version 3.5 and higher)

In our guide, the control node will run Ubuntu 20.04 with Python 3.8. Managed nodes can run any modern Linux distro like CentOS 8, etc.

Having covered the prerequisites, let‘s move on to installing Ansible.

Step 1 – Install Ansible on Ubuntu

We will install the latest Ansible release (version 5.1 as of this writing) using apt on an Ubuntu 20.04 LTS control node.

First, update the local apt package index and install the pre-requisites:

sudo apt update
sudo apt install software-properties-common -y

Next, add the official Ansible PPA repository:

sudo add-apt-repository --yes --update ppa:ansible/ansible

Finally, install ansible:

sudo apt install ansible -y

To install Ansible on Ubuntu 18.04, the steps are identical.

Verify you have Ansible 5.1+:

ansible --version  
# ansible 2.10.7
# ansible 5.1.0

Upgrading existing versions of Ansible to the latest is also straightforward:

sudo apt update
sudo apt install ansible -y

With Ansible now ready to go, let‘s configure everything needed to start managing nodes.

Step 2 – Configure Ansible and Managed Nodes

After installation, we need to set up inventories, SSH keys, privilege escalation and other elements to allow Ansible to contact and execute commands on remote Linux systems.

Inventory File

The inventory file provides Ansible with data on all the servers or devices it can connect to and manage. This includes IP addresses, credentials and grouping into tiers like databases, web, etc.

Let‘s create a sample inventory at /etc/ansible/hosts to define our nodes:

[web]
web1.techdomi.com ansible_host=192.168.1.5 
web2.techdomi.com ansible_host=192.168.1.6

[db] 
db1.techdomi.com ansible_host=192.168.1.7

[lb]
lb1.techdomi.com ansible_host=192.168.1.8 

We have divided our nodes into 3 tiers – web servers, database servers and load balancers. The ansible_host lines map the domain names we want to use for each server to the actual IP addresses Ansible will use to connect to them.

To authenticate over SSH, Ansible relies on system usernames and either passwords or preferably SSH keys.

SSH Keys

SSH keys allow passwordless authentication for Ansible tasks. First generate an ssh key pair:

ssh-keygen -t rsa -b 4096

Then copy the public key over to each node Ansible will manage:

ssh-copy-id john@web1
ssh-copy-id john@web2 
ssh-copy-id john@db1
ssh-copy-id john@lb1

This will enable the john user account on the Ansible control node to SSH into each of the servers without supplying a password.

Make sure to apply strict permissions like 0400 or 0600 on sensitive SSH credentials.

For enterprise environments, use secrets management vaults to securely distribute and rotate SSH keys.

Privilege Escalation

When executing playbooks, Ansible connects via your SSH user. Often tasks like installing packages require root or sudo privileges.

We can configure sudo access on managed nodes:

# Visudo 
john ALL=(ALL)       NOPASSWD: ALL

# Validate sudo works without password prompt
sudo whoami # root

And enable privilege escalation in the main ansible config at /etc/ansible/ansible.cfg like so:

[privilege_escalation]
become=True
become_method=sudo
become_ask_pass=False

This allows playbooks to use become: yes to privileged commands on remote managed nodes when required.

Environment Isolation

Ansible relies on having a compatible Python interpreter available on managed nodes. It‘s good practice to isolate app dependencies in virtual environments.

First create and activate a venv on the control node:

python3 -m venv ansible-venv
source ansible-venv/bin/activate

Next install ansible inside it:

pip install ansible

ansible --version
# ansible 5.1.0
# ...

The virtual environment provides an encapsulated bubble for Ansible and its libraries without conflicting with operating system-level packages.

With the core configuration covered, let‘s test connectivity with our nodes.

Step 3 – Test Connectivity with Managed Nodes

Before running playbooks, we should validate Ansible can contact the inventory of servers:

ansible all -m ping 

This will ping every host grouped under [web] [db] [lb] in our inventory:

web1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

db1 | SUCCESS => {
    "changed": false,
    "ping": "pong"  
}

lb1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Seeing the pong response for all nodes indicates Ansible can successfully SSH into each system and execute Python code.

If any node fails, double check the SSH credentials supplied, inventory entries and network firewall rules. Test SSH access manually from command line as well to isolate the issue.

Now let‘s run some ad-hoc commands before diving into Ansible playbooks.

Step 4 – Running Ad-Hoc Commands

Ansible ad-hoc commands enable running Ansible modules directly from the command line instead of needing dedicated playbooks. They are useful for quick admin tasks across managed nodes.

See the uptime status across web servers:

ansible web -a "uptime" 

web1 | CHANGED | rc=0 >>
 13:10:26 up 12 days, 19:44,  1 user,  load average: 0.00, 0.01, 0.05

web2 | CHANGED | rc=0 >> 
 13:10:29 up 14 days, 11:32,  1 user,  load average: 0.00, 0.01, 0.05

The command and output runs on both web1 and web2 showing the uptime.

Check memory usage on the database host:

ansible db -b -m shell -a ‘free -m‘

db1 | CHANGED | rc=0 >>
              total        used        free      shared  buff/cache   available
Mem:           7846        1093         198           5        6554        6999
Swap:             0           0           0

The -b flag becomes sudo to report memory stats. Hundreds of modules exist for specific operations like managing users, filesystems, packages, containers, clouds and more.

But Ansible really shines when automating multi-step procedures using playbooks.

Step 5 – Ansible Playbooks

Playbooks provide reusable, scriptable automation policies and workflows for deploying and configuring entire environments. They beats writing tons of shell scripts!

Our sample playbook will deploy and configure a simple NGINX web server:

---
- name: Configure webserver host 
  hosts: web

  tasks:
    - name: Install nginx 
      apt: 
        name: nginx
        state: latest

    - name: Copy custom nginx config  
      template:  
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf

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

This playbook runs sequentially on the [web] inventory group:

  1. Install latest NGINX using apt module
  2. Copy a nginx config template over
  3. Starts and enables the NGINX systemd service

We can execute playbooks like so:

ansible-playbook playbook.yml

Playbooks simplify automating multi-server environments by sequencing tasks needed to achieve the end-goal state regardless of the starting state of systems.

Idempotency ensures if servers are already configured, Ansible will NOT make unnecessary changes. This makes playbooks safe to rerun.

Let‘s explore some key capabilities that make playbooks powerful…

Templating

Playbooks build on top of Ansible facts about managed nodes. We can create configuration template files that substitude in node specific details.

For example, a template nginx.conf.j2 file:

server {
  listen {{ ansible_facts[‘default_ipv4‘][‘address‘] }}; 

  location / {
    proxy_pass http://localhost:3000;
  }
}

Will render a unique config file on each node inserting the correct IP address per host automatically!

No need to manually specify IPs everywhere. Ansbile pulls common facts on all systems it manages.

Handlers

Handlers allow triggering tasks only when key changes occur, similar to if-then constructs:

tasks:
  - name: Install Apache
    apt:
      name: apache2 

  - name: Copy conf file
    template:
      src: apache.conf
      dest: /etc/httpd/conf/httpd.conf
    notify:
     - restart apache   

handlers:    
  - name: restart apache
    service:
      name: apache2
      state: restarted

The handler will restart Apache only if the conf file changes. Otherwise Ansible will correctly avoid unnecessary restarts!

Reusable handlers simplify playbooks quite a bit.

Roles

As playbooks grow, it can help to break them into roles representing multi-tier components:

playbooks/
└── site.yml
roles/
    ├── common
    ├── web
    └── db

And roles provide libraries of standardized automation code to simplify reuse between teams, projects and organizations.

Leveraging roles allows managing infrastructure-as-code in a highly flexible manner.

Now that we have covered core Ansible capabilities, let‘s tackle some common issues you miight encounter…

Troubleshooting Ansible

Here are solutions to some typical errors:

Connectivity Issues

web1 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh...",
    "unreachable": true
}
  • Check SSH access manually works from the control node
  • Validate the proper SSH keys are configured
  • Resolve any network security rules blocking TCP 22 access
  • Recheck inventory ansible_host values match actual IPs

Privilege Escalation Warnings

web1 | FAILED! => {
    "changed": false, 
    "msg": "Escalation prompt missing..."
}
  • Configure become directives in ansible.cfg as outlined earlier
  • Specify privilege escalation permissions at play/task level
  • Add and validate NOPASSWD sudo rules on managed nodes

Playbook Syntax Errors

ERROR! ‘name‘ is not a valid attribute for a Play

The error appears to be in ‘/home/web.yml‘: line 2, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
  • Use online YAML validators to detect issues
  • View verbose -v output for exact line numbers
  • Double check spacing/indentation follows YAML standards

Make sure to become highly familiar with Ansible terminology, syntax standards and runtime output. This will drastically reduce confusion and accelerate troubleshooting.

Conclusion

In this comprehensive guide, we covered Ansible installation, node configuration, ad-hoc commands, playbook usage and troubleshooting common issues.

Key takeways include:

  • Installing Ansible control node with Python environments
  • Managing nodes by tiered inventory groups
  • SSH key and privilege escalation for playbook execution
  • Ad-hoc commands for quick administrative fixes
  • Playbooks for reproducible Infrastructure-as-Code
  • Modularity via templates, handlers and roles
  • Debugging connectivity, permissions and syntax problems

Ansible enables you to completely automate the provisioning and maintenance of infrastructure across clouds, data centers and edge sites.

Check out Ansible Galaxy for pre-made roles and content collections that simplify complex multi-tier orchestration.

I hope you found this guide helpful. Let me know if you have any other questions getting started with leveraging Ansible to streamline your infrastructure workflows!