Everything about Ansible

πŸ‘¨β€πŸ’Ό Have you ever thought Installing software on one machine takes roughly 5 mins, and if we need to install the same software on five different machines how much time will it take? 5 * 5 mins = 25 mins . Does it sound OK to you to spend 25 mins when the same work can be done in literally 1 min? There are several automation tools that can achieve this, one of them is “ANSIBLE

TABLE OF CONTENT

  1. What is Ansible?
  2. Ansible Main Concepts
  3. Install Ansible on Ubuntu Machine
  4. Getting Started
  5. Beginner Commands to start
  6. Ansible Adhoc Commands
  7. Introduction to Ansible Playbook
  8. Interesting Ansible Loops and Conditions
  9. Ansible Blocks
  10. Ansible Handlers
  11. Ansible Variables
  12. Ansible Inventory
  13. Ansible TAGS
  14. Ansible Debugger
  15. Ansible Role

What is Ansible ?

IT Automation Tool used for deploying applications and system easily it could be CLOUD, Services, orchestration etc.] . Ansible uses YAML Language to build playbooks which are finally used to deploy or configure the required change. Ansible is an agentless automation tool that by default manages machines over the SSH protocol. Once installed, Ansible does not add a database, and there will be no daemons to start or keep running

Ansible Main Concepts

Control Node: Node from where you run ansible playbooks/commands. You can have multiple Control nodes but not windows. Must have Python Installed.

Managed Nodes: Servers/Network Devices where you manage Ansible also know as HOSTS.

Inventory: List of Managed Nodes.

Collections: It includes all of above playbooks , modules, roles and plugins. Install and Manage collections through Ansible Galaxy.

Modules: These are code or say Libraries that Ansible needs to execute. You can call or invoke modules in your playbooks.

Tasks: Unit of action is known as Task . You can execute one task , more tasks and later you can use many tasks in playbook as well.

Playbook: Ordered list of Tasks written in YAML Human readable language.

Installing Ansible on Ubuntu Machine

  • First thing is to update the ubuntu system package manager.
sudo apt update
  • Install software’s that are required to work with repositories.
sudo apt install software-properties-common # This Package is used to work with PPA  
  • Add the PPA repository on the system. PPA is Personal Package Archive ( NON Standard but used for TESTING by Organizations)
sudo apt-add-repository --yes --update ppa:ansible/ansible 
  • Finally install ansible by running the following command.
sudo apt install ansible

Getting Started

  1. Selecting machines from inventory

Basic inventory can be created and edited in /etc/ansible/hosts

10.111.4.18
aserver.domain.com
bserver.domain.com

2. How to Connecting to remote nodes.

Ansible which is installed on control node communicates with remote machines over SSH Protocol.

Related: working-with-ssh-connectivity

3. Copying and executing modules.

Once the connection is made ansible transfers the modules which are required for your commands or playbook to execute on the remote machine.

Beginner Commands

ansible all -m ping            # Ping Module to Ping all the nodes
ansible all -a "/bin/echo Automate"  # Echo Command to Provide the output
ansible all -a /usr/bin/uptime   # Provides the Uptime of the Server

Install the Apache service and start it

ansible all -m apt -a  "name=apache2 state=latest" -b  
        # b is to become root and we are installation the Apache service 

ansible all -b -m service -a "name=apache2 state=started"
        # Lets start the service now

Ansible Ad-hoc commands

Basically Ansible playbooks runs with several actions but in case you need a particular action to take place such as reboot of machine or copying a file to remote machine , start or stop any particular service in that case we can use ad-hoc commands. These are basically used when you need to perform any task rarely. It uses /usr/bin/ansible directory to perform the tasks.

Lets see few examples here:

Example 1: To reboot all machines under america-servers inventory run the following command.

ansible america-servers -a "/sbin/reboot"  -f 100 -u username --become
  1. ansible is used to run ansible command
  2. america-servers is a group of hosts which is saved in /etc/hosts
  3. “/sbin/reboot” is to reboot the machine
  4. -f is parallelly our command can execute on 100 servers altogether.
  5. -u is used to run this using a different username
  6. –become is used to run as a root

Example 2: To check if service is running on all machines run the following command.

ansible all -m ansible.builtin.service -a "name=apache2 state=started"

Example 3: To create a user on remote machine run the following command.

