How LambdaTest Uses Ansible To Scale Deployments?

Ansible helped LambdaTest to cut down the deployment time to nearly half in numbers. Our deployments include a lot of VM's/HW level changes and Ansible helped us achieve the goals with a huge reduction in the deployment time.

In this blog, I will cover the following topics:

  • What is Ansible?
  • What are Ansible Roles?
  • Perks of using Ansible Roles
  • How did Ansible help to streamline deployment in LambdaTest?

Rolling its initial release in 2012, Ansible has been the buzzword in the DevOps community. Whether you’re an experienced DevOps professional or a novice DevOps rockstar, chances are, you might’ve had your hands dirty with Ansible or at least heard of it.

This article would help you get familiar with Ansible and understand how LambdaTest leveraged it to reduce deployment time by half.

What is Ansible?

Ansible is an open-source application deployment tool that helps you with infrastructure and configuration automation while maintaining the deployment state (i.e. idempotent nature).

Ansible has a wide range of sub-modules and features supporting different types of platforms. Each module and feature plays an important role in making the automation smooth and streamlined. Let's begin with the ubiquitous and most used module (i.e. Ansible Roles).

What are Ansible Roles?

Ansible Roles play a prominent role in building and segregating the pipeline stages. Ansible Roles let us automatically load related vars, files, tasks, handlers, and other Ansible artifacts based on a known file structure. This structure helps in making the code less complex and easy and read an update. Once the content is grouped into roles, it also lets you to share and reuse the code making it more flexible and compact. The Ansible Galaxy offers a wide range of pre-developed roles where one can get basic role structure and can also create and contribute to the galaxy for general use.

How to create a basic Ansible Role?

In order to create Ansible Roles, Ansible provides a command-line tool i.e. Ansible Galaxy which helps in installing, creating, and managing roles. It creates a blank role with all the directory structure standards and dependencies listed properly. Ansible galaxy also offers a common platform that helps developers to share roles and contribute together.

Command to initialise a role: ansible-galaxy init <role-name>

Screenshot 2021-06-04 at 11.56.45 AM.png

The Usage Of Ansible Directory Structure

Ansible Role structure contains the following files and folders that automatically load vars, tasks, handlers, and other artifacts. Grouping the automation steps in roles let the user control and reuse the code.

  • README - The role usage and scope can be briefly explained here to let the end-user understand the purpose of the role and its usage.

  • Defaults - This folder is used to keep the values that should be passed as a default value to the parameters defined inside the role.

  • Files - This folder is used to keep the files like exe, scripts, folders, zips, or any artifacts which need to be copied to the target machine or used during deployment.

  • Handlers - This folder has a very wide use. They are similar to the task, but they get executed only when they are called or notified. It's more like the functions in programming which run only when they are called. It also helps in reducing the code size and complexities involved in its maintenance. Similar and repeated pieces of code can be put in handlers and can be used when notified.

  • Meta - This is used to define metadata for the role and the role dependencies on any other role. Any pre_task and post_task can be included here.

  • Tasks - This folder consists of all the tasks that need to be performed or maintained as a part of the role. It consists of a range of benefits; let's talk about it at a later point in time.

  • Main.xml - This file contains the main task, which has to be performed by default. This is called by default whenever the role is executed. It can also be used to include the tasks and call them on a tag basis.

    Here in this example, we have tagged an individual task with a unique tag and added a tag full to the roles' tag list. We can call the install task by the tag "install" and also when the whole setup is required, we can pass the tag “full” to do the entire Apache setup

    Screenshot 2021-06-04 at 12.02.00 PM.png

    Following are the major benefits of bifurcating task list in the above manner:

    1. Task can hover without any complexity
    2. Easy to debug and resolve the issues, as Ansible will let you know which task failed at what step during execution.
    3. Multiple developers can contribute at a single point in time.
    4. The order of task execution list can be updated at our convenience without interrupting the code.
    5. Single or multiple tasks can be called depending on the tags; thus, there is no need to rerun the entire task every time.
  • Templates - This folder is used to save the configuration files which have to be changed dynamically (i.e. depending on any condition or run time variables). The files kept in this folder must be in the jinja (.j2) format to support the dynamic variables replacement during playbook execution.

    Ansible supported variables can also be added here and that will be replaced during Ansible execution. Ex: {{ inventory_hostname }} if used inside the .j2 file, it will be replaced by the IP address of the current target machine on which Ansible tasks are running.

  • Tests - This folder contains the sample or test files that can be used to check the working of the role. It contains two files:

    • Inventory - It contains the details of the servers on which the role can be executed (i.e. IP address, keys/password, usernames, host groups, etc.).
    • Test.yml - This file is the sample play file that contains host group names and the list of roles that have to be executed.
  • Vars - This folder contains the list of variables that have to be used throughout the role with their values. In this way, a user need not update the task for updating the value for a parameter. Changing the value in this file will get reflected in the entire role. The main.yml file has to be included in the task by default. However, if any specific file has to be added, it can be included in the task file as well.

    The precedence order in which variable can pick the value at a broader level is shown below :

    Screenshot 2021-06-04 at 11.51.42 AM.png

