Ubuntu Server 18.04 – Using Ansible’s pull method

How to build a Docker Compose YAML files

The way we set up our Ansible configuration in the previous section works very well if we have a list of specific servers we wish for it to manage. To add a new server, we create the user account and SSH configuration on the new host, and then add it to the inventory file. If we decommission that server, we simply remove it from the inventory file. This works well in a static environment, where servers you deploy typically stay around for a while. In a dynamic environment though, this may not work as well.

Dynamic environments are very typical in the cloud. With cloud computing, you typically have one or more virtual servers that provide a service to your company or users. These servers may come and go at any time. With dynamic environments, servers will come online as needed to handle load, and will also get decommissioned automatically as load decreases. Therefore, you never really know when a server is going to come online, and having to manually provision a server in such an environment is inefficient.

For this reason, Ansible’s inventory file may not be a good fit for dynamic infrastructure. There certainly are ways to make Ansible’s inventory work in such an environment, as you can actually replace the inventory file with an executable script that can make API calls and customize itself for your infrastructure if you so desired to do so. However, that’s out of the scope of this tutorial, and there’s an easier method anyway.

As you know, normally Ansible uses an inventory file, and connects to every server listed in that file. However, Ansible also features a pull mode, where instead of having a central server that connects to other machines, each server in pull mode will actually run Ansible against themselves. In my opinion, this is a great way to use Ansible and it doesn’t seem to get the attention it deserves. First, I’ll explain the theory of how it works, and then we can work through an actual example.

With pull mode, you’ll want to have your Ansible Playtutorials inside a Git repository. This repository must be accessible from the servers you will manage. For example, if you store the Git repository on GitHub, you’ll want to make sure the servers can access GitHub externally. If you host your own Git server internally, you’ll want to make sure your servers are able to access it through your firewall or any security rules you may have in place.

Pull mode is used with the ansible-pull command, which actually comes bundled with Ansible. The syntax looks like the following:

ansible-pull -U https://github.com/myusername/ansible.git

Of course, you’d replace the URL with the actual HTTP or HTTPS URL to your actual Git repository. However, that’s basically it. The ansible-pull command simply expects the -U option (short for URL) along with the URL to a Git repository.

In order for this to work, you’ll need a Playtutorial inside the repository with a special name, local.yml. If you don’t declare a specific Playtutorial with Ansible, it will expect to find a Playtutorial with that name inside the root of the repository. If you choose to use a name for the main Playtutorial as something other than local.yml, you’ll need to specify it:

ansible-pull -U https://github.com/myusername/ansible.git myplaytutorial.yml

In this example, the ansible-pull command will cache the Git repository located at the specified URL locally, and run the Playtutorial myplaytutorial.yml that you would have inside the repository. One thing you may find is that Ansible might complain about not finding an inventory file, even though that’s the entire point of the ansible-pull command. You can ignore this error. This will likely be fixed in Ansible at some point in the future, but as of the time of this writing, it will print a warning if it doesn’t detect an inventory file.

With the theory out of the way, let’s work through an actual example. If you’ve been following along so far, we created a Playtutorial in the previous section that automates the deployment of a hypothetical web server. We can reuse that code. However, it’s best practice to have a file with the name of local.yml, so you can simply rename the apache.yml Playtutorial we created earlier to local.yml. There’s one small change we need to make to the file, which I’ve highlighted below:

---
- hosts: localhost
  become: true
  tasks:
  - name: Install Apache
    apt: name=apache2
  - name: Start the apache2 services
    service: name=apache2 state=started
  - name: Copy index.html
    copy: src=index.html dest=/var/www/html/index.html

Since we’re executing the Playtutorial locally (without SSH) we changed the hosts line to point to localhost, to instruct Ansible that we want to execute the commands locally rather than remotely. Now, we can push this Playtutorial to our Git repository and execute it directly from the repository URL.

Pay careful attention to the hosts: line of any Playtutorial you intend to run. If you are using the pull method, this line will need to be changed from hosts: all to hosts: localhost, the reason being, we are executing the Playtutorials directly on localhost, rather than from a remote SSH connection. If you don’t make this change, you’ll see an error similar to the following:

