Revolutionizing Server Management with Ansible

Anurag KumarAnurag Kumar
13 min read

In the early days of system administration, handling upgrades, security patches, and software installations was often a manual and tedious task. To make life easier, shell scripts were created, serving as powerful tools that allowed administrators to automate these processes. These scripts were a huge step forward, enabling efficient management of hundreds of servers with minimal effort.

As cloud computing and microservice architectures began to take hold, the number of servers increased, and managing them became complex. The simple shell scripts that once handled everything smoothly now faced new challenges in this expanded landscape.

This transformation led to a new age of automation, where system administrators needed to create more advanced and scalable solutions to meet the needs of modern infrastructure.

Simple scripts were no longer enough to manage the growing number of servers. Instead, they needed better tools and methods to handle the complexity and changes in cloud environments, ensuring everything ran smoothly and efficiently.

The Challenge with Traditional Shell Scripts

Initially, system administrators had to manage a variety of operating systems across their server environments. Each server could be running a different distribution, such as Alpine, Ubuntu, CentOS, or Debian, each with its own unique characteristics.

This diversity created a significant challenge. A shell script designed to perform a task on one distribution might rely on specific commands, file paths, or system behaviors that were not present or behaved differently on another distribution. For instance, a script that worked flawlessly on Ubuntu might fail on Alpine because certain packages were named differently or because the default shell behaved in an unexpected way.

These inconsistencies made it difficult to achieve a uniform and reliable server management process. System administrators had to constantly tweak and adapt their scripts to account for these differences, often resulting in a complex and error-prone codebase. Ensuring that every script worked seamlessly across all environments required extensive testing and maintenance, which consumed valuable time and resources.

This diversity often meant that a script working on one distribution might fail on another. This was a significant hurdle in ensuring consistent and reliable server management.

Configuration Management Tools

To address these challenges, configuration management tools like Puppet, Chef, and Ansible came into the picture. These tools revolutionized the way servers were managed, bringing in consistency, reliability, and scalability. Among these, Ansible has gained significant popularity. But why choose Ansible over others?

Why Ansible?

Push Mechanism Model

Ansible operates on a push mechanism model.

This means you can write an Ansible script on a single instance and push updates to all the worker nodes from there. This centralized approach simplifies management and reduces the risk of errors.

Agentless Architecture

One of Ansible's standout features is its agentless architecture. You don't need to install any additional software on the managed servers.

Just include the DNS or IP addresses of your servers in the inventory file, ensure SSH password-less authentication is configured, and have Python 3 installed (which is standard in most Linux distributions).

Dynamic Inventory

In cloud environments like AWS, where servers can be created and destroyed dynamically, Ansible's dynamic inventory feature is invaluable.

It can automatically detect new servers, ensuring they are immediately managed and configured according to your scripts.

Cross-Platform Support

Ansible supports various operating systems, including Windows, AIX, and all Linux distributions. This cross-platform capability makes it a versatile tool for diverse IT environments.

Simplicity and Readability

Ansible scripts are written in YAML, a human-readable data serialization standard.

This simplicity makes Ansible accessible even to those who may not have a deep programming background, enabling more team members to contribute to and manage automation tasks.

Independent of Cloud Provider

Ansible's functionality is largely independent of which cloud provider is being used. What truly matters for Ansible to work effectively is whether it has access to the target machines. Specifically:

  • Public Accessibility: The target machines need to be accessible over the network.

    This usually means that the machines should be either publicly accessible or reachable via a private network that Ansible can connect to.

  • SSH or WindowsRM Access: For Linux-based systems, Ansible relies on SSH (Secure Shell) for communication.

    The target machines must have SSH enabled and configured to allow Ansible to connect and execute commands.

    For Windows systems, Ansible uses Windows Remote Management (WinRM). The target Windows machines must have WinRM enabled and properly configured to facilitate communication and management.


In this blog, we will focus on using Ansible with a Linux machine to gain hands-on experience. If your current setup involves a different type of machine, you can easily create and access Linux-based EC2 instances on AWS by following these steps: Click here for instructions.