Perks of using Ansible Roles

  • Code segregation makes updating and reading the roles smooth and fast.

    Screenshot 2021-06-03 at 9.04.22 PM.png

  • Developers can contribute together at each part of the project.

  • Each component can be called individually as well as together.

    Screenshot 2021-06-03 at 9.05.34 PM.png

  • Tagging makes the execution of the roles extremely smooth and dynamic. In the above figure, we can easily see that "Nginx" only calls and sets up Nginx but “nginx_complete” installs both the components (i.e. MongoDB and Nginx).

  • Role dependencies make the code lighter and much easier to connect the different wires by just listing the names in the metafile.

    Screenshot 2021-06-03 at 9.19.34 PM.png

  • Use "blocks" and "when" to add condition-based role/task execution. Block is used to apply conditions to a set of tasks while "when" works on a single task.

  • Use "gather_facts" to make the play more flexible. Gather facts collects some common data like machine software version, hostname, etc. For example - Apache installation for Centos requires the yum package and for Ubuntu, you would require apt package. These edge cases can be managed easily by gathering system information and adding condition-based execution of tasks.

  • Use "include" for adding (or including) any other task in the current task to make it usable with the same piece of code. By doing so, we avoid the need to copy the task twice for using the same. By just calling the file, the entire task of the included file is inherited to the currently executed task list. This simplifies the code and subsequently eases the code execution and updation process.

How did Ansible help to streamline deployment in LambdaTest?

At LambdaTest, doing a rollout of changes was never an easy task. Earlier, the deployments took around 7-8 hours to perform the rollout on the entire inventory. This also included a lot of manual intervention, which slowed down the deployment process.

We cut down this time by 50 percent (i.e. less than 4 hrs) by doing smaller yet effective changes. These changes made our life smoother, as there was a significant reduction in manual efforts.

image.png Source

The above saying perfectly matched our situation at LambdaTest. We were using tools to automate but not using them to their fullest potential. So, how did we achieve greater efficiency? Let's look at those impactful factors that brought the change!

Here are four major factors which we implemented at LambdaTest for streamlining the deployment process:

  • Code Simplification

    We rolled from plays to breaking out the entire deployment process to roles. Roles helped us in code segregation and code modularisation. This helped minimise the overall code footprint along with helping us save efforts on maintenance of the code.

    Consider that you have an Apache role that has setup steps and configuration written in the same file. If you have to change configuration settings, you need to go through the entire file to find out the piece that requires modification. This consumes a lot of time and makes the code difficult to understand and maintain. Once you put the setup and configure part in a different file, it would reduce the search and update time to half.

  • Time Reduction

    We added tags in the roles. Tags are the catch of Ansible that help us perform only what is required. Generally, once the service or changes have been deployed, we don’t have to deploy the things from scratch unless it’s an entirely new feature.

    In such cases, calling and executing the entire set of tasks will consume a lot of time. Ansible skips the part where no changes are present since it is idempotent in nature. But even checking those parts will be time-consuming. To reduce the time, we added tags and only called what's needed.

  • Code Generalisation

    We heavily started using Ansible "gather facts" information. These parts of Ansible collect a lot of information about target systems, thereby making the deployment flawless. Since LambdaTest Grid lets users perform cross browser tests on different browsers and platform combinations, the deployment process was more lengthy and complex.

    To make our code flawless and independent of the OS parameter as input, we added "gather_facts", which collects the basic information of the target machine, including OS type, which can be used widely across the roles. We used the same information and added conditional-based executions.

    Here is the example code which shows a single task written to start httpd service but on two different platform types. The first one shows that it will run only when the target machine is Ubuntu 18.04 or above as 'systemctl' works from this version. The second task shows that it will run only when the target machine is Ubuntu 16.04 or below.

    Screenshot 2021-06-10 at 12.32.16 AM.png

  • Blue /Green Deployment

    Using "serial" in the playbook. Serial is a very powerful weapon to control the playbook execution. Using serial, we can restrict the number of machines on which updates to be rolled out at a time. This helps us make the blue-green deployment successful, thereby helping cut down the downtime window frame.

    Here is the example, which explains the execution order by using "serial: 3" in the play - This means the playbook will be executed simultaneously on 3 machines completely at a time before rolling out to the next batch.

    Screenshot 2021-06-14 at 12.04.55 PM.png

It's a Wrap!!

We all are aware of the fact that change is defining the characteristics of software and in order to make those changes deployed we need to be more trained and more bent towards adopting new technologies and new ways. This article gives insights on the best practices and ways to use Ansible to make it more efficient and generalize.

“It's really clear that the most precious resource we all have is Time.” - Steve Jobs

Life in DevOps can be fun but hectic at times. Have you ever been in a crunch-time scenario where you realize the deadline is closing in sooner than your expectation? And you wish, to get more time on your hands. If you're using Ansible for future deployments then I hope you've learned how to reduce deployment times by leveraging Ansible roles.

We achieved this by pipelining our ways of deployment and making it more robust and streamlined.

Reference Links : Ansible Guide

Shobhit Srivastava's photo

Very Helpful!

Riddhima Sharan's photo

Very informative 👏

Aakash Srivastava's photo

That's a legit piece of information!