Welcome to Summary, a series where I summarize a concept or tool for you so you can understand your coworkers when they talk about it. In this post, I cover Ansible: a smooth, powerful, and versatile automation tool.
Ansible shines brightest in two fields. The first is configuration management, which is the idea of keeping the configuration between a cluster of computing devices consistent and predictable. The other is automatic deployment, which is the process of automatically and continuously deploying software to a target machine or machines, usually for testing or demonstrations. Your idea of Ansible should not be limited by these fields, however, as it is capable of much more.
To manage any number of nodes (computational devices), Ansible requires a master node. A master node is a machine that has access to all other nodes and is where Ansible itself resides.
The master node must be running Linux; however, child nodes can run either Windows or Linux. Apart from installing Ansible on the master node, child nodes must have SSH and any recent version of Python. SSH is the mechanism that the Ansible master uses to remotely access child nodes, while Python is what it used to perform any tasks. Because of the use of both these tools, Ansible is agentless, meaning that no additional Ansible-specific software is required to exist on child nodes.
Ansible isn't magic and doesn't know what its child nodes are until you configure it yourself. The inventory file is what Ansible provides to you to set them. It exists at
/etc/ansible/hosts and accepts either the INI or YAML formats. Here's an example of what one might look like (YAML):
all: children: devgroup: hosts: 10.0.0.133: 10.0.0.209: 10.0.0.103: demogroup: hosts: 10.0.0.182: 10.0.0.114: 10.0.0.235:
There is a root level "all" group that can have any number of child groups. Child groups can also recursively have children and can share nodes. Inventory allows for a high level of control and organization for your infrastructure that most small-scale environments may never need but is available nonetheless.
When trying to perform any task in Ansible, you will need to provide the group of child nodes you would like to perform it on. It's important to note that if you improperly configure the inventory file, it is difficult to tell since Ansible isn't very straightforward with its error messages about it. Make sure to double-check your formatting after editing!
For any host listed in your inventory file, Ansible expects that you have already set it up. As I mentioned before, each child of the master must have SSH and Python. Further, the SSH access to the child from the master must be passwordless through a tool such as
ssh-copy-id. If you can manually access the target machine with SSH without a password, so can Ansible.
Ansible is designed to be easy for both beginners and professionals to use, and to accomplish this, the Ansible team created modules. Modules are at the core of Ansible and are the basic building block for any action. They are pre-defined functionality for almost any automation task that you can fathom. There are modules for networking, cryptography, databases, files, and a ton more. There are many, and it would be more than impractical for me to list them all here. If you want to see a list of all of the modules provided by Ansible, click this link.
Modules are designed to be idempotent when possible. That means if you run the same module multiple times in a row, the module will only do its job once. For example, if you try to transfer a file that already exists on a target, nothing will happen. This philosophy saves Ansible from being inefficient and allows you to be more liberal in terms of what modules you run. If you want to transfer a file to a group of nodes in which some already contain it, it doesn't hurt to run the module to make sure; Ansible will only perform the task for the nodes that don't.
For small tasks like sending a file or restarting a service across a group of nodes, it is possible to run modules in isolation. To do this, you can run the
ansible command itself, specifying the inventory group, username on the machines (it must be shared amongst them all), and the module to run. For example, the "Hello World" of Ansible is to ping every child node to ensure the master can contact them with
ansible all -u username -m ping. In most situations, however, modules need to run in groups.
In a typical environment, when multiple modules must run in succession to accomplish a more significant overall task, playbooks can be leveraged. Ansible requires users to write playbooks in YAML, and the file can be stored anywhere. A playbook must contain the target group from the Ansible inventory file, the username to use for all the machines, and a sequential list of modules to run called the task list. Here's an example playbook to install the "tmux" and "neovim" packages with
--- - hosts: demogroup remote_user: username tasks: - name: Ensure tmux is installed apt: name: tmux state: present - name: Ensure neovim is installed apt: name: neovim state: present
Ansible detects a lot of information about the target nodes, including what operating system they are running, and even what Linux distro it has if it's a Linux machine. Ansible stores these values in variables called "facts" that are available in playbooks. It's possible to use these variables to run tasks on nodes that require different tools selectively. For example, it's possible to use
apt for Debian systems and
yum for RedHat systems.
ansible-playbook command is available to run an Ansible playbook. A playbook is executed by running
ansible-playbook X, where
X is the path of the playbook to run.
Roles are a way to sequentially group a group of tasks that are reusable between multiple playbooks. Take compiling software from scratch, for example. Software compilation takes several steps. It requires the transfer of many files to the target machine, the installation of several packages, and several other commands to perform their tasks. Multiple, unrelated playbooks may require the compiled software, and re-writing the tasks for each playbook would be inefficient. Instead, the tasks specific to the compilation can be separated and imported by each playbook that requires them.
Roles require a concrete file structure to work, and they get into advanced Ansible. However, anybody that uses Ansible regularly should also be using roles.
To learn Ansible, I would suggest looking at the official documentation, as it's well-written. The "Getting Started" guide is an excellent place to start. Below are the concepts covered in this post, and the documentation links for them:
- Inventory: https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#intro-inventory
- Modules: https://docs.ansible.com/ansible/latest/modules/modules_by_category.html
- Playbooks: https://docs.ansible.com/ansible/latest/user_guide/playbooks.html#working-with-playbooks
- Roles: https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html
- Best Practices:: https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html