PolarSPARC |
Introduction to Ansible - Part 1
Bhaskar S | 06/17/2018 |
Overview
Ansible is an agentless open-source automation tool that enables Enterprise IT teams to perform one or more tasks, such as:
Provision different devices and systems
Configure and manage services exposed by the operating system and applications
Deploy various applications artifacts and binaries across one or more systems
So, how does Ansible actually work ???
At a high level, Ansible makes ssh connections to one or more specified hosts and executes each of the listed tasks (one at a time) across all the specified hosts in parallel.
The host from where Ansible is executed, is referred to as the Controller, while the hosts being managed are referred to as the Targets.
The following are some of the commonly used terms in the context of Ansible:
Term | Description |
---|---|
Host | A managed remote server with an IP address. Could be a bare metal server, a virtual machine, or a container, either on-premise or the cloud |
Group | A list of hosts targeted and managed as a single entity |
Inventory | A text file that lists the targeted hosts and groups |
Module | A reusable script, performing some action, that can be executed on a target (host or group) |
Task | An action performed on a target (host or group) using a module |
Play | A list of tasks performed on a target (host or group) |
Playbook | A list of plays |
Installation
The installation is on a Ubuntu 16.04 LTS based Linux desktop.
Download and install the Python 3 version of the Anaconda distribution.
Install Ansible by executing the following command:
$ conda install -c conda-forge ansible
To emulate multiple hosts, we will make use of virtual machine instances. For this, we will install VirtualBox and Vagrant by executing the following commands:
$ sudo apt-get update
$ sudo apt-get install virtualbox
$ sudo apt-get install vde2 virtualbox-guest-additions-iso
$ sudo apt-get install vagrant
Setup
Assume a hypothetical user alice with the home directory located at /home/alice.
Create two directories - one called Ansible and another called Vagrant under the home directory /home/alice by executing the following command:
mkdir -p Ansible Vagrant
Create and save a text file called Vagrantfile, with the following contents, in the directory /home/alice/Vagrant:
The above Vagrantfile defines 2 virtual machines, each with 1 cpu and 2 MB memory, based on Ubuntu 16.04 LTS, with host names of host1 (ip address 192.168.100.11) and host2 (ip address 192.168.100.12) respectively.
Change to the directory /home/alice/Vagrant and execute the following command to start the 2 virtual machine instances:
vagrant up
The following would be a typical output:
Bringing machine 'host1' up with 'virtualbox' provider... Bringing machine 'host2' up with 'virtualbox' provider... ==> host1: Importing base box 'ubuntu/xenial64'... ==> host1: Matching MAC address for NAT networking... ==> host1: Checking if box 'ubuntu/xenial64' is up to date... ==> host1: Setting the name of the VM: Vagrant_host1_1529241165789_22840 ==> host1: Clearing any previously set network interfaces... ==> host1: Preparing network interfaces based on configuration... host1: Adapter 1: nat host1: Adapter 2: hostonly ==> host1: Forwarding ports... host1: 22 (guest) => 2222 (host) (adapter 1) ==> host1: Running 'pre-boot' VM customizations... ==> host1: Booting VM... ==> host1: Waiting for machine to boot. This may take a few minutes... host1: SSH address: 127.0.0.1:2222 host1: SSH username: vagrant host1: SSH auth method: private key host1: Warning: Connection reset. Retrying... host1: Warning: Remote connection disconnect. Retrying... host1: Vagrant insecure key detected. Vagrant will automatically replace host1: this with a newly generated keypair for better security. host1: Inserting generated public key within guest... host1: Removing insecure key from the guest if it's present... host1: Key inserted! Disconnecting and reconnecting using new SSH key... ==> host1: Machine booted and ready! ==> host1: Checking for guest additions in VM... ==> host1: Setting hostname... ==> host1: Configuring and enabling network interfaces... ==> host2: Importing base box 'ubuntu/xenial64'... ==> host2: Matching MAC address for NAT networking... ==> host2: Checking if box 'ubuntu/xenial64' is up to date... ==> host2: Setting the name of the VM: Vagrant_host2_1529241250945_29687 ==> host2: Fixed port collision for 22 => 2222. Now on port 2200. ==> host2: Clearing any previously set network interfaces... ==> host2: Preparing network interfaces based on configuration... host2: Adapter 1: nat host2: Adapter 2: hostonly ==> host2: Forwarding ports... host2: 22 (guest) => 2200 (host) (adapter 1) ==> host2: Running 'pre-boot' VM customizations... ==> host2: Booting VM... ==> host2: Waiting for machine to boot. This may take a few minutes... host2: SSH address: 127.0.0.1:2200 host2: SSH username: vagrant host2: SSH auth method: private key host2: Warning: Connection reset. Retrying... host2: Warning: Remote connection disconnect. Retrying... host2: Vagrant insecure key detected. Vagrant will automatically replace host2: this with a newly generated keypair for better security. host2: Inserting generated public key within guest... host2: Removing insecure key from the guest if it's present... host2: Key inserted! Disconnecting and reconnecting using new SSH key... ==> host2: Machine booted and ready! ==> host2: Checking for guest additions in VM... ==> host2: Setting hostname... ==> host2: Configuring and enabling network interfaces...
To check the status of the 2 virtual machine instances, execute the following command:
vagrant status
The following would be a typical output:
Current machine states: host1 running (virtualbox) host2 running (virtualbox) This environment represents multiple VMs. The VMs are all listed above with their current state. For more information about a specific VM, run `vagrant status NAME`.
From the Output.2 above, we see the 2 virtual machine instances running and we are now ready to get our hands dirty with Ansible.
Hands-on with Ansible
Create and save an inventory file called hosts, with the following contents, in the directory /home/alice/Ansible:
The above inventory file defines the two target hosts with ip addresses 192.168.100.11 and 192.168.100.12 respectively. In addition, there are definitions for the 3 groups - host1, host2, and myhosts.
A group is defined by using the format [group-name] on its own line followed by a list of target hosts belonging to the group-name, each on its own line.
To ping the host with ip address 192.168.100.11 using Ansible, change to the directory /home/alice/Ansible and execute the following command:
$ ansible 192.168.100.11 -i hosts -m ping
The -i command-line option is used to specify the inventory file, while the -m command-line option is used to specify the module to use.
The following would be a typical output:
192.168.100.11 | UNREACHABLE! => { "changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 192.168.100.11 port 22: Connection timed out\r\n", "unreachable": true }
Hmm !!! What happened here ???
From Output.1 above, we see the ssh address for the virtual machine instance with ip address 192.168.100.11 is mapped to the host address 127.0.0.1:2222. Similarly, the ssh address for the virtual machine instance with ip address 192.168.100.12 is mapped to the host address 127.0.0.1:2200.
Modify the inventory file called hosts (located in the directory /home/alice/Ansible) with the following contents:
From the above inventory file called hosts, we see some parameters (with values) at the end of each of the hosts. These are referred to as the inventory Behavior Parameters and allow one to control how Ansible interacts with the target hosts.
The parameter ansible_ssh_host specifies the ssh host, ansible_ssh_port the ssh port, ansible_ssh_user the ssh user, and ansible_ssh_private_key_file the shh private key.
Let us retry the Ansible ping to the host with ip address 192.168.100.11 by executing the following command:
$ ansible 192.168.100.11 -i hosts -m ping
Type 'yes' when prompted to continue.
The following would be a typical output:
The authenticity of host '[127.0.0.1]:2222 ([127.0.0.1]:2222)' can't be established. ECDSA key fingerprint is SHA256:CFwZX0o1O5BJ8wAgc68oItcu1ATQaIoXViJbbFeRiqM. Are you sure you want to continue connecting (yes/no)? yes 192.168.100.11 | FAILED! => { "changed": false, "module_stderr": "Shared connection to 127.0.0.1 closed.\r\n", "module_stdout": "/bin/sh: 1: /usr/bin/python: not found\r\n", "msg": "MODULE FAILURE", "rc": 127 }
We will encounter a similar output if we Ansible ping the host with ip address 192.168.100.12.
From the Output.4 above, we see that the target hosts are missing Python, which is critical dependency for working with Ansible.
To install Python on the target host host1 (ip address 192.168.100.11), change to the directory /home/alice/Vagrant, and execute the following command:
$ vagrant ssh host1
The following would be a typical output:
Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.4.0-128-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage Get cloud support with Ubuntu Advantage Cloud Guest: http://www.ubuntu.com/business/services/cloud 0 packages can be updated. 0 updates are security updates. Last login: Sun Jun 17 13:19:06 2018 from 10.0.2.2 vagrant@host1:~$
At the command prompt, execute the following command to install Python:
$ sudo apt-get install -y python-minimal
Once the installation completes, the target host1 (192.168.100.11) will have Python.
Repeat the same installation steps for the target host2 (192.168.100.12) as well.
Once again, let us try the Ansible to ping the host with ip address 192.168.100.11 by executing the following command:
$ ansible host1 -i hosts -m ping
The following would be a typical output:
192.168.100.11 | SUCCESS => { "changed": false, "ping": "pong" }
Walla !!! Success at last.
To ping all the targets (the 2 virtual machine instances - host1 and host2) using Ansible, execute the following command:
$ ansible all -i hosts -m ping
The following would be a typical output:
192.168.100.12 | SUCCESS => { "changed": false, "ping": "pong" } 192.168.100.11 | SUCCESS => { "changed": false, "ping": "pong" }
The command-line option all is a built-in alias representing all the target hosts.
To execute any command using Ansible, we use the command module. To execute the uname command on all the targets, execute the following command:
$ ansible myhosts -i hosts -m command -a "/bin/uname -a"
The following would be a typical output:
192.168.100.12 | SUCCESS | rc=0 >> Linux host2 4.4.0-128-generic #154-Ubuntu SMP Fri May 25 14:15:18 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux 192.168.100.11 | SUCCESS | rc=0 >> Linux host1 4.4.0-128-generic #154-Ubuntu SMP Fri May 25 14:15:18 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
References