Part 1 : Presentation and prerequisites
Part 2 : Basics and first Playbook
Part 3 : Variables
Part 4 : Collections
––
Second part of the Ansible article series. The objective is still the same, to increase our skill on the tool based on use cases. Like the first part, it will be a mix of good practices but also of my own experience feedback. In this article, we come back to the basics of the product as well as the realization of a first Playbook. We will start the deployment of a Stack TIG (Telegraf, InfluxDB, Grafana) for the supervision of a VMware environment (vCenter + ESXi), it will be our common thread 🙂
Little reminder, the prerequisites are here.
The code for the following examples is on GitHub
Introduction and Ansible objectives
I have already writtend a quick introduction of Ansible in the first part, here we are gonna talk about use cases before giving more theoretical explanations.
Ansible was originally created to deal with configuration management on Linux server. It is agentless, so it relies on SSH to interact with the nodes (servers which will be managed by Ansible). Once connected to the nodes, Python code is executed. This is where I see the strengths of the product:
- Agentless thanks to SSH
- SSH allows remote control of network and security devices
- Python, which is included in most systems nowadays, based code is executed on nodes
Windows nodes are managed with PowerShell, and any kind of application can be configured as long as they are accessible through API (like Rest)
If you have all the steps described in the prerequisites article, you should have a Ansible ready environment by now. It is really easy to execute Playbook:
|
|
Use cases
One of the most common use case is the deployment and the configuration of a software stack. But there are many others:
- Deploying automatically network devices
- Creation and configuration of virtual machines
- ESXi server configuration
- Have a Playbook ready to re-deploy a complete application.
Components
- Control Node: Where Ansible is installed
- Managed Nodes Remotely controlled equipment. There are listed in the inventory file
- Tower Centralized web console. AWX is the Tower upstream
- Playbook: The file where the desired configuration is written
- Modules: The actual code which is executed on the nodes
We will come back to the Playbook and Modules later in the article.
Required files
To begin, we will see both mandatory and recommended files to make our Playbook works. But it is important to note there are more of them that can be useful under certain circumstances.
Playbook
In the Playbook we have to described the desired configuration. Unlike a script, here we don’t have to write test before taking an action, those tests are already present into the modules we are using, it is call idempotency.
Playbook must be written in YAML, here is an explanation regarding the syntax: https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
Inventory
The Inventory file is where we are listing our nodes we want to manage. There is a default file here: /etc/ansible/hosts.
The inventory can be in tow different format:
- ini
- YAML
ini format
Most of the time we will see the inventory as a ini file, but the YAML format is also interesting.
In our example where you are setting-up a TIG stack, here is an example of the Inventory file:
|
|
If we look more closely:
- [tig] Is the group name, it composed by the 3 following items (lines). The group can be used in the Playbook, in that case the Playbook will apply the configuration on all the members.
- influxdb is the name we chose to give to the node. It can also be used in the Playbook/
- ansible_host is a variable associated to the host, each variable is space separated, then we set the value with =. In our case, I specify the IP address as the host name is unresolvable.
I have add a second group (which will not be used) just to show we can create as many as we want/need. Here we can see we do not need to set the IP address if there is a DNS resolution.
At last, just a host, without any group.
For the demo, we will install the 3 software on the same server, so we will just set one line.
YAML format
Here the equivalent in YAML:
|
|
I think it is clear enough to understand how it works. You can see several key words like all, host and children. Important point, all is implicitly present in the inventory.ini file, it matches all host in the inventory.
Dynamic inventory
I won’t get into too much detail about dynamic inventory as it is not the subject here. But I would like you to know it is very convenient for an inventory to be managed by an external data source. Here is two examples:
- A vCenter server allows you to have a list of VM in your inventory
- An IPAM which acts as a Source of Truth, like NetBox
I invite you to have a look on the official documentation to get deeper about inventory: https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html
Interactive mode
The Ansible main goal is to run Playbook, but you can also run it into an interactive mode, to help you debugging for example. If we want to know if a host is accessible, we can try:
|
|
We try to connect to host grafana, which is defined in the inventory file with th user jlg. On this host, we execute the module ping (which is not an actual ICMP ping, it is a Python code running on the host).
Expected result:
|
|
We get the documentation of each module with the following command:
|
|
Or directly from the Ansible website: https://docs.ansible.com/ansible/latest/modules/modules_by_category.html
An other example is the module command which can be use to run any sort of command on the host. The command itself is set as an argument of the module, using -a. As it is the default module, you don’t have to specify it:
|
|
|
|
In my opinion, the most useful functionality about using the interactive mode is the ability to display facts. I will come back to the facts and variables with Ansible in the next article. What is important here is Ansible’s ability to discover information about the managed node and record it in variables. For example:
- Hardware information, CPU, memory, disk, network etc…
- System information such as: IP address, OS name and version…
- Many other things
The best being still to test it, for that one executes the module setup, without argument:
|
|
There is very long result, here is one part:
|
|
We can also apply a filter in order to limit the amount of information displayed. Here is an example for mount points:
|
|
All this information can be reused later in the Playbook, for example if you want to check an OS version before running a task.
Playbook composition
Now we get to the Playbook, we’ll keep it simple here too, the goal being to introduce the concepts.
Here is the Playboo we will run:
|
|
Even if a Playbook has the merit of being understandable, it deserves some explanations 😊
- Configuration de la Timezone
- Mise à jour de tous les paquets
- Ajout du Repository apt d’InfluxData (éditeur de de InfluxDB et Telegraf) avec la clé associée
- Ici on a exemple de l’utilisation des Facts vu plus haut avec la récupération de l’OS (ubuntu) et de la version (focal)
- Si InfluxDB n’est pas présent, on l’installe.
- Ici on précise present et non pas latest, on veut que InfluxDB soit installé, si c’est déjà le cas, on ne souhaite pas le mettre à jour.
- Enfn on démarre le service et on l’active (démarrage automatique)
Play
Un Playbook est divisé en plusieurs Play, ici on en retrouve deux, le premier commence dès le début avec :
|
|
Le fait qu’il commence par un tiret indique que c’est une liste, et donc qu’il est proabable qu’il y en est plusieurs.
Un Play correspond à une liste de tâches qui seront exécutées suivant les informations dans l’en-tête. Ces tâches seront exécuter dans l’odre, sur chacun des nœuds. Cependant le traitement se fait en parallèle sur les nœuds (si on a précisé un groupe au lieu d’un hôte simple).
En-tête
Un en-tête par Play, la première :
|
|
We find information that have already specified in the interactive mode such as the host (or group of hosts, we could have set all or grafana) and the remote user. We specify some additional information that we want to collect facts and get a privilege elevation (become).
Task
The Tasks are used to indicate the desired state of the node. If we look at the first Task :
|
|
The name is optional and it will be used to obtain a clear display, but also as a marker if you wish to restart the Playbook from a specific point. Then comes the call of the module timezone to which we pass an argument corresponding to the Timezone.
The objective of this Task is to have the specified Timezone configured on the host. If it is the case, it will show a status Ok, if it has to modify it, it will specify Changed. In this logic, when the Task is restarted, it will show Ok and will not make any modification.
Module
The module is the component that contains the Python code that will be executed on the node, it is the one that must be idempotent and therefore make sure to make modifications only if necessary. The modules are accessible directly on Github: https://github.com/ansible/ansible/tree/stable-2.9/lib/ansible/modules.
At the time of writing this article I am using Ansible 2.9.9, so the module are still inside the ansible/ansible project.
For the next version, 2.10, there will be a big change in the module gestion as collections are coming. Modules will be separated from the main project, I think for the best.
Explanation here : https://github.com/ansible-collections/overview
Collections : https://github.com/ansible-collections/
An example for VMware : https://github.com/ansible-collections/vmware/tree/main/plugins/modules
Exécution du Playbook
At last, we can run our first Playbook with the following command :
|
|
This time no user or node is specified on which to run the Playbook, these information is indicated in the header of the Plays. We have added -K option to be asked for a sudo password.
I encourage you to run it several times to see the difference.
Cya!