ansible all -m ansible.builtin.user -a "name=name password=password" -b

Introduction to Ansible Playbook

  1. Playbooks are used to deploy complex applications
  2. Playbooks offers reusable and simple configuration management
  3. Playbooks offers multi machine deployment
  4. Playbook offers help when we need to perform certain tasks multiple times.
  5. Playbooks are written in YAML format
  6. Playbooks executes in sequential order.
  7. Playbooks has different tasks to execute.

Lets run our first Ansible Playbook to install Apache latest service

  • Make sure control node can ssh into worker node , else please review working-with-ssh-connectivity
  • Create a apache.yml file in home directory and copy the below content

# Playbook apache.yml
---
- name: Installing Apache service on all my_app_servers  # Define the process
  hosts: my_app_servers                                  # Define all the hosts
  remote_user: ubuntu                                    # Remote_user is ubuntu

  tasks:                                                    # Define the tasks
  - name: Install the Latest Apache
    apt:
      name: httpd
      state: latest
  • Before you run your first ansible-playbook you can verify the syntax , check tasks and details of playbook.
ansible-playbook apache.yml --syntax-check
ansible-lint apache.yml
ansible-playbook apache.yml --check

Now, after performing syntax checks and other detailed scan of playbook, Lets run first command

ansible-playbook apache.yml
# Playbook of apache installation and service startup
---
- name: Installing Apache service on all my_app_servers  # Define the process
  hosts: my_app_servers                                  # Define all the hosts
  remote_user: ubuntu                                    # Remote_user is ubuntu

  tasks:                                                    # Define the tasks
  - name: Install the Latest Apache
    apt:
      name: httpd
      state: latest
  - name: Ensure apache2 serice is running
    service:  
      name: apache2
      state: started
    become: yes            # if become is set to yes means it has activated privileges
    become_user: ubuntu    # Changes user to ubuntu ( By default it is root )

Interesting Ansible Loops and Conditions

  1. Iterating over a dictionary

Lets learn how to iterate over a dictionary by the following example. Create a another playbook and copy the code as below.

# Playbook using iterating over a dictionary

 - name: Add several users 
   ansible.builtin.user: 
      name: "{{ item.name }}" 
      state: present 
      groups: "{{ item.groups }}" 
   loop: 
     - { name: 'automate1', groups: 'root' }
     - { name: 'automate2', groups: 'root' } 

Now run the command

ansible-playbook apache.yml

2. with_items and when condition

# Playbook using with_items and when condition


---
- name: update web servers
  hosts: webserver
  remote_user: ubuntu

  tasks:
   - name: ensure apache is at the latest version
     apt:
       name: apache2
       state: latest

   - name: Ensure apache2 serice is running
     service:
        name: apache2
        state: started
     become: yes
 
   - name: Create users
     user:
         name: "{{item}}"
     with_items:
      -bob
      -sam
      -Ashok
 
   - name: Edit the text file
     lineinfile:
     path:  /tmp/file
     state: present
     line:  "LogLevel debug"
     when:
     - ansible_hostname == "ip-10-111-4-18" 

Now run the command

ansible-playbook apache.yml

Ansible blocks

In case of blocks you get a common parent layer ie. you don’t need to write common things every time , it automatically inherits directives applied at block level.

In below example when , become , become_user , ignore_errors are common for entire block.

---
- name: update web servers
  hosts: webserver
  remote_user: ubuntu

  tasks:
   - name: Install, configure and start Apache
     block:
      - name: Install apache
        apt:
          name: apache2
          state: present
      - name: Ensure apache2 serice is running
        service:
           name: apache2
           state: started
  
    # Below Directives are common for entire block i.e all tasks

     when: ansible_facts['distribution'] == "Debian" 
     become: true
     become_user: root
     ignore_errors: yes

Error Handling within Blocks

---
- name: update web servers
  hosts: webserver
  remote_user: ubuntu


  tasks:
   - name: Handle the error
     block:                                  # block 
       - name: Print a message
         ansible.builtin.debug:
           msg: 'I execute normally'

       - name: Force a failure               # Fails here
         ansible.builtin.command: /bin/false

       - name: Never print this              # This will not proceed
         ansible.builtin.debug:
           msg: 'I never execute, due to the above task failing'
     rescue:
       - name: Print when errors            # Rescues thats why playbook works till end.
         ansible.builtin.debug:
           msg: 'I caught an error, can do stuff here to fix it'
     always:        
       - name: Always do this                # It always gets executed no matter what.
         ansible.builtin.debug:
           msg: 'This executes always'
  
     when: ansible_facts['distribution'] == "Debian" 
     become: true
     become_user: root
     ignore_errors: yes                       # Handling the Error
     ignore_unreachable: yes                  # If Host is unreachable, ignore it 
# Error Handling

---
- hosts: localhost
  tasks:
  - name: Get files
    get_url:
	  url: "http://{{items}}/index.html"  ---> Declared as variable
	  dest: "/tmp/{{items}}"
	ignore_errors: yes              ------>  Ignoring the errors 
    with_items:
       - automate1.labserver.com
       - automate2.labserver.com	   
       - automate3.labserver.com

Ansible Handlers

Handlers are used when you need to perform any task only once notified . You add all the tasks inside the handler and they are run whenever notify calls them.

  • Often you would to run a task only when there is any update or change like change in configuration or restarting the service. Handlers run when they are notified.

In below Example , whenever there is change in file or update then apache service is restarted. Handler will restart the Apache service only when some tasks notifies to it.

# Playbook Handlers

---
- name: update web servers
  hosts: webserver
  remote_user: ubuntu
 
  tasks:
   - name: ensure apache is at the latest version
     apt:
      name: apache2
      state: latest

   - name: Ensure apache2 serice is running
     service:
        name: apache2
       state: started
     become: yes

   - name: Edit the text file
     lineinfile:
       path:  /tmp/file
       state: present
       line:  "LogLevel debug"
     when:
       - ansible_hostname == "ip-10-111-4-18"
     notify:
      -  Restart Apache
  handlers:
   - name: Restart Apache
     ansible.builtin.service:
       name: apache2
       start: restarted 

Ansible Variables

There are lots of ways in which you can set your variables inside the ansible playbook, lets look some of them below.

  • Defining Simple Variable
  • Defining Variables from file
  • Defining Variables from roles
---
- name: update web servers
  hosts: webserver
  remote_user: ubuntu
  remote_install_path: /tmp/mypath       # Defining Simple Variable      ~1
  vars:                                  # Defining Variables from Role  ~2
     favcolor: blue   
  vars_files:                           # Defining Variables from file   ~3  
     - /vars/external_vars.yml
 
  tasks:
   - name: Check Config
     ansible.builtin.template:
       src: my.cfg.j2
       dest: '{{remote_install_path}}/my.cfg'

Defining variable at run time

  • Key=value format
ansible-playbook myplaybook.yml --extra-vars "version=1.23.45 other_variable=auto"
  • JSON format
ansible-playbook myplaybook.yml --extra-vars '{"version":"1.23.45","other_variable":"auto"}'

Ansible Inventory

Ansible works against managed nodes or hosts to create or manage the infrastructure . We list down these hosts or nodes in a file known as Inventory. Inventory can be of two types one is ini and other is YAML format.

INI format

           automate2.mylabserver.com
           [httpd]
           automate3.mylabserver.com
           automate4.mylabserver.com
           [labserver]
           automate[2:6].mylabserver.com

YAML Format

           all:
             hosts:
                automate2.mylabserver.com
             children:
                 httpd:
                   hosts:
                     automate3.mylabserver.com
                     automate4.mylabserver.com
                 labserver:
                    hosts:
                     automate[2:6].mylabserver.com

Ansible TAGS

When you need to run specific tasks in the Playbook , you can apply TAGS to them. TAGS are applied on block level , playbook level, individual task level or role level.

Adding Tags to individual task

