If you study Ansible’s documentation you’ll notice most of it refers to using Ansible in “push” mode. Push means, that a central Ansible “master server” connects via SSH to the machines (I call nodes) it wants to manage and does what it’s supposed to do. (The diagram I drew depicts this.) This is all fine and dandy, but I the places I get to work in typically don’t allow that sort of deployment: tightly-firewalled security zones often forbid this kind of setup; workarounds forbidden, we have to think of a different mechanism. We need “pull” mode, where a node connects to a master for instructions on what to do.
Furthermore, “push” mode can be a bottleneck for simultaneous provisioning of many nodes: Ansible processes nodes with a certain amount of parallelism, but there are limits to that. The documentation on Ansible Playbooks mentions pull-mode playbooks, but doesn’t dig into them very deeply.
The ugly and the not so ugly
Ansible can run a playbook locally, rather than connecting over SSH. If you let that sink in for a bit, you’ll come to the conclusion that the local machine requires a copy of Ansible and its dependencies and modules. In other words, some of the great features of Ansible I mentioned when first discussing Ansible are invalidated. I said:
Managed nodes don’t require any software installation, i.e. there is no need to install ansible nor any of its dependencies and thus there are no daemons to manage.
In order to use pull-mode (see also: ansible-pull), we have to install Ansible and its dependencies on each _node, some of which we can automate. A node should be OK for pull-mode, if you can run this on it:
Storage of playbooks, templates, etc.
In production I would not use a publicly accessible repository of any sort because sensitive data could slip into configuration files and become readily visible. (Not that I feel sensitive data has any place in configuration management repositories.) We have plenty of alternatives:
- Set up your own private git (or your favorite revision control, e.g. Subversion) repository on the local network using the git http backend protected by SSL client certificates.
- Use rsync over SSH
- etc., etc., etc.
Provisioning a “Pull”-node
Each of the machines (nodes) that will be using our pull-based deployment
system, need a working copy of Ansible and its dependencies (Jinja2 and
Ansible currently requires Paramiko SSH, but I’ve proposed that
Ansible run locally need not). This can be done
- by your bare-metal provisioning system
- through an initial Ansible run (push mode) from master to nodes (impossible if SSH from manager to nodes is forbidden)
- “manually”, via a glorified shell script provisioned as for the Ansible code in the sample n-repo.
As Ansible is still a work in progress, I propose to distribute its source
together with all other files I require on nodes; this is the reason for the
ansible-dist directory in the repository. This also allows me to
ensure that whatever nodes will be pulling in actually works.
Remember that any modules you use in your playbooks must be available to
Ansible on your nodes as well: I store them together with the Ansible
distribution which gets cloned to the nodes.
Lets provision a node. We’ll be doing this manually once.
You should probably look at what each file contains.
What each node does
Once the repository is cloned onto nodes, we periodically launch (e.g. via
node-runner.sh program which pulls in a possibly updated copy of
the repository and then launches
ansible-playbook localhost.yaml to do the
node-runner.sh sources a trivial
node-runner.cf, in which
I can, say, change the playbook name from
an architecture- or hostname-specific playbook, or do any other customization I need
on a particular node or group of nodes.
Some ideas for
- Capture output (errors mainly) and send them via e-mail to an administrator. If the repository it accesses is writable, it could add output to that and push it back. (You get the idea.)
- Indicate to our monitoring system (Nagios, Icinga, whatever) that it has run and completed with code so-and-so.
- Verify it’s own MD5 sum before and after loading itself from the repository to determine whether it should re-exec itself.
Setting Ansible up for nodes to “pull from” instead of “pushing to” them has some disadvantages, such as requiring software installed on the nodes, but there are a few advantages I see:
- No central management server is required (depends on the type of repository)
- De-central repositories are possible. (Again: depends on type of repository.)
- Connections can be initiated by nodes (can be important if you’re not allowed to alter firewall policies)
- Increased parallelism
- Nodes can pull when they are available. (In a push-based model, if a node is unavailable it cannot be configured.)
- Very fast, because the SSH-connection overhead incurred for each task is avoided.
I’m liking Ansible more and more.