Skip to content

Instantly share code, notes, and snippets.

@vsathyak
Last active October 18, 2022 11:02
Show Gist options
  • Save vsathyak/8850736e76ce80e34b89b5af79cf7a20 to your computer and use it in GitHub Desktop.
Save vsathyak/8850736e76ce80e34b89b5af79cf7a20 to your computer and use it in GitHub Desktop.
Ansible Introduction and Architecture
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ansible is an automation engine that allows for agentless system configuration and deployment means it is simply a tool
that can execute tasks on remote or local system wherever you need to. It does this in a way that doesn't require any
real additional software outside of the ansible based binaries that you install on one server and runs Ansible modules
on remote system over SSH inorder to complete tasks.
Basic Components
----------------
Control Node : Any machine with Ansible installed. You can run commands and playbooks, invoking /usr/bin/ansible or
/usr/bin/ansible-playbook, from any control node.
Managed Nodes : The network devices (and/or servers) you manage with Ansible. Managed nodes are also sometimes called
“hosts”. Ansible is not installed on managed nodes.
Inventory : A list of managed nodes. An inventory file is also sometimes called a “hostfile”. Your inventory can
specify information like IP address for each managed node.
Modules : The units of code Ansible executes. Each module has a particular use, from administering users on a
specific type of database to managing VLAN interfaces on a specific type of network device.
Tasks : The units of action in Ansible. You can execute a single task once with an ad-hoc command.
Playbooks : Ordered lists of tasks, saved so you can run those tasks in that order repeatedly. Playbooks can
include variables as well as tasks.
________________________________________________________________________________________________________________________
Ansible Installation and Configuration Part 1 & 2
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To install Ansible you must configure EPEL repository in your system first. Once the EPEL repository is configured, your
package manager install Ansible and manages the dependencies.
(Note:EPEL repo stands for Extra Packages for Enterprise Linux Repository. It is a free and open source repository project
made available to everybody by Fedora. It is well known for providing 100% high quality add-ons for Linux based software’s
including CentOs and RHEL. These packages are needed to be enabled separately for CentOS with a unique set of commands.
The reason for adding them separately is because they are not included in the standard software package yet they are very
useful.)
Verify epel-release installed, if not, install
----------------------------------------------
>> yum list epel-release
>> yum install epel-release(do it only if not installed)
Install Ansible
---------------
>> sudo yum install ansible
Install Git
-----------
>> sudo yum install git
Configure Ansible
-----------------
>> vim /etc/ansible/ansible.cfg (Primary Ansible Configuration File)
The commented line is simply the default value here and if you want to change the default value, uncomment and update.
Configure Ansible Inventory(An inventory is a list of hosts that Ansible manages)
---------------------------
>> vim /etc/ansible/hosts (Default Ansible Inventory File)
vsathyak5 ansible_host=vsathyak5c.mylabserver.com
vsathyak6 ansible_host=vsathyak6c.mylabserver.com
Ansible is best implemented using a common user across all Ansible controlled systems.
Create a user 'ansible' in Control node
-------------------------------------
>> sudo useradd ansible
Create a user 'ansible' and set password in Host node
-----------------------------------------------------
>> Connect over ssh to host node from control node
>> sudo useradd ansible (adding user ansible)
>> sudo passwd ansible (learn@123)
Reason for setting password in host nodes and not in control node is that we need to login from control node to host
node later using to set up ssh preshared key. So we need password in host nodes.
Sharing preshared key from Control node to Host node
---------------------------------------------------
>> sudo su - ansible (switching to ansible user)
>> ssh-keygen (generate ssh keys)
>> ssh-copy-id vsathyak5c.mylabserver.com
>> Enter ansible user password
Giving sudo privilege to 'ansible' user in Host nodes
-----------------------------------------------------
>> ssh [email protected] from control node or access it from terminal directly.
Note :: /etc/sudoers may be edited to allow 'ansible' user to sudo any commandwithout a password
>> sudo visudo
Find the line
# %wheel ALL=(ALL) NOPASSWD: ALL
Add the following
ansible ALL=(ALL) NOPASSWD: ALL
________________________________________________________________________________________________________________________
Where to Find Documentation
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Reference >> docs.ansible.com
List specific ansible module >> ansible-doc -s <module_name> Eg: ansible-doc -s lineinfile
>> ansible-doc <module_name> Eg: ansible-doc lineinfile
List all ansible module >> ansible-doc -l
________________________________________________________________________________________________________________________
Ad-hoc Ansible Commands
~~~~~~~~~~~~~~~~~~~~~~~
An ad-hoc command is something that you might type in to do something really quick, but don’t want to save for later and
is executed on any target hosts or group of target hosts where as Playbooks are more like a scripts.
Syntax : ansible <HOST> -m <MODULE> -a "<ARG1 ARG2 ARG3>"
Eg : ansible vsathyak5 -m setup
Note : Please make sure you are using the correct user to execute the commands. In our case, its 'ansible'.
>> sudo su - ansible
Return info regarding the host vsathyak5
----------------------------------------
>> ansible vsathyak5 -m setup
Try to connect to host, verify a usable python and return pong on success
-------------------------------------------------------------------------
>> ansible vsathyak6 -m ping
Install httpd on host vsathyak5c
--------------------------------
>> ansible vsathyak5 -m yum -a "name=httpd state=latest"
This will return the error "You need to be root to perform this command.\n"
You need to use -b flag to get the sudo privilege and this will install httpd package in host : vsathyak5c
>> ansible vsathyak5 -b -m yum -a "name=httpd state=latest"
Controls services on remote hosts
---------------------------------
>> ansible vsathyak5 -b -m service -a "name=httpd state=started"
>> Login to the host machine
>> sudo systemctl status httpd (Will return the status running)
________________________________________________________________________________________________________________________
Ansible Playbooks
~~~~~~~~~~~~~~~~~
As ad-hoc commands are to bash commands, playbooks are to bash scripts. Playbooks run using 'ansible-playbook' command
and not the ansible command. Playbooks are written in YAML. It contain different elements called plays. Plays contain
list of host and at minimum one task.
You can use default host inventory or you can create a new inventory file.
>> sudo su - ansible
>> pwd ==> /home/ansible
Create user defined inventory file
----------------------------------
>> vim inv
[webservers] --> this defines a group of hosts in ansible called 'webservers'
vsathyak5 ansible_host=vsathyak5c.mylabserver.com
vsathyak6 ansible_host=vsathyak6c.mylabserver.com
>> Save it.
Create playbook
---------------
>> vim web.yml
--- # Loading Webservers
- hosts: webservers
become: yes
tasks:
- name: install latest httpd # Plain English
yum:
name: httpd
state: latest
- name: create index.html file
file:
name: /var/www/html/index.html
state: touch
- name: add web content
lineinfile:
line: "Here is some text..."
path: /var/www/html/index.html
- name: start httpd
service:
name: httpd
state: started
Run Playbook
------------
>> ansible-playbook -i inv web.yml (-i is to pass the user defined inventory file.)
PLAY [webservers] *********************************************************************************
TASK [Gathering Facts] ****************************************************************************
ok: [vsathyak5]
ok: [vsathyak6]
TASK [install latest httpd] ***********************************************************************
ok: [vsathyak5]
changed: [vsathyak6]
TASK [create index.html file] *********************************************************************
changed: [vsathyak5]
TASK [add web content] ****************************************************************************
changed: [vsathyak6]
changed: [vsathyak5]
TASK [start httpd] ********************************************************************************
changed: [vsathyak6]
changed: [vsathyak5]
PLAY RECAP ****************************************************************************************
vsathyak5 : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
vsathyak6 : ok=5 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Check Mode
----------
>> ansible-playbook -i inv web.yml --check
Retry file : If playbook fails, a retry file is generated and contain the list of hosts where the playbook
failed : playbook-name.retry
The file may be specified using --limit with the same playbook to reattempt the playbook at a
later time.
________________________________________________________________________________________________________________________
Ansible Variables
~~~~~~~~~~~~~~~~~
Variable names should be letters, numbers and underscores.
Eg: foobar, foo_bar, foo5
Passing Variables via Command line : using --extra-vars or -e flag
Eg : ansible-playbook web.yml -e "target_host=localhost target_service=httpd"
---
hosts: webservers
become: yes
vars:
target_service: httpd
target_state: started
tasks:
- name: Ensure target service
service:
name: "{{ target_service }}"
state: "{{ target_state }}"
Run Playbook
------------
>> ansible-playbook -i inv web_var.yml
Create playbook with vars
-------------------------
>> vim web.yml
---
- hosts: webservers
become: yes
tasks:
- name: install latest httpd
yum:
name: "{{ target_service }}"
state: latest
- name: create index.html file
file:
name: /var/www/html/index.html
state: touch
- name: add web content
lineinfile:
line: "Here is some text..."
path: /var/www/html/index.html
- name: start httpd
service:
name: "{{ target_service }}"
state: started
Run Playbook and passing value to variable on runtime
-----------------------------------------------------
>> ansible-playbook -i inv web_var.yml -e "target_service=httpd"
________________________________________________________________________________________________________________________
Ansible Facts
~~~~~~~~~~~~~
Facts are properties regarding a given remote sytem. The 'setup' module can retrieve facts. The filter parameter
takes regex to allow you to prune fact output.
Eg: ansible vsathyak5 -m setup retrieves fact about the node vsathyak5
Eg: ansible vsathyak5 -m setup -a filter=*ipv4* retrieves ipv4 facts of the node vsathyak5
Writing Playbook with content to be shown is the name of the host
-----------------------------------------------------------------
---
- hosts: webservers
become: yes
tasks:
- name: install latest httpd
yum:
name: "{{ target_service }}"
state: latest
- name: create index.html file
file:
name: /var/www/html/index.html
state: touch
- name: add web content
lineinfile:
line:"{{ ansible_hostname }}"
path: /var/www/html/index.html
- name: start httpd
service:
name: "{{ target_service }}"
state: started
Run playbook
------------
>> ansible-playbook -i inv web_fact.yml -e "target_service=httpd"
Setting up gather_facts = false in playbook
-------------------------------------------
---
- hosts: webservers
become: yes
gather_facts: no ===> This will result in not gathering facts and returns error on task 'name: add web content'
vars:
tasks:
- name: install latest httpd
yum:
name: "{{ target_service }}"
state: latest
- name: create index.html file
file:
name: /var/www/html/index.html
state: touch
- name: add web content
lineinfile:
line:"{{ ansible_hostname }}"
path: /var/www/html/index.html
- name: start httpd
service:
name: "{{ target_service }}"
state: started
Run playbook
------------
>> ansible-playbook -i inv web_nofact.yml -e "target_service=httpd"
This will return error since gather_facts is set no and so the variable 'ansible_hostname' become undefined.
And it results in auto creation of creation of file web.retry
>> ls -la
web_var_nofact.yml web_var_nofact.retry
>> cat web_var_nofact.retry
vsathyak5
vsathyak6
________________________________________________________________________________________________________________________
Troubleshooting and Debugging Ansible
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There are couple of ways to troubleshoot and debug ansible.
1. Debug Module
2. Registry keyword
Debug Module
------------
Debug module takes two parameter.
a. msg: A message that is printed to STDOUT
b. var: A variable whose content is printed to stdout
Register Module/Keyword
-----------------------
Register module is used to store task output
>> vim web_debug.yml
--- # Bootstrap webservers
- hosts: webservers
become: yes
gather_facts: yes
tasks:
- debug:
var: target_service # Print the value of target_service variable.
- name: install latest httpd
yum:
name: " {{ target_service }} "
state: latest
- name: create index.html file
file:
name: /var/www/html/index.html
state: touch
- name: add web content
lineinfile:
line: "{{ ansible_hostname }}"
path: /var/www/html/index.html # What happens in lineinfile task is stored in task_debug register.
register: task_debug # Also the location of the register tag is same as parent tag.
- debug:
msg: "Output of lineinfile is: {{ task_debug }}" # What is stored in the register is printed out here.
- name: start httpd
service:
name: "{{ target_service }}"
state: started
Run playbook
------------
>> ansible-playbook -i inv web_debug.yml -e "target_service=httpd"
________________________________________________________________________________________________________________________
Ansible Handlers
~~~~~~~~~~~~~~~~
Ansible provide a mechanism that allows an action to be flagged for execution when a tasks performs a change. By only
executing certain tasks during a change, plays are more efficient. This mechanism is known as Handler.
Also even if the handler is flagged multiple times in a play, it only runs during a plays final phase.
'notify' will only flag a handler if tasks blocks make a change.
>> vim web_handler.yml
--- # Bootstrap webservers
- hosts: webservers
become: yes
gather_facts: yes
tasks:
- name: install latest httpd
yum:
name: " {{ target_service }} "
state: latest
notify:
- restart httpd # A change in task yum will call the handler restart httpd
- name: create index.html file
file:
name: /var/www/html/index.html
state: touch
- name: add web content
lineinfile:
line: "{{ ansible_hostname }}"
path: /var/www/html/index.html
notify:
- restart httpd # A change in task yum will call the handler restart httpd
handlers:
- name: Attempt to restart httpd
service:
name: httpd
state: restarted
listen: "restart httpd"
Run playbook
------------
>> ansible vsathyak5 -b -m yum -a "name=httpd state=absent" (Remove httpd from vsathyak5)
>> ansible-playbook -i inv web_handler.yml -e "target_service=httpd"
________________________________________________________________________________________________________________________
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment