Summary:
- Introduction
- Installation (step-by-step)
- Architecture
- Modules
- Ad-hoc commands/Playbooks with examples
1. Ansible Introduction
Ansible is a RedHat product. It is a configuration management tool. It is one of the many IaC (infrastructure as Code). It is agentless (means only have it in the master node and it can modify/configure other system through a script/playbook or Ansible Adhoc Commands). Ansible is also a push-based mechanism.
Why ansible?
To install java in 3 servers is easy but to install java in 100 servers, needs a lot of time.
When you installed already, then your boss ask you to update the java, cause you installed java 7, but he wanted java 11.
So use Ansible (you write one script, edit one script and fire to several servers).
Another example: Whatsapp
Whatsapp server (master) and Whatsapp mobile app (client/agent machine)
So what is happening is pull based and push based.
Pull based: client asking from master to change
Push based: master deciding a particular script will be installed in the client
Example of pull based:- Chef, Puppet
Example of push based:- Ansible, Terraform
2. Ansible Installation (on Ubuntu)
Syntax: ssh-keygen
Step 2:- Copy the generated SSH keys onto the hosts using copy id command
Step 3:- Before installing Ansible package, add Ansible repository to your system
Syntax: sudo apt-add-repository ppa:ansible/ansible
Step 4:- Run the update command before installing to update existing packages
Syntax: sudo apt-get update
Step 5:- Now install the Ansible package
Syntax: sudo apt-get install ansible
Step 6:- You can check if you’re on the latest version of Ansible by running the version command
Syntax: ansible --version
3. Ansible Architecture
Ansible is run on the ssh(secure shell) layer
What is ssh layer? see the other post for a step-by-step implementation of communicating with different servers via ssh.
Master Server
The Server where Ansible is installed and using Ansible you are managing the configuration on host machines (Agent machine)
Host servers/machines
These are agent machines where you need to change the configuration using Ansible. For example, if you need to change the configuration on webservers and DB servers then webservers and DB servers are host servers.
Inventory or host file.
It is a file where it contains information of all host machines' IP addresses or FQDN (Fully Qualified Domain Name).
By default the path is: /etc/ansible/hosts
Below are the formats by which we can configure hosts machines IP or FQDN in the hosts file:
a) IP Addresses:- You can specify the IP addresses of your host machine.
b) Group:- You can add IP address which is relevant to a group. For example, if you have 2 IPs 192.168.10.10 and 192.168.10.11 are web servers then you can specify a group called webservers
[webservers]
192.168.10.10
192.188.10.11
c) Range:- You can also define a range of domain names in the host file. In below example, we are specifying domain names like db-99.mydomain to db-110.mydomain.com
db-[99-110].mydomain.com
- Ansible should be installed on the master node.
- Should have root access to Master and other nodes
- Not mandatory but In my example, I am working as a root user
- Master node and Managed Nodes ( Agent nodes) should be configured and in the network.
- Master node's public key and private key ssh should be generated (ssh-keygen)
- Master node's public key is to be shared with Agent nodes ( /root/.ssh/authorized_keys ) file
Current Setup: 3 Ubuntu Servers ( 1 master and 2 agent nodes)\
Master server is also a dbserver.
Master IP: 192.168.33.10
Webserver1 IP: 192.168.33.11
Webserver2 IP: 192.168.33.12
Host file Setup
Goto /etc/ansible/hosts file and add below entries
[dbservers]
192.168.33.10
[webservers]
192.168.33.11
192.168.33.12
/etc/ansible/ansible.cfg
uncomment(remove '#') from host file path
inventory = /etc/ansible/hosts
- Modules are used to accomplish automation tasks in Ansible. These programs are written to be resource models of the desired state of the system. Ansible then executes these modules and removes them when finished. Without modules, you'd have to rely on ad-hoc commands and scripting to accomplish tasks.
- Modules are used with Adhoc commands as well as with playbooks.
- Modules are having attributes or properties which help to define the desired state of a given task.
- For example, if you want to install an apache server on a Ubuntu machine then the apt module has a property called name and state where name indicates package name (apache2) and state indicates installed, removed etc.
- yum module represents yum package manager for RedHat-based OS.
- apt module represents apt package manager for Debian-based OS.
Ansible configuration file (ansible.cfg). It is the file that has settings related to the Ansible package.
For example:
Change role path
Ansible commands to manage the IaC.
If the configuration is not complex (do not have conditional statements or loops etc.) then we can use the Ad-hoc commands.
An Ansible ad hoc command uses the /usr/bin/ansible command-line tool to automate a single task on one or more managed nodes. ad-hoc commands are quick and easy, but they are not reusable. So why learn about ad hoc commands first? ad-hoc commands demonstrate the simplicity and power of Ansible.
- ping module
- service module
- shell module
- apt/yum module
- copy module
Ansible playbooks are the script files that are used for managing the infrastructure on host machines that are configured in the inventory file. For example if you want to install a package on Webservers then you can write a yaml script (playbook) to install package on webservers.
Playbook (file name)
It is an ansible script name which is having extension either .yml or .yaml
---
In Playbook we can define one or more plays and these plays represents a set of tasks that you need to execute on a set of host machines.
Plays can have some variables which you can use in the tasks.
Plays consist of tasks and each task is having a set of modules definition which need to execute at run time.
command:
Create a hello.yaml in /etc/ansible folder. ( you can give any primary name but the extension should be .yaml or .yml).
In the .yaml file,
Type in:
---
- name: play for running shell commands
hosts: dbservers
tasks:
- name: Executing command module
command: echo "Hello World"ansible-playbook hello.yaml
register: output- name:debug:msg: "{{ output.stdout }}"
now it will show as per picture below!
Example 3: Create MULTIPLE plays in a playbook.
- playbook: multiple.yaml
- play1: run on dbservers and create a file /tmp/local.txt
- play2: run on webservers and create a file /tmp/webserver.txt
---
- name: running shell commands on dbservers
hosts: dbservers
tasks:
- name: creating local.txt on dbservers
command: touch /tmp/local.txt
- name: running shell commands on webservers
hosts: webservers
tasks:
- name: creating webservers.txt on webservers
command: touch /tmp/webservers.txt
done!
Example 4: Create a yaml file called (mul.yaml) and make a script such that it will create a file (testansible.txt) in /tmp and add content ("Hello World") in that .txt file and read it.
touch /etc/ansible/mul.yaml
vi /etc/ansible/mul.yaml
---
- name: running shell command on dbservers
hosts: dbservers
tasks:
- name: creating a file in /tmp/
command: touch /tmp/testansible.txt
- name: adding text in the .txt file (you can use copy module or shell module)
copy:
content: "Hello World"
dest: /tmp/testansible.txt
shell: echo "Hello World2" >> /tmp/testansible.txt
- name: executing cat command on content of .txt file
command: cat /tmp/testansible.txt
register: output
- name: printing out the content
debug:
msg: "{{ output.stdout }}"
ansible-playbook mul.yaml
Example 5: Create a variable and store "Hello World" then print it out using the same mul.yaml file as example 4 above.
vi mul.yaml
---
- name: running shell command on dbservers
hosts: dbservers
vars:
string: "Hello World!"
tasks:
- name: creating a file in /tmp/
command: touch /tmp/testansible.txt
- name: adding text in the .txt file
copy:
content: "{{ string }}"
dest: /tmp/testansible.txt
- name: executing cat command on content of .txt file
command: cat /tmp/testansible.txt
register: output
- name: printing out the content
debug:
msg: "{{ output.stdout }}"
touch /tmp/testing.txt
ansible-playbook dryrun.yml --check
vi dryruncheckmode.yaml
---
Playbook:- dryrun.yaml
uncomment log_path in ansible.cfg file to store the log output in /var/log/ansible.log
---
ansible-playbook dryrun.yaml
After execution check /var/log/ansible.log file it should have the log of this file execution.
Example 11: no_log attribute
if no_log =True then no log information is recorded in the log file for that particular module.
Example 12: Error handling ( ignore_errors: True)
Playbook: error.yaml
---
ansible-playbook error.yaml
Playbook: facts.yaml
Run the below command to find the facts of a host, it also returns the in-built ansible variables. In below command stores the output of these inbuilt variables in facts.log file
ansible dbservers -m ansible.builtin.setup > facts.log
---
ansible-playbook facts.yaml
It should print the OS Name and node name of web servers.
Create a playbook to install apache2 on OS which doesn't belong to RedHat family
vi conditionnotequals.yaml
---
- name: play for conditonal statements
hosts: webservers
tasks:
- name: install apache2
apt:
name: apache2
state: present
when: ansible_facts['os_family']!="RedHat"
ansible-playbook conditionnotequals.yaml
Example 16: (or logical operator)
Create a playbook to install apache2 on OS which doesn't belong to RedHat family or belongs to Debian OS family
vi conditionor.yaml
---
- name: play for conditonal statements
hosts: webservers
tasks:
- name: install apache2
apt:
name: apache2
state: present
when: ansible_facts['os_family']!="RedHat" or ansible_facts['os_family']=="Debian"
ansible-playbook conditionor.yaml
Example 17: (and logical operator)
Create a playbook to install apache2 on OS which doesn't belong to Debian OS family and OS is Ubuntu
vi conditionand.yaml
---
- name: play for conditonal statements
hosts: webservers
tasks:
- name: install apache2
apt:
name: apache2
state: present
when: ansible_facts['os_family']=="Debian" and ansible_distribution=="Ubuntu"
ansible-playbook conditionand.yaml
Example 18: Loops part 1 (iterate through item)
vi loops1.yaml
- name: playbook for loops example
hosts: dbservers
tasks:
- name: print values using loops
debug:
msg: "{{ item }}"
with_items / loop:
- 1
- 2
- 3
- 4
Example 19: Loops part 2 (iterate through making directories)