Apr 13 2021
Make Ansible 6X Faster with these 3 Tips
Ansible is generally slow because it connects to the remote host for every task it runs. Let’s do some checks on a tiny role that gets the latest kubectl version (Kubernetes client) from a URL and installs the binary.
Let’s see 3 easy ways how to speed up Ansible and get better execution times and overall performance.
- name: get kubectl last version
uri:
url: "{{kubectl_url}}/stable.txt"
return_content: yes
status_code: 200
register: kubectl_latest_version
- name: Download kubectl binary
get_url:
url: "{{kubectl_url}}/v{{ kubectl_version |
default(kubectl_latest_version.content |
regex_replace('^v', '')) }}/bin/{{kubectl_os}}/{{kubectl_arch}}/kubectl"
dest: "/usr/local/bin"
mode: '755'
owner: "{{ kubectl_owner }}"
group: "{{ kubectl_group }}"
Execution time with default settings: 32,2s
Ansible Fact Cache
Gathering facts is the first task Ansible runs when connecting to a host and the least we can say, it is slow. Very slow. Performance is also an issue when writing a playbook that you are testing over and over.
Luckily, it can be tweaked to save some time by adding these lines in ansible.cfg:
# implement fact caching and a smaller subset of facts gathered
# for improved performance
gathering = smart
gather_subset = !hardware,!facter,!ohai
fact_caching_connection = /tmp/ansible_fact_cache
fact_caching = jsonfile
# expire the fact cache after 2 hours
fact_caching_timeout = 7200
According to the ansible.cfg example available online, smart gathering “gathers by default, but doesn’t regather if already gathered”.
Hardware facts are the longest facts to retrieve but you may need them especially if you build roles based on network interfaces. You may get “ansible_eth0 is undefined” for example.
Facter and ohai are related to Puppet and Chef clients.
And the most efficient is fact caching of course that stores information in a JSON file. But it could also fit in memory, or even in a shared Redis database.
Facts can also be disabled within a playbook if you don’t need them for that specific playbook. That’s a potential significant speed up that you can’t use too often though, most playbooks need facts.
- name: my_playbook
hosts: *
gather_facts: no
tasks:
Execution time: 19,2s
SSH Speedup with Pipelining
Enabling pipelining reduces the number of SSH operations required to execute a module on the remote server. This improves performance but ‘requiretty’ must first be disabled in /etc/sudoers on all managed hosts, reason why pipelining is disabled by default. Add in ansible.cfg:
[ssh_connection]
pipelining = True
If requiretty is set, sudo will only run when the user is logged in to a real tty. When this flag is set, sudo can only be run from a login session and not via other means such as cron or cgi-bin scripts. However, this flag is off by default according to the man page on Debian and Ubuntu at least.
It is safe to use pipelining is this case. Check your Linux distro sudo man page.
Execution time: 11,6s
Delegate_to localhost
Most improvements are dependant on the way you write Ansible tasks. In this role, you could connect to the URL from any host – localhost? – and spare an SSH connection.
This is the purpose of delegate_to that is usually set to localhost. The first task becomes:
- name: get kubectl last version
delegate_to: localhost
become: false
uri:
url: "{{kubectl_url}}/stable.txt"
return_content: yes
status_code: 200
register: kubectl_latest_version
This is a non neglectable optimisation that you can use anytime the task can be run anywhere.
You’d better set become: false or you might get this error message if Ansible tries to sudo as root on your local host.
fatal: [backup]: FAILED! => {"changed": false, "module_stderr": "sudo: a password is required\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}
Execution time: 4,7s
Execution time is the mean time of 10 runs in a row, and tests were conducted through a VPN link that was far from good.
Of course, results are not linear, all playbooks will not run 6 times faster every time but you get the idea. Fact cache saves a few seconds at the start of the playbook while delegation to localhost is only applicable to a small bunch of cases.
There are other possible improvements to speed up Ansible such as async tasks to launch a task and move on immediately to the next one, but your best bet is to run Ansible on a server as close as possible to the target hosts, because people often experience slow SSH connections.