8i | 9i | 10g | 11g | 12c | 13c | 18c | 19c | 21c | 23c | Misc | PL/SQL | SQL | RAC | WebLogic | Linux

Home » Articles » Misc » Here

Ansible : First Steps

This article presents the first steps to get you up and running with Ansible. We will be using Oracle Linux 8 for all the virtual machines.

Related articles.

What is Ansible and Why Would You Use It?

Ansible is an open-source tool that enables infrastructure as code. It can be used for the whole lifecycle of your infrastructure, including provisioning, configuration management, and application-deployment.

But can't I do this already with shell scripting? Yes, you can. Ultimately Ansible is just running commands on your servers that you could put into a shell script and check into version control. If you are currently writing robust shell scripts with good error handling, and making sure they can be rerun to make new changes without destroying your server, then Ansible isn't doing anything new for you. In reality, many people don't write robust shell scripts with great error handling. In some cases, rerunning their scripts will destroy the system they've just built.

Ansible allows us to define a system, make changes over time and play only those changes against the server. This means the definition of the system stays up to date, and acts as a permanent record of how the system was built. If we need to rebuild the system in the future, when moving to a newer version of the operating system, our existing Ansible scripts can often be used with little or no modifications to build an exact replica of our original system.

Ansible encourages us to write generic and reusable pieces of code, that allow us to build systems in more consistent and reliable way. In some cases allowing us to use the same scripts across multiple Linux distributions.

Ansible is not magic. Just like shell scripts can be good or bad, Ansible can be good or bad depending on how it is used. If you follow the rules, Ansible will simplify your infrastructure lifecycle.

Where Do We Install Ansible?

There is nothing to stop us installing Ansible on every server and running Ansible playbooks on the servers directly, but typically we would install Ansible on a central administration server, or Ansible control host, and perform all operations from that server. This has a number of advantages.

So for now we will assume we are going to install Ansible on a central server, and administer all other servers from that central server. For the remainder of the article, we will assume we have the following servers.

192.168.56.100 ansible-server.localdomain
192.168.56.101 appserver1.localdomain
192.168.56.102 appserver2.localdomain
192.168.56.103 database1.localdomain

The following diagram may make it easier for you to visualise the relationship.

Ansible Topology

There is a vagrant build for these servers here.

Ansible Installation

The installation guide gives the steps for a number of Linux distributions, but we will follow the steps for Installing Ansible on RHEL, CentOS, or Fedora.

Ansible is part of the EPEL repository. Oracle Linux makes this available from yum.oracle.com. The following commands enable the EPEL repository, and install Ansible.

# dnf install -y epel-release
# dnf install -y ansible

We check the Ansible version installed using the --version flag.

# ansible --version
ansible 2.9.27
  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, Nov 10 2021, 06:50:23) [GCC 8.5.0 20210514 (Red Hat 8.5.0-3.0.2)]
#

Inventory

The inventory identifies all the hosts managed by Ansible. These can be listed by IP address or more typically by fully qualified names. They don't actually need to be fully qualified, so long as they resolve to an IP address. The hosts can be grouped, so commands can be run against all hosts in a group. A single host can be present in multiple groups, or ungrouped. The inventory is supported in multiple formats, but we will stick to the basic format here.

The following actions are only needed on the central administration server, called "ansible-server".

We don't have DNS set up for these servers, so we add the IP addresses of the hosts to our "/etc/hosts" file. We have four hosts, representing the central administration server, two application servers and one database server.

# cat >> /etc/hosts <<EOF
192.168.56.100 ansible-server.localdomain ansible-server
192.168.56.101 appserver1.localdomain appserver1
192.168.56.102 appserver2.localdomain appserver2
192.168.56.103 database1.localdomain database1
EOF

We could create a default inventory by adding hosts to the "/etc/ansible/hosts" file, but needing root access to edit this annoying, so we will create an inventory in our current user.

$ cat >> ~/git/oraclebase/ansible/first_steps/inventory.ini <<EOF
ansible-server.localdomain

[appservers]
appserver1.localdomain
appserver2.localdomain

[databases]
database1.localdomain
EOF

The inventory can contain lots of other things including aliases and variables, but we are going to ignore those for now. For more information about the inventory, read the documentation.