Once you have set up and accessed your EC2 instance, follow the instructions below to proceed:

Creating and Setting Up Servers

In this guide, we'll walk through the process of setting up Ansible in a practical scenario. We will:

  1. Create a Server(ansible_ub): Begin by provisioning a Linux server (for example, an EC2 instance on AWS). Set Up Ansible install and configure Ansible on this server.

  2. Prepare a Target Server(target_instance): Provision another server to be managed by Ansible.

This setup will allow us to demonstrate how to use Ansible to automate tasks and manage configurations across multiple servers.

Setting up ansible on the "ansible_ub" server

sudo apt update
sudo apt install ansible

Configuring Passwordless Authentication Between Instances

To establish passwordless authentication between two instances within the same VPC, we'll use the private key of the target_instance. This guide assumes both instances are running Ubuntu.

  1. Generate SSH Key Pair onansible_ub Instance:

    First, log in to the ansible_ub EC2 instance. Then, generate an SSH key pair by running the following command:

     ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa
    

    This command creates the following files:

    • Private Key: ~/.ssh/id_rsa

    • Public Key: ~/.ssh/id_rsa.pub

  2. Prepare the Public Key for Target Instance:

    Next, we need to copy the contents of the public key (id_rsa.pub) to the authorized_keys file of the target_instance. This file determines which public keys are allowed to access the instance.

     cat ~/.ssh/id_rsa.pub
    

    Copy the output of the above command.

  3. Log in to thetarget_instance:

    Use the existing method to access the target_instance. This could be through an initial SSH key or password that is already set up.

  4. Paste the content of the private key in the authorized_keys of the target_instance and save it.

     vim /home/ubuntu/.ssh/authorized_keys
    
  5. Then log in from the ansible_ub to the target_instance, by the following command:

     ssh <private_ip target_instance>
    

    And you'll be logged in password-less authentication way.

Ansible ad-hoc commands

Ansible ad-hoc commands are one-off commands used to perform quick tasks on remote servers without the need to write a full playbook. These commands are useful for simple operations like restarting a service, copying files, or checking the status of a system. They provide a way to execute Ansible modules directly from the command line.

Syntax

The basic syntax for an Ansible ad-hoc command is:

ansible <host-pattern> -m <module> -a "<module arguments>" [-i inventory] [other options]

Components

  • <host-pattern>: Specifies the target hosts or group of hosts.

  • -m <module>: Specifies the Ansible module to be used.

  • -a "<module arguments>": Provides the arguments required by the module.

  • -i inventory: Specifies the inventory file (if not using the default).

  • [other options]: Additional options like becoming a superuser (-b), defining extra variables, etc.

Examples

  1. Ping All Hosts:

    This command uses the ping module to check the connectivity to all hosts in the inventory.

     ansible all -m ping
    
  2. Install a Package:

    This command uses the apt module to install the git package on hosts in the webservers group.

     ansible webservers -m apt -a "name=git state=present" -b
    
  3. Restart a Service:

    This command uses the service module to restart the Apache service on hosts in the webservers group.

     ansible webservers -m service -a "name=apache2 state=restarted" -b
    

Using Inventory

By default, Ansible uses /etc/ansible/hosts as the inventory file. You can specify a different inventory file using the -i option:

inventory file -> File contains the IP address of the target servers.

Example

ansible -i /path/to/inventory all -m "shell" -a "touch file1"

In the place of touch file1 we can provide any command to execute it.

We can store the inventory file at any location. By default in Ubuntu it stored on a particular location /etc/ansible/hosts.

Whenever we write playbooks or ad-hoc commands, the inventory file is stored in the same location.

Becoming a Superuser

Many tasks require elevated privileges. You can use the -b option to become a superuser (similar to sudo):

ansible webservers -m apt -a "name=git state=present" -b

If you ever come across the above meme, it will delete all the files in all the servers as it has superuser permission(-b) for all files.😅

Running Ad-Hoc Commands on Specific Hosts

