Ansible is a wonderful tool for orchestration, configuration management and automating a lot of tasks you’d never want to do by hand. Sometimes you just need to get in there and run a few commands across a bunch of different systems. These systems might listen on different SSH ports, or require different entry-point users. Parallel SSH (pssh) can be useful but I find Ansible to fit the bill even better. Here’s some useful examples.
Take Advantage of the Inventory
Your hosts inventory is more than just a list of target hosts and groups, you can also set custom variables, SSH listen ports, usernames and more.
Here’s an example:
cat ~/ansible/data/hosts
[servers] server01 myvariable=1234 server02 ansible_user=sadsfae host01.example.com:8899 ansible_user=sadsfae host02.example.com:9988 ansible_user=jhoffa vm-test ansible_connection=local ansible_port=4423 vm-test2 ansible_connection=local /mnt/chroot ansible_connection=chroot [webservers] web01 ansible_port=6696 web02 ansible_port=6697
Let’s look at some of these options:
[somethinghere] – This is a host group, and how you’ll categorize your various hosts.
myvariable=1234 – You can pass any variable to a specific host, and refer to it via {{myvariable}}
ansible_user – Overrides the default user that Ansible will try to run as and SSH into the system.
ansible_connection – Tells whether the connection is a local resource or chroot
ansible_port – Another way to specify the SSH port Ansible uses in lieu of hostname:port
You can also apply variables to a specific group at once:
[servers:vars] dns_server=ns1.example.com proxy=proxy.example.com
Order and Overrides
Ansible has a particular order of what overrides what with variables, the general rule of thumb in precedence order is:
- CLI: ansible-playbook -e “myvariable=1234” (or –extra-vars=”myvariable=1234″)
- Hosts file variables
- variables set in group_vars/all.yml or in the task via the set_fact module.
Note: hosts file options like ansible_user will also override what’s set as -u username in the examples below.
There are other references here in the inventory official documentation.
Shell and Command Module
The shell module lets you run commands in parallel across a subset of hosts, for example I will update all my machines listed in the [servers] group.
ansible servers -u root -m shell -i hosts -a "yum update -y"
Perhaps I wanted to simply query what Ansible thinks is the local hostname?
ansible servers -u root -m shell -i hosts -a "echo {{ansible_host}}"
How about bouncing the webserver across all my hosts?
ansible webservers -u root -m command -i hosts -a "systemctl restart httpd.service"
Referencing Ansible Facts
You can get a list of the known facts (things Ansible knows about your systems) by running the following command. Warning: it’s a lot of data. You can also substitute the group servers for all below if you want to reference all hosts in all inventory groups.
ansible servers -u root -m setup -i hosts
--- snip --- vm-test01 | SUCCESS => { "ansible_facts": { "ansible_all_ipv4_addresses": [ "192.168.0.142" ], "ansible_all_ipv6_addresses": [ "fe80::5054:ff:fe90:15e" ], "ansible_architecture": "x86_64", "ansible_bios_date": "01/01/2007", "ansible_bios_version": "0.5.1", "ansible_cmdline": { "BOOT_IMAGE": "/vmlinuz-4.4.9-200.fc22.x86_64", "LANG": "en_US.UTF-8", "console": "ttyS0,115200", "quiet": true, "rd.luks.uuid": "luks-2db1ecfa-6dcb-4255-b35a-0f5459988b5b", "rd.lvm.lv": "fedora/swap", "rhgb": true, "ro": true, "root": "/dev/mapper/fedora-root" -- snip --
These will come in handy when you start using playbooks to do more advanced tasks. Note that you cannot use most of the facts that return unless you use playbooks as Ansible hasn’t discovered them yet. Actual retention of fact discovery occurs during ansible-playbook runs whereas with the above example we’re simply querying them for our own knowledge.
Doing More with Playbooks
These examples are really basic. You’ll want to look at creating playbooks to do more advanced tasks. I’ve got a few Ansible playbooks that will automate tasks for you like installing the ELK stack or a multiplayer game server for further examples.
If you’ve got suggestions or additional useful tasks please share them in the comments below and I’ll update this post.
Nice post, thanks.
While Ansible can be useful in certain situations, it and ParallelSSH are not equivalent tools.
The latter is an asynchronous parallel SSH client library as its name suggests, nothing more nothing less.
The former is an orchestration and state configuration tool with its own DSL, a cmd line interface et al.
For example, consider the equivalent “systemctl restart httpd.service” command using ParallelSSH:
from pssh import ParallelSSHClient
host_config = {‘host01.example.com’: {‘port’ : 8899′, ‘user’: sadsfae},
‘host01.example.com’: {‘port’ : ‘9988’, ‘user’: ‘jhoffa’},
}
hosts = host_config,keys()
client = ParallelSSHClient(hosts, host_config=host_config)
client.run_command(“systemctl restart httpd.service”, sudo=True)
client.pool.join()
This is the equivalent of the ansible example above in six lines of python code. As its a library, there is no command line binary to run and library code can be embedded into an application or run as a stand alone script.
Not to say one is better than the other, as before they serve different purposes. Fabric would be an alternative tool to ansible for example and, yes, ansible is a much much better tool than fabric.
Ansible furthermore is not asynchronous and when running commands in parallel will use threads and increase system load on the machine it is running on.
Try and use ansible to run a command on, for example, two hundred hosts and note CPU and system load on the host it’s running from..
ParallelSSH on the other hand could be the SSH client used _by_ ansible to run its parallel commands asynchronously.
LikeLiked by 1 person
Thanks for the insight Dan, I haven’t really done much with pssh so I appreciate this additional information. In particular the points about asynchronism is going to be useful for doing massive one-shot commands across a large fleet.
LikeLike
Can we use Ansible with Reverse SSH proxy Tunnel?
I mean if we use reverse SSH Tunnel, we don’t have the IP address of our clients to put in the host file of Ansible.
Is there any way to use Ansible with clients behind Nat?
LikeLike
Good question, the closest thing I found here is using some ~/.ssh/config trickery as outlined here. This is more for proxying through a bastion/jump host however, I’m not sure going through a reverse proxy is possible but I’ve not extensively tested this either. There is also the Ansible parameter
ansible_ssh_common_args=
which might be worth a try.LikeLike