When it comes to Ansible, choosing the right Ansible strategies can drastically optimize Ansible performance, improve resource usage, and ensure efficient task completion across multiple hosts. In this blog, we’ll explore Ansible linear strategy, serial execution, and the free strategy, and see how these different approaches can impact automation.
We will also discuss how these strategies contribute to task concurrency and performance optimization through the use of forks, pipelining, and other Ansible techniques.
Ansible execution strategies control how tasks are processed across multiple hosts in your inventory. Choosing the right Ansible playbook strategies ensures that automation is executed efficiently and predictably. These strategies help you manage automation workflows more effectively by dictating how tasks are executed—either sequentially, concurrently, or in batches.
Ansible offers three core strategies: linear (default), free, and serial (not a strategy but a keyword).
Each of these modes has a specific purpose for different types of workloads.
The Ansible Linear Strategy is the default execution strategy used in Ansible playbooks, designed to ensure that tasks are executed in a sequential and predictable order across all hosts. This means that Ansible will complete each task on every host before moving on to the next task.
In the linear strategy, if you define 100 hosts in your inventory, Ansible will perform task 1 on all 100 hosts before moving to task 2. This ensures that all hosts are at the same step in the process at any given time.
As discussed previously, this strategy is ideal for scenarios where tasks have dependencies on one another. For instance, if a web server must be configured before a database server, the linear strategy ensures that the web server setup is completed first, preventing potential errors due to unmet dependencies.
- hosts: all
tasks:
- name: Install Apache
yum: name=httpd state=present
- name: Start Apache
service: name=httpd state=started
The free strategy enables tasks to run independently on each host without waiting for other hosts to complete the same task. This means that as soon as a task finishes on one host, the next task will begin, regardless of whether the other hosts have completed the previous task. This significantly optimizes Ansible performance by maximizing concurrency and reducing execution time.
The free strategy is particularly useful for optimizing execution times in large-scale environments with a diverse range of hosts or servers.
As we saw earlier, this strategy is beneficial when tasks are independent of one another and can be executed in parallel without concern for completion order. For instance, collecting logs from multiple servers can be done simultaneously without waiting for each host to finish its logging task.
In this scenario, each host will execute the logging command independently and as fast as possible.
- hosts: all
strategy: free
tasks:
- name: Collect logs
command: cat /var/log/syslog
The serial keyword isn’t an execution strategy itself but a way to control how many hosts are processed at once. It allows you to throttle the execution by defining the batch size of hosts to work on at any given time.
When you use serial: 10, Ansible processes 10 hosts at a time for each task. After the first batch of hosts finishes, the next 10 will start, continuing until all hosts are complete.
As pointed out above, this strategy is particularly useful in environments where changes could impact service availability or stability. For example, when updating software on redundant devices, you might want to update only a subset of devices at a time to maintain overall system functionality.
This configuration ensures that only three hosts are updated simultaneously, allowing for monitoring and rollback if issues arise.
- hosts: all
serial: 3 # Process three hosts at a time
tasks:
- name: Update software
yum: name=myapp state=latest
In addition to these strategies, Ansible provides several keywords that can further refine execution behavior:
run_once
: Executes a task only once regardless of the number of hosts targeted.ignore_errors
and ignore_unreachable
: These options allow playbooks to continue executing even if some tasks fail or if some hosts are unreachable.Learn more about these keywords and others in the comprehensive Ansible playbook keywords section.
To get the most out of Ansible's execution strategies, consider these optimizations:
controlPersist
to reduce SSH overhead by keeping connections open between tasks.The linear strategy can lead to longer execution times, especially when dealing with a large number of hosts. For instance, a playbook that takes about 11 seconds to run on a single machine may take significantly longer when scaled up to 100 hosts due to the sequential nature of task execution. Reports indicate that such playbooks can take over 4 minutes for 97 machines, even with fast networking conditions.
With Serial Execution you can improve overall performance by reducing the load on the control node and allowing for staggered updates across hosts. By updating only a subset of devices at a time, organizations can maintain service availability while still pushing updates efficiently.
The free strategy maximizes parallelism and can significantly reduce execution time when tasks are independent of one another. This is particularly useful for operations like log collection or configuration checks where tasks do not depend on the completion of others.
Here’s a sample playbook demonstrating how to use both linear and serial execution for better task concurrency and resource management.
This example uses both linear strategy and serial execution to ensure tasks run sequentially but with a limited batch of hosts at a time. This combination optimizes orchestration while controlling system resources.
- name: Optimize Performance
hosts: all
strategy: linear
serial: 5
tasks:
- name: Install Packages
apt:
update: True
cache_valid_time: 3600
- name: Upgrade Packages
package:
upgrade: yes
- name: Restart Services
service:
name: my_service
state: restarted
forks: 10
control_persist: True
Execution Strategy: The playbook uses the linear strategy, which is the default in Ansible. This means tasks will be executed sequentially across all targeted hosts, ensuring that each task completes on all hosts before moving on to the next.
Serial Execution: By setting serial: 5
, this playbook processes hosts in batches of five. This approach is beneficial for reducing the risk of downtime during updates, as it allows for staggered changes across the environment. For example, if you are upgrading packages on a large number of servers, processing them in smaller batches can help maintain service availability.
Task Definitions: The tasks defined include installing packages, upgrading them, and restarting services. Each task is executed in order, ensuring that dependencies are respected.
Performance Optimizations:
forks
parameter is set to 10, which allows Ansible to run up to ten tasks concurrently across the specified hosts. This can significantly speed up execution time when dealing with multiple hosts.control_persist
to True keeps SSH connections open for a specified duration, reducing the overhead of establishing new connections for each task.Throttling Execution: When using serial, it’s important to consider how many hosts are being processed simultaneously. If you have a large number of hosts and want to avoid overwhelming your network or control node, you may want to adjust the serial value accordingly.
Using Other Strategies: Depending on your use case, you might also explore other strategies such as:
Debugging and Error Handling: Incorporating error handling and debugging strategies can help identify issues during execution. For example, using ignore_errors
can allow playbooks to continue running even if some tasks fail, which is useful in production environments .
To further optimize your playbooks and enhance performance:
Use linear for tasks requiring order, free for independent tasks, and serial for controlled batches.
Reduce SSH overhead by enabling persistent connections. This can significantly speed up playbook execution by keeping SSH connections open, which reduces the time spent establishing new connections for each task. Configure this in your ansible.cfg:
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
Adjust the forks parameter to balance resource usage and concurrency. The default value is 5, but increasing it allows Ansible to execute tasks on more hosts simultaneously, which can lead to faster completion times. Be mindful of your control node’s capacity when increasing this value:
[defaults]
forks = 20
Enable pipelining to reduce the number of SSH operations required during playbook execution. This can significantly decrease execution time, especially in environments with many tasks:
[ssh_connection]
pipelining = True
Group related tasks together using task blocks. This not only improves readability but also enhances reliability by ensuring that all related tasks are executed as a single unit. If one task fails, you can handle errors more gracefully.
By default, Ansible gathers facts about managed nodes before executing tasks, which can add unnecessary overhead if those facts are not used in the playbook. Disable this feature by setting gather_facts: False
in your playbook:
hosts: all
gather_facts: False
tasks:name: Example Task
command: echo "Hello World"
Use callback plugins like timer
, profile_tasks
, and profile_roles
to identify which tasks consume the most time during execution. This information can help you focus your optimization efforts where they will have the most impact.
For tasks that take a long time to complete (e.g., backups or installations), consider using asynchronous execution with a polling interval. This allows subsequent tasks to run without waiting for the long-running task to finish:
- name: Long Running Task
command: /path/to/long_running_script.sh
async: 300 # Maximum allowed time in seconds
poll: 0 # Do not wait for completion
Understanding and leveraging Ansible's execution strategies—linear, serial, and free—allows you to optimize playbook performance based on your infrastructure needs. By selecting the right strategy, you can optimize execution times, manage system resources effectively, and ensure that your automation tasks are completed efficiently.
🚀 Want to test and refine your Ansible automation in a real-world environment? Try CloudMyLab’s hands-on Ansible labs to validate your automation workflows before deploying them at scale. Start your free trial today!