Speed up Ansible SSH with Multiplexing

Ansible runs a whole lot of SSH commands one after another.

The Problem

Ansible opens an SSH connection for every playbook run. When you have multiple servers you need to configure in parallel with multiple playbooks being applied to them then it all adds up.

This is how a typical SSH log file may look like when Ansible runs against a host in order to configure it:

Sep  1 18:32:54 admin1 sshd[]: pam_unix(sshd:session): session opened for user ansible by (uid=0)
Sep  1 18:33:17 admin1 sshd[]: pam_unix(sshd:session): session opened for user ansible by (uid=0)
Sep  1 18:33:40 admin1 sshd[]: pam_unix(sshd:session): session opened for user ansible by (uid=0)
Sep  1 18:34:02 admin1 sshd[]: pam_unix(sshd:session): session opened for user ansible by (uid=0)
Sep  1 18:35:14 admin1 sshd[]: pam_unix(sshd:session): session opened for user ansible by (uid=0)

Reusing the connections could make a difference.

The Solution

Use SSH multiplexing.

OpenSSH multiplexing can re-use an existing outgoing connection for multiple concurrent SSH sessions to a remote server, avoiding the overhead of creating a new connection and re-authenticating each time. OpenSSH client supports multiplexing using the ControlMaster, ControlPath and ControlPersist configuration directives.

We are going to use ~/.ssh/multiplexing directory to store control sockets. Since this directory does not exist by default, we need to create it on the Ansible control node:

$ mkdir ~/.ssh/multiplexing

We also have to update ansible.cfg to use multiplexing:

[default]
transport = ssh

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=3600
control_path = ~/.ssh/multiplexing/ansible-ssh-%%r@%%h:%%p

In this case ControlMaster=auto creates a master session automatically, and if there is a master session already available, subsequent sessions are automatically multiplexed.

Setting ControlPersist=3600 will leave the master connection open in the background to accept new connections for 3600 seconds (1 hour).

Note that the control_path can be changed to suit your needs. It can be something a bit more generic if you don’t feel like using a specific directory, for example:

control_path = %(directory)s/ansible-ssh-%%r@%%h:%%p

Test it by running some playbook, e.g:

$ ansible-playbook playbooks/configure-admin-hosts.yml

Then check to see if control sockets were created:

$ ls -1 ~/.ssh/multiplexing/
[email protected]:22
[email protected]:22

There should only be a single session opened for user ansible on both hosts if you check each server’s SSH logs.

Note that the first connection to the server will create a control socket in the directory ~/.ssh/multiplexing/, and any subsequent connections, up to 10 by default as set by MaxSessions on the SSH server, will re-use that control path automatically as multiplexed sessions.

Leave a Reply

Your email address will not be published. Required fields are marked *