In this article I will share Step-by-Step tutorial to install Ansible on RHEL/CentOS 8 Linux environment. We will setup a three node environment with one controller node and two managed nodes. But before we start with the steps to install Ansible on RHEL 8 or CentOS 8, let us understand what is Ansible and how it works (if you don't already know)
What is Ansible and how it works?
Ansible is a modern automation tool that makes our lives easier by helping us manage our servers, deployments, and infrastructure. We declare what we want and let Ansible do the hard work. Some of the things that Ansible can do are as follows:
- Install and configure software
- Manage users and databases
- Deploy applications
- Remote execution
- Manage Infrastructure as Code
Ansible has certain distinct advantages over other similar tools.
- Ansible is agentless. So we do not need to install any software on the servers that are to be managed. It does require Python runtime on the servers and a SSH server on remote hosts.
- Ansible supports both push and pull modes. So we can execute Ansible code from a central control machine to make changes on remote machines or the remote machines can pull configuration from a well defined source periodically.
- Code for Ansible is written in YAML (http://yaml.org/), which stands for YAML Ain't Markup Language.
- Ansible does not try to re-invent the wheel. Hence it uses SSH as a transport and YAML as a Domain Specific Language (DSL).
Lab Environment
I have created three Virtual Machines running on Oracle Virtual Box installed on a Linux Server. One of these VM will be the controller on which we will install ansible while the other two VMs will act as a managed server (client) on which we will perform different tasks using playbook.
Below are the configurations of these 3 VMs:
Configuration | Conroller Node | Managed Node1 | Managed Node2 |
---|---|---|---|
hostname | controller.example.com | server1.example.com | server2.example.com |
OS | CentOS 8 | CentOS 8 | CentOS 8 |
IP Address | 10.10.10.6 | 10.10.10.12 | 10.10.10.13 |
rpms required | ansible python3 |
python3 | python3 |
Step 1: Update /etc/hosts
You can either configure BIND DNS server to resolve hostname or alternatively update /etc/hosts file with the hostname and IP details of your controller and managed hosts in your setup
[root@controller ~]# cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 10.10.10.6 controller controller.example.com 10.10.10.12 server1 server1.example.com 10.10.10.13 server2 server2.example.com
Step 2: Install Ansible
I will share the steps to install Ansible on both RHEL and CentOS 8 using different methods:
Method 1: CentOS 8 Install Ansible using EPEL repo
For CentOS 8 install ansible, in this method we will show installation via EPEL repo. First manually install EPEL repo on your CentOS 8 Linux node:
[root@controller ~]# dnf -y install epel-release
Now once epel repo is installed you can search for ansible package
# dnf search ansible ======================================= Name Exactly Matched: ansible ======================================= ansible.noarch : SSH-based configuration management, deployment, and task execution system
So you can now install ansible.noarch
rpm on the controller node using dnf or yum
[root@controller ~]# dnf install -y ansible.noarch
Method 2: CentOS 8 Install Ansible using pip
In the next method for CentOS 8 install ansbile you can also use pip. To install ansible via pip install the below rpms on your controller node:
[root@controller ~]# dnf install python3 python3-pip -y
Next install ansible using pip3
as a normal user "deepak
"
[deepak@controller ~]$ pip3 install ansible --user
Requirement already satisfied: ansible in /usr/lib/python3.6/site-packages
Requirement already satisfied: jinja2 in /usr/lib/python3.6/site-packages (from ansible)
Requirement already satisfied: PyYAML in /usr/lib64/python3.6/site-packages (from ansible)
Requirement already satisfied: cryptography in /usr/lib64/python3.6/site-packages (from ansible)
Requirement already satisfied: MarkupSafe>=0.23 in /usr/lib64/python3.6/site-packages (from jinja2->ansible)
Requirement already satisfied: idna>=2.1 in /usr/lib/python3.6/site-packages (from cryptography->ansible)
Requirement already satisfied: asn1crypto>=0.21.0 in /usr/lib/python3.6/site-packages (from cryptography->ansible)
Requirement already satisfied: six>=1.4.1 in /usr/lib/python3.6/site-packages (from cryptography->ansible)
Requirement already satisfied: cffi!=1.11.3,>=1.7 in /usr/lib64/python3.6/site-packages (from cryptography->ansible)
Requirement already satisfied: pycparser in /usr/lib/python3.6/site-packages (from cffi!=1.11.3,>=1.7->cryptography->ansible)
Method 3: RHEL 8 Install Ansible
To install ansible on RHEL 8 you must first register your RHEL 8 node. Now I have already registered my RHEL 8 node to Red Hat Network.
Next you can enable the Red Hat Ansible Engine Repository:
# subscription-manager repos --enable ansible-VERSION-for-rhel-8-x86_64-rpms
Currently at the time of writing this article ansible-2.8.5
was the latest
# subscription-manager repos --enable ansible-2.8-for-rhel-8-x86_64-rpms
Next use dnf
on RHEL 8 install ansible
# dnf install ansible -y
Based on your environment once you install Ansible, next you can see this will also install python3
as dependency
[root@controller ~]# ansible --version ansible 2.8.5 config file = /etc/ansible/ansible.cfg configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] ansible python module location = /usr/lib/python3.6/site-packages/ansible executable location = /usr/bin/ansible python version = 3.6.8 (default, May 21 2019, 23:51:36) [GCC 8.2.1 20180905 (Red Hat 8.2.1-3)]
Step 3: Install Python on managed nodes
Now we don't need to install ansible on the managed hosts but we must install python3
on all the managed hosts:
You can use dnf
to search and install python3
package. I have installed below rpm on both my managed host where dnf
will handle all the dependencies
[root@server2 ~]# rpm -qa | grep python36 python36-3.6.8-2.module_el8.1.0+245+c39af44f.x86_64 [root@server1 ~]# rpm -qa | grep python36 python36-3.6.8-2.module_el8.1.0+245+c39af44f.x86_64
Step 4: Create normal user
This is important as we will use this user to perform all ansible related tasks. For the sake of this article I will create a user "ansible
"
[root@controller ~]# useradd ansible
Assign a password to this user
[root@controller ~]# passwd ansible
Repeat the same on managed nodes i.e. create the same user on all your managed hosts:
[root@server1 ~]# useradd ansible [root@server1 ~]# passwd ansible
[root@server2 ~]# useradd ansible [root@server1 ~]# passwd ansible
Step 5: Create and distribute SSH keys to managed nodes
Now we must enable password less login between our controller node and all the managed hosts. So we can configure passphrase based login using ssh-keygen
Login or switch user to "ansible
" and execute ssh-keygen
in the below format. With -P
we assign a null password to the key pair
[ansible@controller ~]$ ssh-keygen -t rsa -P ""
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ansible/.ssh/id_rsa):
Created directory '/home/ansible/.ssh'.
Your identification has been saved in /home/ansible/.ssh/id_rsa.
Your public key has been saved in /home/ansible/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:NYGxDBObnWXDGHpM1MtvnMQpOWS59MsVtTZFfl//Ym0 ansible@controller.example.com
The key's randomart image is:
+---[RSA 2048]----+
| +o=B=. o+|
| @o=Bo ..o|
| + B=o* . =+|
| . .B.= o *|
| S B + o|
| B ..|
| . o E|
| . o |
| |
+----[SHA256]-----+
This will create public and private key pair in the home directory under ~/.ssh/
. Now since we have a public and private key pair, copy public key to target managed server. We use ssh-copy-id
as it saves time and performs all the tasks required to enable passphrase based login.
[ansible@controller ~]$ ssh-copy-id server1 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/ansible/.ssh/id_rsa.pub" The authenticity of host 'server1 (10.10.10.12)' can't be established. ECDSA key fingerprint is SHA256:RxJuKeoiMc/+4H7IO52YTFOStE3hgd7ulMwjpAVGDZs. Are you sure you want to continue connecting (yes/no)? yes /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys ansible@server1's password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh 'server1'" and check to make sure that only the key(s) you wanted were added.
Repeat the same for other managed hosts
[ansible@controller ~]$ ssh-copy-id server2
Also copy the public key for localhost on controller node. This will also be required later.
[ansible@controller ~]$ ssh-copy-id controller
Verify password less SSH authentication
The ssh-copy-id
command will copy the public key we just created to server1
and server2
and append the content of the key to ansible user's authorized_keys
file under ~/.ssh
. Here .ssh is a hidden directory.
You can perform a ssh to managed host to make sure you can connect to the server without giving any password or passphrase.
[ansible@controller ~]$ ssh server1
Last login: Wed Jan 29 20:24:46 2020
[ansible@server1 ~]$
So we were able to connect to our server1
managed host without any password here.
Similarly verify SSH from controller to other managed hosts
Step 6: Configure privilege escalation using sudo
Since our ansible
user would need privilege escalation we will create a new rule for ansible
user using a new file under /etc/sudoers.d
[root@controller ~]# cat /etc/sudoers.d/ansible ansible ALL=(ALL) NOPASSWD: ALL
Add the same rule on all your managed hosts
[root@server1 ~]# echo "ansible ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/ansible [root@server2 ~]# echo "ansible ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/ansible
Step 7: Configure Ansible on controller node
After we install ansible let us configure ansible to run some ad-hoc commands.
Create custom project
We will create a project "base" under our home directory:
[ansible@controller ~]$ mkdir base
Create and Configure ansible.cfg
The configuration file for Ansible can exist in a few different locations, where the first file found will be used. The search involves the following:
- ANSIBLE_CFG: This environment variable is used, provided it is set
- ansible.cfg: This is located in the current directory
- ~/.ansible.cfg: This is located in the user's home directory
- /etc/ansible/ansible.cfg
We will create an ansible.cfg
under the base
project. In this we identify how to connect remote hosts.
[ansible@controller base]$ cat ansible.cfg
[defaults]
remote_user = ansible ; use ansible user
host_key_checking = false
inventory = inventory ; Inventory file exists in the current directory
log_path = base.log ; base.log file will be created/used as log file
[privilege_escalation]
become = True ; Become someone else to perform tasks
become_method = sudo ; use sudo
become_user = root ; Become root user
become_ask_pass = False , Don't ask for password with sudo
inventory argument tells the relative filename which contains the list of managed hosts. So you must create a file "inventory" under your project directory.
#
) and semicolon (;
) are allowed as comment markers when the comment starts the line. However, if the comment is inline with regular values, only the semicolon is allowed to introduce the comment
Create static inventory file
- Inventory contains a list of hostname or IP addresses. Although you should avoid using IP Addresses in the inventory
ansible.cfg
defines how privileges are escalated. You can create this per project or use a generic file- It is common to work with project directories that contain these files
- In Ansible, we have static and dynamic inventory. For now we will only use static inventory
- Even ad hoc actions performed on the localhost require an inventory, though that inventory may just consist of the localhost.
- The inventory is the most basic building block of Ansible architecture.
- When executing ansible or ansible-playbook, an inventory must be referenced. Inventories are either files or directories that exist on the same system that runs ansible or ansible-playbook.
- The location of the inventory can be referenced at runtime with the
--inventory-file
(-i
) argument, or by defining the path in an Ansible config file.
[ansible@controller base]$ cat inventory server1.example.com server2.example.com
To list the matching hosts using our inventory file use below command. This will not execute any command on the inventory nodes:
[ansible@controller base]$ ansible all --list-hosts hosts (2): server1.example.com server2.example.com
Step 8: Running ad-hoc commands
- Ad hoc commands in Ansible are used to perform tasks or operations that are needed on an ad hoc basis, or only once, based upon the requirement.
- In other words, these are tasks that a user wants to be performed on the fly but doesn't want to be saved for later use.
- Ansible modules can be called using ansible command
- Use
ansible-doc -l
for a list of modules andansible-doc <modulename>
for information about module options - Specify which module to use, using
-m
- The "
command
" module is default, and does not have to be specified
Examples:
ansible all -m command -a id ansible all -m shell -a env
In the below ansible example we will check the available memory on our managed hosts using "free -m
" command
[ansible@controller base]$ ansible all -m shell -a "free -m"
server1.example.com | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 4789 201 4100 8 487 4353
Swap: 955 0 955
server2.example.com | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 4789 204 4102 8 482 4350
Swap: 955 0 955
Next we will try to add some content in a file on server1.example.com
[ansible@controller base]$ ansible server1.example.com -m copy -a "content='Hello, My name is deepak' dest=~/hello.txt"
server1.example.com | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"checksum": "3fa09515585e1da48417b015f7bd40cd665d9cf2",
"dest": "/root/hello.txt",
"gid": 0,
"group": "root",
"md5sum": "4f5f6876ea14b5e5d005059b0112dd32",
"mode": "0644",
"owner": "root",
"size": 24,
"src": "/home/ansible/.ansible/tmp/ansible-tmp-1580546381.2696378-140602648211740/source",
"state": "file",
"uid": 0
}
Lastly I hope the steps from the article to install ansible on RHEL/CentOS 8 Linux was helpful. So, let me know your suggestions and feedback using the comment section.
References:
Learn Ansible
Donot put comments in the ansible.cfg file, otherwise you will get errors like this:
[ansible@centos8 ~]$ ansible all –list-hosts
[WARNING]: Unable to parse /home/ansible/inventory # Inventory file exists in the current directory as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match ‘all’
hosts (0):
Thank you for highlighting this. I have corrected and also added a HINT.