You can target specific hosts by their names :

ansible webserver1 -m ping

By patterns of the specific hosts by their names:

ansible "webserver[1:3]" -m ping

Ansible Playbooks

Just as we write scripts in shell scripting and call those files shell scripts, or write code in Python and call those files Python scripts, in Ansible, we write configuration and automation code in YAML format and refer to those files as playbooks.

Ansible playbooks serve as the primary means of defining and executing tasks to automate the configuration, deployment, and management of systems. Playbooks are the heart of Ansible's automation, enabling users to orchestrate the configuration, deployment, and management of systems in a repeatable and efficient manner.

Ansible Playbooks -> Collection of ansible ad hoc commands in the yaml file in a certain structure

Example Playbook

---
- name: Example Playbook
  hosts: webservers
  become: yes
  vars:
    http_port: 80
  tasks:
    - name: Ensure Apache is installed
      apt:
        name: apache2
        state: present

    - name: Ensure Apache is running
      service:
        name: apache2
        state: started

    - name: Copy the Apache config file
      template:
        src: /path/to/httpd.conf.j2
        dest: /etc/apache2/httpd.conf
        owner: root
        group: root
        mode: '0644'

    - name: Ensure the firewall is allowing HTTP traffic
      ufw:
        rule: allow
        name: 'Apache'
        port: "{{ http_port }}"
        proto: tcp

Components of a Playbook

  1. Playbook Header (---):

    • The three dashes at the beginning of the file indicate the start of the YAML document.
  2. Play:

    • A play is a list item (indicated by -) that contains several key components such as name, hosts, become, vars, and tasks.
  3. Name:

    • The name attribute is a human-readable identifier for the play.
  4. Hosts:

    • The hosts attribute specifies the target hosts or groups of hosts on which the play should run.
  5. State:

    • present: Ensures the package is installed.

    • absent: Ensures the package is not installed.

  6. Become:

    • The become attribute, when set to yes, allows tasks to run with elevated privileges (e.g., as the root user).
  7. Vars:

    • The vars section defines variables that can be used within the playbook.
  8. Tasks:

    • The tasks section is a list of actions to perform on the specified hosts. Each task is a dictionary containing at least two keys: name and the module to execute.

Commonly Used Modules

  • apt/yum: Manages packages on Debian-based/Red Hat-based systems.

  • service: Manages services.

  • copy: Copies files to remote locations.

  • template: Manages files using Jinja2 templates.

  • ufw: Manages firewall rules on systems using UFW.

Execution of the Ansible Playbook

ansible-playbook -i inventory dummy-playbook.yaml

If you want to see how Ansible is executing all the commands step by step in a more detailed way, you can just use the -v flag which will log detailed logs of executing commands in debug mode.

We can add more v's if we want more verbose output.

ansible-playbook -vv -i inventory dummy-playbook.yaml

Ad-Hoc Commands vs. Playbooks

Ad-Hoc Commands:

  • Quick, one-time tasks

  • Syntax Example: Run directly from the command line

      ansible all -m apt -a "name=nginx state=present" -b
      ansible all -m service -a "name=nginx state=started enabled=yes" -b
    

Now you'll see how the same command is written in the Ansible playbook.

Playbooks:

  • Purpose: Complex, reusable automation tasks

  • Syntax Example: Defined in YAML files

      ---
      - name: Install and configure nginx
        hosts: all
        become: yes
        tasks:
          - name: Install nginx
            apt:
              name: nginx
              state: present
    
          - name: Ensure nginx is running
            service:
              name: nginx
              state: started
              enabled: yes
    

Ad-hoc commands are useful for quick and simple tasks, while playbooks offer a more powerful, flexible, and maintainable way to automate complex workflows and ensure consistency across multiple runs.


You might be thinking that these commands are too lengthy and difficult to learn, no worries we shouldn't need to remember these, whenever we require any command to execute we can go through this Ansible Documentation Click me.

Grouping Servers in Ansible Inventory