The examples in this series use a single inventory that contains all hosts. In reality you may want to use several distinct inventories to separate the hosts. For example:

There are no rules. The important thing is it has to work for you.

SSH Configuration

We will require SSH access to all hosts when performing administration tasks. The simplest way to handle this is to add the public key from your central Ansible server to the "authorized_keys" file on each managed host.

There is more information about setting up passwordless SSH here.

We create a key pair on the central Ansible server with the following command. I used the name "ansible_key" for the key name, but you could use the defaults if you prefer. It's more secure to use a passphrase, but if you want you can hit return on the passphrase prompts also.

$ /usr/bin/ssh-keygen -t rsa

We now have a private and public key on the central server.

$ ls ~/.ssh
ansible_key  ansible_key.pub
$

We will let the central administrative server connect to itself using passwordless SSH, by copying the public key to the "authorized_keys" file.

$ cat ~/.ssh/ansible_key.pub >> ~/.ssh/authorized_keys
$ chmod 600 ~/.ssh/authorized_keys

We push the public key to the managed hosts as authorized keys using the ssh-copy-id command. We will be prompted to confirm we want to connect to the remote host, and for the password for the remote host.

# appserver1
ssh-copy-id -i ~/.ssh/ansible_key appserver1.localdomain

# appserver2
ssh-copy-id -i ~/.ssh/ansible_key appserver2.localdomain

# database1
ssh-copy-id -i ~/.ssh/ansible_key database1.localdomain

If your key has a passphrase, you will probably want to cache the password, so you are not prompted each time you use it in your current terminal session. You can do this by starting an SSH agent and caching the passphrase for your key. Run the following commands, and enter the passphrase when prompted. If you didn't give your key a passphrase, you can ignore this step.

$ ssh-agent bash
$ ssh-add ~/.ssh/ansible_key

We should now have passwordless SSH working between the central server and managed hosts, provided we use the key.

$ ssh -i ~/.ssh/ansible_key appserver1.localdomain date
Sun Feb 20 12:34:28 UTC 2022
$ ssh -i ~/.ssh/ansible_key appserver2.localdomain date
Sun Feb 20 12:34:33 UTC 2022
$ ssh -i ~/.ssh/ansible_key database1.localdomain date
Sun Feb 20 12:34:37 UTC 2022
$

Setting Default Configuration

We are now able to run Ansible commands provided we explicitly include the private key (--private-key or --key-file) and the inventory (-i, --inventory or --inventory-file) file we want to use.

$ ansible --private-key ~/.ssh/ansible_key --inventory inventory.ini database1.localdomain -m ping

To simplify things we can set the default key and the inventory files in the "ansible.cfg" file.

$ cat > ansible.cfg <<EOF
[defaults]
inventory = /home/vagrant/git/oraclebase/ansible/first_steps/inventory.ini
private_key_file = ~/.ssh/ansible_key
interpreter_python = auto_legacy_silent
EOF

We no longer need to include them in the Ansible commands.

$ ansible database1.localdomain -m ping

An alternative to having the "ansible.cfg" file in the current directory is to place a file with the same contents called ".ansible.cfg" in the home directory for the user.

Running Your First Ansible Command

We can now use Ansible to perform an action on the hosts by name, using the group name they are part of, or using the "all" and "ungrouped" default groups. In this case we use the "ping" module, which makes a connection to the server, allowing us to check the connectivity to the server.

We ping an individual host by name.

$ ansible database1.localdomain -m ping
database1.localdomain | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
$

We ping all the hosts that are part of the "appservers" group.

$ ansible appservers -m ping
appserver2.localdomain | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
appserver1.localdomain | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
$

We ping all the hosts in the inventory.

$ ansible all -m ping
database1.localdomain | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
appserver2.localdomain | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
appserver1.localdomain | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
ansible-server.localdomain | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3.6"
    },
    "changed": false,
    "ping": "pong"
}
$

We ping all hosts that are not part of a group.

$ ansible ungrouped -m ping
ansible-server.localdomain | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3.6"
    },
    "changed": false,
    "ping": "pong"
}
$

For more information see:

Hope this helps. Regards Tim...

Back to the Top.