ERROR! Specified hosts and/or --limit does not match any hosts

Before you run the Playtutorial, you’ll want to first switch to your Ansible user, since the Playtutorial will need to be run as a user with sudo privileges since it will execute system-level commands:

sudo su - ansible

Then, execute the Playtutorial:

ansible-pull -U https://github.com/myusername/ansible.git

If we kept the name as apache.yml, we would just specify that:

ansible-pull -U h https://github.com/myusername/ansible.git apache.yml
Keep in mind that since ansible-pull executes Playtutorials directly on localhost, the Playtutorial must be executed by a user that has access to sudo. If sudo is not configured to run as that user without a password, the Playtutorial will fail as it’s not interactive (it won’t ask for the password). You can also use sudo in front of the ansible-pull command and provide the password before it runs, but that won’t work if you set it up to run automatically via cron.

If all goes according to plan, the Playtutorial repository should be cached to the server, and the instructions carried out. If there is an error, Ansible is fairly good about presenting logical error messages. As long as the user has permission to execute privileged commands on the server, the required files are present in the repository, and the server has access to download the repository (a firewall isn’t blocking it) then the Playtutorial will run properly.

When it comes to implementing the pull method in production across various server types, there are several ways we can go about this. One way is to have a separate Playtutorial per server type. For example, perhaps you’d have an Apache Playtutorial, as well as Playtutorials specific to database servers, file servers, user workstations, and so on. Then, depending on the type of server you’re deploying, you’d specify the appropriate Playtutorial when you called the ansible-pull command. If you’re using a service such as cloud computing, you can actually provide a script for each server to execute upon their creation. You can then instruct the service to automatically run the ansible-pull command any time a new server is created. In AWS for example, you can use a feature known as user data to place a script for a server to execute when it’s launched for the first time. This saves you from having to provision anything manually. For this to work, you would first include a command for the server to install Ansible itself, and then the next command would be the ansible-pull command along with the URL to the repository. Just those two lines would completely automate the installation of Ansible and the application of your Playtutorial. Full coverage of AWS is beyond the scope of this tutorial, but it’s important to think of the possibilities ahead of time so you can understand the many ways that automation can benefit you.

While provisioning new servers properly and efficiently is very important, so too is maintaining your existing servers. When you need to install a new package or apply a security update, the last thing you want to do is connect to all of your servers manually and update them one by one. The ansible-pull command allows for simple management as well; you just simply run the command again. Every time you run ansible-pull, it will download the latest version of the code within your repository and run it. If there are any changes, they will be applied, while things that have already been applied will be skipped. For example, if you include a play to install the apache2 package, Ansible won’t reinstall the package if you run the Playtutorial a second time; it will skip that Play since that requirement is already met.

One trick worth knowing with ansible-pull is the -o option. This option will ensure that the Playtutorial inside the repository is only run if there have been any actual changes to the repository. If you haven’t committed any changes to the repository, it will skip the entire thing. This is very useful if you set up the ansible-pull command syntax to be run periodically via cron, for example, every hour. If you don’t include the -o option, Ansible will run the entire Playtutorial every hour. This will consume valuable CPU resources for no good reason at all. With the -o option, Ansible will only use as much CPU as required for simply downloading the repository locally. The Playtutorial will only be run if you actually commit changes back to the repository.

The introduction to Ansible within this chapter has been very basic, as we’ve only used the very core of the required components. By looking deeper into Ansible, you’ll find more advanced techniques, and some more clever ways to implement it. Examples include automating firewall rules, security patches, user passwords, as well as having Ansible send you an email any time a server is successfully provisioned (or even when that fails). Basically, just about anything you can do manually, you can automate with Ansible. In the future, I recommend looking at server administration from an automation mindset. As I’ve mentioned several times before, if you will need to perform a task more than once, automate it. Ansible is one of the best ways of automating server administration.

Comments are closed.