In Ansible, you can group servers in the inventory file to organize and manage them more effectively. Here's an example of how to define groups in the inventory file:

inventory

[dbservers]
172.31.62.28
172.31.64.28

[webservers]
172.31.62.101
172.31.62.105

Executing Commands on Grouped Servers

To execute commands on specific groups of servers, use the following syntax:

ansible -i inventory groupName -m shell -a "df"

Here groupName can be webservers, or dbservers.

On executing the following command ansible goes in the inventory file and then it finds out on which of the IP it have to execute the certain commands.

Ansible Role

An Ansible role is a pre-defined set of tasks and configurations that can be applied to a host or a group of hosts. Roles allow you to:

  • Organize: Break down playbooks into reusable components.

  • Reuse: Share and reuse common configurations across different playbooks.

  • Distribute: Easily distribute roles across different environments or teams.

Creating and Managing Roles

You can create a role manually using the directory structure or use the ansible-galaxy command to generate a role skeleton:

ansible-galaxy init my_role

This command creates the necessary directory structure and sample files for a new role.

Structure of an Ansible Role

An Ansible role is typically organized into a directory structure with several standard subdirectories and files. Here’s a basic layout:

roles/
  my_role/
    defaults/
      main.yml
    files/
    handlers/
      main.yml
    meta/
      main.yml
    tasks/
      main.yml
    templates/
    vars/
      main.yml

Key Directories and Files

  1. defaults/:

    • Contains default variables for the role.

    • File:main.yml

    • Example:

        # roles/my_role/defaults/main.yml
        ---
        some_variable: "default_value"
      
  2. files/:

    • Contains files that can be copied to remote hosts.

    • Example: config_file.conf

  3. handlers/:

    • Contains handlers that are called by other tasks (e.g., to restart services).

    • File:main.yml

    • Example:

        # roles/my_role/handlers/main.yml
        ---
        - name: Restart service
          service:
            name: my_service
            state: restarted
      
  4. meta/:

    • Contains metadata about the role, including dependencies.

    • File:main.yml

    • Example:

        # roles/my_role/meta/main.yml
        ---
        dependencies:
          - { role: another_role }
      
  5. tasks/:

    • Contains the main tasks for the role.

    • File:main.yml

    • Example:

        # roles/my_role/tasks/main.yml
        ---
        - name: Install package
          apt:
            name: my_package
            state: present
      
        - name: Ensure service is running
          service:
            name: my_service
            state: started
      
  6. templates/:

    • Contains Jinja2 templates that can be used to generate configuration files dynamically.

    • Example: config.j2

  7. vars/:

    • Contains variables specific to the role that override defaults.

    • File:main.yml

    • Example:

        # roles/my_role/vars/main.yml
        ---
        some_variable: "specific_value"
      

Using Roles in a Playbook

To use a role in a playbook, you can reference it in the roles section of your playbook. Here’s an example:

---
- name: Apply My Role
  hosts: all
  become: yes
  roles:
    - my_role

To wrap things up, Ansible is a game-changer for configuration management and automation. Whether you're just starting out or you're a seasoned pro, its simplicity, flexibility, and powerful features make it a must-have tool in your DevOps toolkit. By leveraging Ansible's capabilities, you can streamline your workflows, reduce manual tasks, and improve the overall efficiency of your infrastructure management. Keep exploring, experimenting, and harnessing the full potential of Ansible to transform the way you work.

If you like this blog, please do like it.

Check out my Portfolio website for connecting with me or just to say Hi !!.
Happy automating!

20
Subscribe to my newsletter

Read articles from Anurag Kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Anurag Kumar
Anurag Kumar

Hi, I’m Anurag Kumar, a Software Engineer who loves exploring the latest technologies and sharing insights to help others grow. With a focus on web development and DevOps, I enjoy writing about practical tips, emerging trends, and tools that make developers' lives easier. My goal is to keep my blogs simple, engaging, and useful for everyone—whether you're just starting out or looking to level up your skills. Follow me for friendly and accessible content that bridges the gap between learning and doing!