Ansible is a very popular configuration management tool designed to streamline the process of controlling a large number of servers. It can automate the process of setting up new servers and installing applications with a single command or file. You can control as many servers and run processes on them simultaneously from a single node. Ansible doesn't require any special software required to be installed on the server nodes and can control them over SSH.
In this guide, we will learn how to install and configure Ansible on an Ubuntu 22.04 server.
Prerequisites
- Two or more server systems running Ubuntu 22.04 with OpenSSH server installed.
- Both the server and nodes are accessible via public IP addresses.
- A non-root user with sudo privileges is set up on the Ansible server and a root user with a password is set up on the Ansible clients.
Step 1 - Install Ansible
We will use Ansible's official repository to install its latest version. Add Ansible's official repository to the server.
$ sudo add-apt-repository ppa:ansible/ansible
Fortunately, Ubuntu ships with Ansible 2.9 which is what we will install. Run the following command to install Ansible.
$ sudo apt install ansible -y
Test your installation by running the following command.
$ ansible --version ansible [core 2.13.3rc1] config file = /etc/ansible/ansible.cfg configured module search path = ['/home/navjot/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] ansible python module location = /usr/lib/python3/dist-packages/ansible ansible collection location = /home/navjot/.ansible/collections:/usr/share/ansible/collections executable location = /usr/bin/ansible python version = 3.10.4 (main, Jun 29 2022, 12:14:53) [GCC 11.2.0] jinja version = 3.0.3 libyaml = True
Install and activate the python3-argcomplete
package to set up Ansible bash completion support.
$ sudo apt install python3-argcomplete $ sudo activate-global-python-argcomplete3
You can now press the Tab key to get a list of options for the bash shell.
Step 2 - Setting up Inventory File
To be able to connect to multiple hosts, you need a file that will record the details of the nodes. This file is called the Inventory file.
Depending on how many servers you want to control, Ansible's Inventory file also allows you to arrange them in groups and subgroups. You can also set custom variables applicable to select hosts or groups which can be further used while passing on the instructions.
Ansible ships with a default Inventory file available at /etc/ansible/hosts
. Open it with the Nano editor.
$ sudo nano /etc/ansible/hosts
Paste the following code at the bottom of the file.
[servers] server1 ansible_host=203.0.113.111 server2 ansible_host=203.0.113.112 server3 ansible_host=203.0.113.113 [all:vars] ansible_python_interpreter=/usr/bin/python3
The servers
section defines the list of nodes you want to connect to. You can create as many groups to arrange servers in multiple groups.
The all:vars
group sets the ansible_python_interpreter
parameter on all the hosts in the inventory. It makes sure that Ansible uses Python 3 executable instead of Python 2 which has been removed from recent Ubuntu versions.
When you’re finished, save the file by pressing Ctrl + X and entering Y when prompted to confirm your changes.
Note: You can also create your Inventory file in any location of your choice which you can then pass off by using the -i
parameter while running Ansible commands.
You can check your inventory list by the following command.
$ ansible-inventory --list -y all: children: servers: hosts: server1: ansible_host: 203.0.113.111 ansible_python_interpreter: /usr/bin/python3 server2: ansible_host: 203.0.113.112 ansible_python_interpreter: /usr/bin/python3 server3: ansible_host: 203.0.113.113 ansible_python_interpreter: /usr/bin/python3 ungrouped: {}
Organizing Servers into Groups and Subgroups
This is a useful tip if you have a lot of servers with some servers performing specific functions. For example, you can use this method to group web servers and database servers separately. You can even make a host part of multiple groups. To achieve that, your inventory file should look something like the following.
[webservers] 203.0.113.111 203.0.113.112 [dbservers] 203.0.113.113 server_hostname [development] 203.0.113.111 203.0.113.113 [production] 203.0.113.112 server_hostname
Step 3 - Set up SSH Keys
For Ansible to be able to connect to the servers, you must configure SSH keys between your Ansible server and hosts specified in the inventory file. This will work only if the Ansible clients don't have a public key enabled and have a root account enabled with a password.
Use the following steps to create and set up an SSH key for Ansible and its nodes.
Create the key for Ansible.
$ ssh-keygen -t rsa -b 4096 -C "Ansible key"
Copy the public key to your accounts on the remote servers. For this, we will use the ssh-copy-id
command.
$ ssh-copy-id -i $HOME/.ssh/id_rsa.pub root@203.0.113.111 $ ssh-copy-id -i $HOME/.ssh/id_rsa.pub root@203.0.113.112 $ ssh-copy-id -i $HOME/.ssh/id_rsa.pub root@203.0.113.113
That's all. Now Ansible should be able to talk to your servers.
Setup SSH Keys on Nodes with existing Public Key
If the clients already have public keys enabled, then you will have to follow certain extra steps. For that, you need to create a new user accessible only by Ansible on every node server. That user will have sudo privileges accessible without a password and can be accessed only from your ansible server.
To create a ansible
user, run the following command.
$ sudo adduser ansible
Choose a strong password and leave all the other fields empty.
Now, configure password-less sudo access to this user through the following command.
$ echo "ansible ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/ansible
Now, that you have the new user added and configured, you can copy the SSH key from your ansible server to the node server using the following command.
$ ssh-copy-id ansible@203.0.113.111
You will be prompted a password for the ansible user. Enter it and the SSH key will be copied.
Next, disable the password-based login for the ansible
user on the node server.
$ sudo usermod -L ansible
Now, your node server is only accessible from the Ansible server since only that server has the public key for it and you can't use it with sudo privileges on the node server directly since password login is disabled.
You will have to repeat these steps for each node server. Also, replace the root
user with the ansible
user in this tutorial.
Step 4 - Test Connection
After setting up the inventory file and SSH keys, we should check if Ansible can connect to the servers.
Type the following command to check the connection. This command will test the connection to all the servers from the inventory file.
$ ansible all -m ping -u root
This command uses Ansible's ping module to run a connectivity test on all the servers. You should get an output like the following.
server1 | SUCCESS => { "changed": false, "ping": "pong" } server2 | SUCCESS => { "changed": false, "ping": "pong" } server3 | SUCCESS => { "changed": false, "ping": "pong" }
If this is the first time you are using Ansible, you will be asked to confirm the authenticity of all the servers. When prompted, type yes
and press ENTER
to confirm.
Step 5 - Run Some basic commands
Let us run some basic commands on the servers using Ansible. To run any command on the server, the following format is used.
$ ansible all -a "command" -u <username>
Check Disk Usage
First, let us check disk usage on all our servers.
$ ansible all -a "df -h" -u root server1 | CHANGED | rc=0 >> Filesystem Size Used Avail Use% Mounted on tmpfs 198M 972K 197M 1% /run /dev/sda2 50G 3.9G 44G 9% / tmpfs 989M 0 989M 0% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock tmpfs 198M 4.0K 198M 1% /run/user/1000 server2 | CHANGED | rc=0 >> Filesystem Size Used Avail Use% Mounted on tmpfs 198M 922K 197M 1% /run /dev/sda2 50G 4.9G 43G 10% / tmpfs 989M 0 989M 0% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock tmpfs 198M 4.0K 198M 1% /run/user/1000
Target Individual Hosts and Groups
Until now, we were running commands on all remote servers at once. But that is not always the case. To run a command on just one server, you should use the following format.
$ ansible server1 -a "uptime" -u root server1 | CHANGED | rc=0 >> 21:38:26 up 11 min, 2 users, load average: 0.00, 0.20, 0.19
The above command checks the uptime on server1 from the inventory group.
You can also target multiple servers using the following format.
$ ansible server1:server2 -m ping -u root
You can also target groups or subgroups from the inventory file directly.
$ ansible groupname -m ping -u <username>
Update all servers
For this tutorial, we are assuming all the remote servers are running Debian or Ubuntu OS.
Run the following command to update the software on all your servers.
$ ansible all -m apt -a "update_cache=yes upgrade=yes" -u root
The -m
parameter defines the module for Ansible to run. The -a
parameter refers to the arguments or commands for the associated module. Here, we are using the apt
module of Ansible to update servers just like we used the ping
module in our last example. The update_cache
updates the APT cache on the server and upgrade=yes
tells Ansible to run the apt upgrade
command.
If you are using the ansible
user as documented above, you need to modify the ansible
command to run with elevated sudo privileges.
$ ansible server2 -m apt -a "update_cache=yes upgrade=yes" -K -b -u ansible
Here, -K
asks for a privilege escalation password. -b
runs the ansible operation with become
which allows you to be another user. Both variables combined allow ansible to run with elevated sudo privileges. You will need to use this for all commands which require sudo privileges.
Sometimes, some of these update commands may require a reboot so run the following command to reboot all your servers.
$ ansible all -a "reboot" -u root
These were just some of the basic commands you can run using Ansible.
Step 6 - Introduction to Playbooks
The above commands allow you to run one-off tasks but if you want to set up multiple servers or run the same sequence of commands on multiple servers, you need to set up playbooks. Playbooks are files written in YAML and contain instructions to automate a sequence of tasks for setting up applications and services.
We will now create a playbook to install Nginx and set up an HTML page on the Ansible node. Create a directory for Ansible in your home directory.
$ mkdir ~/ansible
Create and open the playbook file for editing.
$ cd ~/ansible $ nano testplaybook.yml
Playbooks use the YAML format to define one or more plays. A play is a set of ordered tasks arranged in a way to automate a process. The plays are defined as a YAML list.
The first step to define a play is to determine which hosts are the target using the hosts: all
directive. The become
directive is used to indicate that the following tasks must be executed by a super user.
We will define three tasks: one to add a user, one to upgrade all the packages, and the last one to install the Nginx server. The vars
section of the playbook is used to define custom variables. We define two variables, one for the user we need to add, and the second one to define the state of the package we need to install. To use the variable, we need to enclose the variable name between double curly brackets.
The ansible.builtin.user
module is used to add a new user with sudo privileges. To add the user, we are using the name
, password
and the group
variables. The group
variable is set to sudo
to give superuser permissions to the user. You can't put the plaintext-password in the playbook file, therefore, we will add an SHA hashed secret. We will use the mkpasswd
utility for that. To install it, run the following command to install the whois
package.
$ sudo apt install whois
Generate the hashed password. You will be asked for the usual password and will be given a hashed string for it. Note down the hashed key to be used in the playbook file.
$ mkpasswd --method=sha-512 Password: $6$dGbprm2oVqClDDDh$Epk6r5eXYkYBaQpQpP.H7VCdz0g9Aj0aO8hjy/WXq4WmfQ7GvQP2/cl/cNhd7.LRFuCKix9uCF2t8X5/Pv0Lk1
The update_cache
directive is to update the system's repository list just like the apt update
command and the upgrade: dist
directive tells Ansible to perform the system upgrade. The third task is self-explanatory which installs the latest version of the Nginx server.
Based on the information we discussed, paste the following code into the file. Paste the hashed key you got as the value for the password
variable.
--- - name: Test playbook hosts: all become: true vars: state: latest user: navjot tasks: - name: Add the user {{ user }} ansible.builtin.user: name: "{{ user }}" password: '$6$dGbprm2oVqClDDDh$Epk6r5eXYkYBaQpQpP.H7VCdz0g9Aj0aO8hjy/WXq4WmfQ7GvQP2/cl/cNhd7.LRFuCKix9uCF2t8X5/Pv0Lk1' group: sudo - name: Upgrade all apt packages apt: update_cache: yes upgrade: dist - name: Install the {{ state }} of package "nginx" apt: name: "nginx" state: "{{ state }}"
Save the file by pressing Ctrl + X and entering Y when prompted to confirm your changes.
To execute the playbook, run the following command. The --ask-become-pass
flag will ask for your root password to perform an elevated operation.
$ ansible-playbook testplaybook.yml --ask-become-pass
You will get the following output.
BECOME password: PLAY [Test playbook] *************************************************************************************************** TASK [Gathering Facts] ************************************************************************************************* ok: [server1] ok: [server2] ok: [server3] TASK [Add the user casablanca] ***************************************************************************************** changed: [server3] changed: [server2] changed: [server1] TASK [Upgrade all apt packages] **************************************************************************************** changed: [server1] changed: [server2] changed: [server3] TASK [Install the latest of package "nginx"] *************************************************************************** changed: [server3] changed: [server2] changed: [server1] PLAY RECAP ************************************************************************************************************* server1 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 server2 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 server3 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
This confirms that your playbook ran successfully.
If you are using a custom inventory file, you need to include the location of the file in the command as follows.
$ ansible-playbook -i /etc/ansible/custominventory testplaybook.yml --ask-become-pass
Conclusion
That wraps up our tutorial on installing and configuring Ansible on Ubuntu 22.04 server. If you have any questions, post them in the comments below.