This Ansible tutorial will take you through the step-by-step process of installing and setting up this incredibly useful open-source tool by RedHat. We’ll also go into detail on key Ansible features and components including:
By the end of the tutorial you’ll have set-up your own automated configuration management and be able to do so again and again in different environments. But before we get into the nitty gritty of the installation, set-up and details of using Ansible in this way, let’s take a quick look at the role the provisioning, configuration management, and application-deployment tool plays as part of a DevOps architecture in enabling infrastructure as code.
Ansible, originally written by Michael DeHaan and acquired by RedHat in 2015, plays a key role in DevOps architecture by allowing for automated software configuration management. A delightfully simple and lightweight DevOps automation engine, Ansible can applied to realising productivity gains across the automation of cloud resource provisioning, deployment and orchestration of Kubernetes clusters (you can also read our step-by-step tutorial on deploying Ceph in Kubernetes using Ceph-Ansible). Ansible’s value is in how simple it is to use, despite packing enough punch to automate complex multi-tier application environment.
While Ansible’s role, as mentioned, can also extend across provisioning and application deployment, enabling infrastructure-as-code, it is its role in configuration management that is the focus of this particular Ansible tutorial. Configuration management underpins DevOps efficiency gains by automating otherwise tedious manual tasks that detract from productivity and agility by sucking up time, resources and injecting the potential for human error.
RedHat’s Ansible is not the only software configuration management tool available to DevOps teams, but after testing several across different projects (Hashicorp’s Terraform, Puppet and Chef and native AWS options all have their strengths and use cases) it’s the one we have opted for at K&C as our standard go-to. It’s the lightest option and another major Ansible plus is that the tool doesn’t requiring a client-server implementation.
The video below is a great Ansible tutorial for beginners if you feel you would benefit from more general information on Ansible’s qualities and use cases.
Automated configuration management using Ansible as part of your DevOps practises brings the following key benefits:
Efficiency Gains – greater control as a direct result of improved visibility and tracking.
Cost Savings – unnecessary duplication of configuration elements sucks up resources. Manual configuration management is tedious and repetitive, heightening the risk of human error, all of which also add to the bottom line of a development project’s expenses column.
Faster Problem Resolution – highlights problems as soon as they appear, allowing for speedy intervention.
Improved system and process reliability – timely detection and adjustment of problem configurations avoids their potential to negatively impact performance and make inefficient use of cloud resources.
Enhanced change management – reduces the risk of product incompatibility and other potential issues.
Faster system restoration – if, or rather when, a process failure occurs, knowledge of the required state of configuration makes recovering that far quicker and easier.
Exceeding budget – neglecting the set-up of automated configuration management, using either Ansible or an alternative tool, or doing so incorrectly, runs the risk of making a major contribution to budget spiralling out of control. Unplanned time and resources being dedicated to intervention when things go wrong as a result of quality and schedule problems can see expenses quickly mount up.
‘Firefighting’ becomes the norm – corrective action, or firefighting, can become a standard way of working for teams without strong DevOps automation processes and standards. It’s a massive energy vacuum that can be hugely detrimental to overall productivity as well as seriously compromising quality.
But that’s the doom and gloom out of the way! Since you are here, you are invested in automated software configuration management and have either settled upon or are strongly considering Ansible as your tool of choice. So let’s focus on the benefits that will bring and get you set-up!
A major plus of Ansible is that the tool’s learning curve isn’t steep and installation and set-up are relatively simple. But that doesn’t mean it won’t be even easier if you follow our detailed step-by-step tutorial. So, let’s get started and see how it works.
We set up ansible on the local machine. Personally, I am using Fedora release 22.
dnf install ansible
DNFÂ is a new package manager, designed to replace yum.
The simplest thing Ansible can do, is running commands on servers. To do this, let’s write in the command line:
ansible all -i inventories/test.ini -u roman -m ping
all  a command running for servers in the inventory file
-i   a path to the inventory file
-u   a user, under which we will access the servers
-m  module
You can also run any arbitrary command:
ansible all -i inventories/test.ini -u roman -a 'date'
Displaying:
62.244.31.234 | success | rc=0 >>Â
Wed Jun 29 06:20:19 EDT 2015Â
62.244.31.235 | success | rc=0 >>
Wed Jun 29 06:20:19 EDT 2015
-a arbitrary command
The inventory file is a list of servers and groups
cat inventories/test.ini [test1] 62.244.31.234 [test2] 62.244.31.235
ip, domaine     List of Servers
test1, test2Â Â Â Â Â Groups
If you run:
ansible test1 -i inventories/test.ini -u roman -a 'date'
You see:
62.244.31.234 | success | rc=0 >>
Wed Jun 29 06:31:10 EDT 2015
The command applies only to the servers of a certain group.
A playbook is one of the main concepts in Ansible and refers to the files where Ansible code is written. Playbooks are YAML files where we use script to specify what actions must be executed on the servers. For example:
- hosts: docker - name: Installing Docker yum: name=docker state=latest become: yes - name: Uninstaling Software yum: name=sendmail state=absent become: yes
In the example above we will install the docker package for docker hosts group and remove sendmail packages
name      An arbitrary designation of the task
yum        yum module
become    Switching user to superuser
If the playbook becomes large, and we need to run an action located somewhere in the end of the file,then we’d have to wait for quite a while.
The video tutorial here is a good explanation of Ansible playbooks and why the tool works particularly well with Docker.
To shorten the waiting time, ansible offers tags. Tags are an attribute that can be set to an Ansible structure (plays, roles, tasks). Once tags or skip-tags, have been configured, they execute or skip over a sub-set of tasks when you run a playbook. This means the playbook only runs what you want it to. For example:
copy: src={{ files_path }}/docker/docker-storage dest=/etc/sysconfig/docker-storage become: yes tags: copy_config
Write in the command line:
ansible-playbook --check inventories/docker.yml -i inventory.ini -t copy_config
PLAY [docker] *****************************************************************
GATHERING FACTS ***************************************************************
ok: [docker_ci]
TASK: [Copying docker-storage config] *****************************************
ok: [docker_ci]
PLAY RECAP ********************************************************************
docker_ci : ok=2 changed=0 unreachable=0 failed=0
ansible-playbook --check docker.yml -i inventory.ini -t copy_config --skip-tags
-check      Check, but do not apply changes
-t           Tag
-skip-tags    Will perform all tasks except those that are specified
Tags can be also specified as follows:
- name: Copying docker-storage config copy: src={{ files_path }}/docker/docker-storage dest=/etc/sysconfig/docker-storage become: yes tags: [docker, config]
The video tutorial below is useful resource on how to only run specific tasks in a playbook using tags:
Sometimes, having performed some changes, you need to restart the service. For this, ansible has handlers. Handlers are performed at the very end of the playbook, because playbook can have a lot of tasks that will restart the service.
- name: Copying nginx config template: src={{ templates_path }}/nginx/nginx.conf.j2 dest=/etc/nginx/nginx.conf become: yes notify: - nginx restart tags: copy_nginx_config handlers: - name: nginx restart service: name=nginx state=restarted become: yes
notify Specifies the name of the handler
The following video tutorial focuses specifically on the use of Ansible handlers:
Ansible has variables. Variables are used in Ansible to manage differences between systems. The tool allows you to execute tasks and playbooks on multiple systems with a single command and creating variables allows for the representation of variations between what is executed on different systems. They are created using standard YAML syntax, including lists and dictionaries.
To omit the same values, let’s ​​define the variables:
- hosts: docker vars: files_path: /path/to/file - name: Copying docker-storage config copy: src={{ files_path }}/docker/docker-storage dest=/etc/sysconfig/docker-storage become: yes
{{}}Â Variables are defined in such brackets
For more information on how to use variables in Ansible playbooks:
Ansible facts are variables retrieved from remote systems and contain information about the servers as well as IP addresses, OS installed, Ethernet devices, mac address, time/date related data and hardware information. To see the facts about a certain machine, we write:
ansible test1 -i inventories/test.ini -m setup
62.244.31.234 | success >> {
«ansible_facts»: {
«ansible_all_ipv4_addresses»: [
«62.244.31.234»
],
«ansible_all_ipv6_addresses»: [],
«ansible_architecture»: «x86_64»,
«ansible_bios_date»: «NA»,
«ansible_bios_version»: «NA»,
«ansible_cmdline»: {
«quiet»: true
},
«ansible_date_time»: {
«date»: «2015-08-25»,
«day»: «25»,
«epoch»: «1440503957»,
«hour»: «07»,
«iso8601»: «2015-08-25T11:59:17Z»,
«iso8601_micro»: «2015-08-25T11:59:17.235193Z»,
«minute»: «59»,
«month»: «08»,
«second»: «17»,
«time»: «07:59:17»,
«tz»: «EDT»,
«tz_offset»: «-0400»,
«weekday»: «Вторник»,
«year»: «2015»
}
……………..
Facts are collected every time Ansible runs. If you put gather_facts:no in playbook, the facts will not be collected:
--- - hosts: docker gather_facts: no tasks: - name: Adding epel repo copy: src={{ files_path }}/repo/epel.repo dest={{ repos }}/epel.repo become: yes when: ansible_distribution == "CentOS" and ansible_distribution_major_version == "7" tags: epel_repo
We should keep cycles in mind, as you may often need to install lots of packages, which overwhelms playbook with multiple similar tasks. For this, Ansible provides the key with_items:
- name: Installing basic packages yum: name={{ item }} state=latest with_items: - mc - nano - atop - htop - iptraf-ng
There is another option of usage, by applying several parameters:
tasks: - name: Adding users user: name={{ item.name }} state=present group={{ item.group }} with_items: - { name: testuser1, group: test } - { name: testuser2, group: test } become: yes
This video tutorial nicely covers both Ansible facts and variables:
Ansible playbooks can contain multiple sets of variables responsible for different tasks. Where there is a mix of variables, each representing different entities like software, servers, network devices etc., conditional statements come into play.
Ansible uses the ‘when’ conditional to determine the outcome of a variable instead of the ‘if-else’ statements in standard programming languages. Basic use of the when condition controls if a task or role runs or is skipped. This is most commonly used to change play behaviour based on facts from the destination system.
If the condition matches, then the task runs the when condition:
tasks: - name: "shutdown Debian flavored systems" command: /sbin/shutdown -t now when: ansible_os_family == "Debian" tasks: - name: "shutdown CentOS 6 and 7 systems" command: /sbin/shutdown -t now when: ansible_distribution == "CentOS" and (ansible_distribution_major_version == "6" or ansible_distribution_major_version == "7")
The following video tutorial is an excellent additional guid to using a simple when clause as an Ansible conditional:
Ansible also has filters, which allow for the manipulation of data. Using filters, you can do things such as converting JSON data into YAML data, split URLs to extract hostnames, add or multiply integers or even obtain the SHA1 hash of a string. The ansible-base repo offers filters as plug-ins and you can even create your own custom plug-in filters.
--- - hosts: localhost vars: numbers: [ 4, 9, 6, 8, 2, 9 ] path: /etc/passwd tasks: - debug: msg={{numbers | min }} - debug: msg={{numbers | max }} - debug: msg={{numbers | unique }} - debug: msg={{ ['a', 'b', 'c' ] | random }} - debug: msg={{ path | basename }} - debug: msg={{ path | dirname }} - debug: msg={{ "~/Documents" | expanduser }}
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [debug msg={{numbers | min }}] ******************************************
ok: [localhost] => {
«msg»: «2»
}
TASK: [debug msg={{numbers | max }}] ******************************************
ok: [localhost] => {
«msg»: «9»
}
TASK: [debug msg={{numbers | unique }}] ***************************************
ok: [localhost] => {
«msg»: «[4,»
}
TASK: [debug msg=b] ***********************************************************
ok: [localhost] => {
«msg»: «a»
}
TASK: [debug msg={{ path | basename }}] ***************************************
ok: [localhost] => {
«msg»: «passwd»
}
TASK: [debug msg={{ path | dirname }}] ****************************************
ok: [localhost] => {
«msg»: «/etc»
}
TASK: [debug msg=/home/roman/Documents] ***************************************
ok: [localhost] => {
«msg»: «/home/roman/Documents»
}
min           Displays the minimum value
max          Displays the maximum value
unique        Displays the unique value
random       Displays a random value
basename     Displays the file name
dirname      Displays the directory name
expanduser   Discloses tildes
Registration of the results:
tasks: - shell: ls /home register: home_dirs - debug: var=home_dirs
TASK: [debug var=home_dirs] ***************************************************
ok: [localhost] => {
«var»: {
«home_dirs»: {
«changed»: true,
«cmd»: «ls /home»,
«delta»: «0:00:00.004283»,
«end»: «2015-08-25 16:35:28.429512»,
«invocation»: {
«module_args»: «ls /home»,
«module_name»: «shell»
},
«rc»: 0,
«start»: «2015-08-25 16:35:28.425229»,
«stderr»: «»,
«stdout»: «lost+foundnromanntestntestuser1ntestuser2»,
«stdout_lines»: [
«lost+found»,
«roman»,
«test»,
«testuser1»,
«testuser2»
],
«warnings»: [] }
}
}
Now we take the value from stdout_lines
tasks: - shell: ls /home register: home_dirs - debug: var=home_dirs - name: Adding home dirs to cron cron: name="Backup" minute="0" hour="6,3" job="backup_dir /home/{{ item }}" with_items: home_dirs.stdout_lines
For more use cases and further details, refer to the Ansible documentation on how to use filters to manipulate data.
If your playbook grows to an unwieldy size, you can remove pieces to other files:
--- - hosts: docker vars_files: - ../vars/basic_vars.yml tasks: - include: includes/basic.yml handlers: - include: includes/handlers.yml - include: includes/httpd_servers.yml
The inclusions are an ordinary YAML file:
cat includes/basic.yml
— name: Uninstaling Software
yum: name={{ item }} state=absent
with_items:
— sendmail
— exim
— httpd
become: yes
K&C offers DevOps outsourcing and consulting services in the form of dedicated teams or team extensions. A Munich-based IT outsourcer of over 20 years experience with nearshored tech talent centres in Krakow, Kiev and Sofia, we have worked with some of Europe’s biggest brands, SMEs and exciting start-ups.
We’ve been supporting the digital success of our partners for over 2 decades now by sticking to a simple commitment of matching our excellence in our technology stack with excellence in communication and delivery management. Our job is to make sure you have one less worry, outstanding IT products and services, so you can focus on what you do best. That also means offering you the kind of flexibility that is crucial to a modern digitally-forward organisation. The technology expertise you need, when you need it. Scale your IT resource up and down without overheads.
We’d love to hear about your next project and how we might be able to contribute, from helping you implement an organisation-wide DevOps culture, web development, cloud consulting, migration and native development and QA to big data and AI needs. Get in touch for a no obligation intro chat!