Recently I was contracted to work on a security upgrade program across a fleet of Linux servers. Most of the servers are Red Hat based and are configured using Ansible, which is a configuration management and automation tool sponsored by Red Hat.
Ansible lets you define the state that your servers should be in using YAML and then proceeds to create that state over SSH. For example, the state might be that Apache should be present and enabled.
The great thing about Ansible is if the server is already in the state that you’ve defined in your playbook then nothing happens. Ansible wont try to reinstall anything or change anything.
This might not seem like a big deal if you’ve only got to configure a small number of servers, but on larger installations where there’s 10s or 100s of servers or more being able to define how you want the servers to be and automating the configuration is essential.
For the purposes of this demonstration I’ll launch a small CentOS server in Linode and install Apache using Ansible.
Once the server is running, we can test the connection to the remote host by running ansible’s setup module.
$ ansible -i hosts -m setup -u centos all
Note the arguments being passed to ansible. The -m setup loads the setup module. The setup module gathers all the facts about the host and displays them in JSON format.
The -u centos is the user I’ve configured (with sudo privileges) that will be running the tasks.
The -i hosts argument loads a text file in the ansible project directory (in this case it’s just a directory in my local user home) that has a list of IP addresses for the servers ansible will be communicating with. The file could have been named anything. As an example it might look like this:
Where the word in square brackets is the host group, in this case the host is in the www group, and underneath is a list of IP addresses.
Now that I know I can communicate with the server using ansible, I’m going to deploy Apache.
The previous setup command was known as an adhoc ansible command and is essentially just for running once off commands across your fleet. Normally though you’d define your tasks in a playbook and instruct ansible to run those which would allow you to ensure the state is always how you expect it be and also allows you to store your tasks in version control to track changes.
The ansible task looks like this:
--- - name: Install Apache yum: name: httpd state: present
This task instructs Ansible to use the yum module which is the package manager module for Red Hat based Linux systems, and install the httpd package. Setting state to present ensures that if the server doesn’t have Apache installed then yum will install it.
Once it’s installed we want to make sure it’s running.
- name: Start apache service: name: httpd state: started
How you structure your ansible project will depend on the scope of tasks you need completed. For small tasks like this I can use a simple playbook, but for larger installations with many tasks you might consider using ansible roles. Roles are a bit beyond what I want to discuss here, so I’ll stick with a simple playbook. In a file called site.yml the complete play might look like this:
--- - hosts: www become: yes tasks: - name: Install Apache yum: name: httpd state: present - name: Start Apache service: name: httpd state: started
Then run the playbook with the following command:
$ ansible-playbook -i hosts -u centos site.yml
The command ansible-playbook is used instead of ansible that we ran previously and instead of passing the -m command to load a module we pass the name of the playbook, in this case site.yml.
We should now be able to go the server IP and see the default Apache page.