---
 - hosts: appservers
   tasks:
   - name: Deploy App Binary
     copy:
	   src: /tmp/app.war
	   dest: /app/
	   
	 tags: 
	   - apptag          # applying TAG to task 1
	 
 - hosts: dbserver	   
   tasks:
   - name: Deploy DB Binary
     copy: 
       src: /tmp/db.war`
       dest: /db
     tags:   	         # applying TAG to task 2
       - dbtag

Adding Tags to block

tasks:
- block:
  tags: ntp                     # Aplying TAG to a block
  - name: Install ntp
    ansible.builtin.yum:
      name: ntp
      state: present

  - name: Enable and run ntpd
    ansible.builtin.service:
      name: ntpd
      state: started
      enabled: yes

Adding Tags to plays

- hosts: all
  tags: ntp                     #  Apply TAG to a PLAY
  tasks:
  - name: Install ntp
    ansible.builtin.yum:
      name: ntp
      state: present

Ansible Debugger

Ansible provides debugger in order to fix errors during execution instead of editing it every time and then running.

✏️ debugger on a task

- name: Execute a command
  ansible.builtin.command: "false"
  debugger: on_failed

✏️ debugger on a play:

- name: My play
  hosts: all
  debugger: on_skipped
  tasks:
    - name: Execute a command
      ansible.builtin.command: "true"
      when: False

✏️ debugger at multiple levels:

- name: Play
  hosts: all
  debugger: never
  tasks:
    - name: Execute a command
      ansible.builtin.command: "false"
      debugger: on_failed

Ansible Roles

Roles are basically a way to structurally maintain your playbooks ie. it lets you load the variables from files , variables , handlers , tasks from based on the structure . Similar to modules kind of thing , you can create different roles and reuse them as many times as you need.

Role Directory Structure:

  • Tasks : This directory contains one or more files with tasks . These file can also refer to files and templates without needing to provide the exact path.
  • Handlers: Add all your handlers in this directory
  • Files: This directory contains all your static files and scripts that might be copied or executed to remote machine.
  • Templates: This directory is reserved for templates that generate files on remote hosts.
  • Vars: You define variables inside this directory and then can be referenced elsewhere in role.
  • defaults: This directory lets you define default variables for included or dependent role.
  • meta: This directory is used for dependency management such as dependency roles.
# Example of Playbook

--- 
- hosts: all 
  become: true 
  vars: 
    doc_root: /var/www/example 
  tasks: 
    - name: Update apt 
      apt: update_cache=yes 
 
    - name: Install Apache 
      apt: name=apache2 state=latest 
 
    - name: Create custom document root 
      file: path={{ doc_root }} state=directory owner=www-data group=www-data 
 
    - name: Set up HTML file 
      copy: src=index.html dest={{ doc_root }}/index.html owner=www-data group=www-data mode=0644 
 
    - name: Set up Apache virtual host file 
      template: src=vhost.tpl dest=/etc/apache2/sites-available/000-default.conf 
      notify: restart apache 
 
  handlers: 
    - name: restart apache 
      service: name=apache2 state=restarted

Let us use above playbook and break it into role.

  • πŸ”– Create a directory roles inside home directory mkdir roles
  • πŸ”– Create a Directory apache inside roles inside home directory mkdir apache
  • πŸ”– Create all directory’s inside apache: mkdir defaults tasks files handlers vars meta templates
  • πŸ”– Create main.yml inside cd ~/roles/apache/tasks
---    
- name: Update apt
      apt: update_cache=yes

    - name: Install Apache
      apt: name=apache2 state=latest

    - name: Create custom document root
      file: path={{ doc_root }} state=directory owner=www-data group=www-data

    - name: Set up HTML file
      copy: src=index.html dest={{ doc_root }}/index.html owner=www-data group=www-data mode=0644

    - name: Set up Apache virtual host file
      template: src=vhost.tpl dest=/etc/apache2/sites-available/000-default.conf
      notify: restart apache

Create main.yml inside cd ~/roles/apache/handlers

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

Create index.html inside cd ~/roles/apache/files

<html>
<head><title>Configuration Management Hands On</title></head>

<h1>This server was provisioned using <strong>Ansible</strong></h1>

</html>

Create vhost.tpl inside cd ~/roles/apache/templates

<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot {{ doc_root }}

<Directory {{ doc_root }}>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>

Create main.yml inside cd ~/roles/apache/meta

---
dependencies:
  - apt

Create my_app.yml inside home directory

---
- hosts: all
  become: true
  roles:
    - apache
  vars:
    - doc_root: /var/www/example

Wrapping Up

So this was a detailed and in depth tutorial on ansible and ansible playbooks. We majorly covered all part of ansible components and how we can work with ansible. This tutorial is prepared with lots of efforts and with complete practical demonstration. I would request you to please share the word with others and go through tutorial if you find it useful.

Please share with your Friends and Groups

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s