This blog post introduces the concept of playbook in Ansible.
In the previous article, we covered the basic concepts and installation required to get started with Ansible. Hence, in this article, we will cover how to connect to managed nodes, run ad-hoc commands, and use playbooks for basic configuration.
Ad-Hoc Commands
Before running commands to apply configurations to managed nodes, we need to set up an inventory file
on the control node. This file stores the IP addresses or hostnames of the nodes we are managing.
To do this manually, we can create an inventory file in the local repository and simply list
one IP address or hostname per row. (We can automatically generate or edit inventory files for
better workflows.) We can confirm successful configurations by running the ad-hoc command
ansible all --key-file <key_file_path> -i inventory -m ping, which uses the ping module
to confirm SSH connection to the host(s).
[defaults]
inventory = inventory
private_key_file = <key_file_path>However, running long commands like the above every time defeats the purpose of using a tool like Ansible.
Hence, we can set up an ansible.cfg file in the local repository, where default parameters are stored
and referenced. (We do have an ansible.cfg in the etc/ansible folder for global default settings,
but it's generally recommended to set up a local ansible.cfg and configure local default settings
to avoid unexpected behaviors.) After setting the configuration file, we can run the same ad-hoc command
like ansible all -m ping. You can also use the gather_facts module to gather information about
the servers, such as processor details, operating system, and environment variables.
Use --limit <host> to extract information on a specific host, which can be useful for troubleshooting.
We can also start making changes to the servers using modules like apt, for example,
ansible all -m apt -a "update_cache=true". However, the above command is likely to fail.
This is because the ansible user on the managed nodes is not allowed to run the apt command
without sudo. Therefore, we need to add --become and --ask-become-pass to provide the sudo
password (which is assumed to be the same across the managed nodes) and run the apt command.
The module can be used to install, uninstall, and upgrade packages depending on the arguments
provided under the -a flag; explanations for these arguments are available in the
Ansible documentation.
Playbook
Although ad-hoc commands can apply changes to all the servers at once and are convenient, we often need to run multiple commands to set up the servers, which might require many ad-hoc commands and make it difficult to keep track of the changes being made. The commands or modules required might also differ depending on the operating systems or Linux distributions, and dealing with different hosts is troublesome with ad-hoc commands. Hence, we often use a YAML file called a playbook to run multiple tasks.
- hosts: all
become: true
tasks:
- name: update repository index for Debian/Ubuntu
apt:
update_cache: yes
when: ansible_distribution in ["Debian", "Ubuntu"]
- name: install apache2 and php package for Ubuntu (22.0.4)
apt:
name:
- apache2
- libapache2-mod-php
state: latest
when: ansible_distribution == "Ubuntu" and ansible_distribution_version = 22.0.4
- name: install apache and php for CentOS
dnf:
name:
- httpd
- php
state: latest
update_cache: yes
when: ansible_distribution == "CentOS"The example playbook for installing Apache and PHP on Ubuntu and CentOS servers is shown above,
demonstrating different functionalities of playbooks. The when statement is optional and is used
to apply modules to different servers based on the fields in gather_facts, which provides access
to operators like in and and, similar to Python. To apply changes based on the playbook,
you can use ansible-playbook --ask-become-pass install_apache.yaml. After running the command,
you should see that all the changes are successfully made. You can further rewrite the playbook
to make it more concise, as shown below.
- hosts: all
become: true
tasks:
- name: install apache and php
package:
name:
- "{{ apache_package }}"
- "{{ php_package }}"
state: latest
update_cache: yesThe above utilizes the package module in Ansible, which runs the appropriate package manager
depending on the operating system running on the server, and it also use templates for package names,
which draw values from the inventory. Thus, the inventory must add key-value pairs like
apache_package=httpd php_package=php for every host for this to work. It is debatable whether
installing packages this way is better or not (personally, I prefer the first approach,
which is more explicit and avoids hardcoding values in the inventory file), but they both
demonstrate how convenient Ansible is in managing multiple nodes.
Conclusion
In this article, we covered the basics of ad-hoc commands and playbooks, which use modules to install different packages on the managed nodes. You can check the Ansible documentation cited below for details of the available modules. The article has already demonstrated the some convenience that Ansible offers in configuring multiple managed nodes with different operating systems and processors using just one or a few commands, but in the next article, we will cover more features that enable even more flexible configurations of managed nodes in Ansible.
Resources
- Ansible. n.d. Ansible Documentation. Ansible Community Documentation.
- Learn Linux TV. 2020. Getting started with Ansible. YouTube.