Beginners guide to install Ansible on RHEL/CentOS 8

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:

ConfigurationConroller NodeManaged Node1Managed Node2
hostnamecontroller.example.comserver1.example.comserver2.example.com
OSCentOS 8CentOS 8CentOS 8
IP Address10.10.10.610.10.10.1210.10.10.13
rpms requiredansible
python3
python3python3

 

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.

 

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 and ansible-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

2 thoughts on “Beginners guide to install Ansible on RHEL/CentOS 8”

  1. 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):

    Reply

Leave a Comment

Please use shortcodes <pre class=comments>your code</pre> for syntax highlighting when adding code.