Chapter 1. Introduction

It’s an interesting time to be working in the IT industry. We no longer deliver software to our customers by installing a program on a single machine and calling it a day. Instead, we are all gradually turning into cloud engineers.

We now deploy software applications by stringing together services that run on a distributed set of computing resources and communicate over different networking protocols. A typical application can include web servers, application servers, memory-based caching systems, task queues, message queues, SQL databases, NoSQL datastores, and load balancers.

IT professionals also need to make sure to have the proper redundancies in place, so that when failures happen (and they will), our software systems will handle them gracefully. Then there are the secondary services that we also need to deploy and maintain, such as logging, monitoring, and analytics, as well as third-party services we need to interact with, such as infrastructure-as-a-service (IaaS) endpoints for managing virtual machine instances.1

You can wire up these services by hand: spinning up the servers you need, logging into each one, installing packages, editing config files, and so forth, but it’s a pain. It’s time-consuming, error-prone, and just plain dull to do this kind of work manually, especially around the third or fourth time. And for more complex tasks, like standing up an OpenStack cloud, doing it by hand is madness. There must a better way.

If you’re reading this, you’re probably already sold on the idea of configuration management and considering adopting Ansible as your configuration management tool. Whether you’re a developer deploying your code to production, or you’re a systems administrator looking for a better way to automate, I think you’ll find Ansible to be an excellent solution to your problem.

A Note About Versions

The example code in this book was tested against versions 4.0.0 and 2.9.20 of Ansible. Ansible 4.0.0 is the latest version as of this writing; Ansible Tower includes version 2.9.20 in the most recent release. Ansible 2.8 went End of Life with the release of 2.8.20 on April 13, 2021.

For years the Ansible community has been highly active in creating roles and modules—so active that there are thousands of modules and more than 20,000 roles. The difficulties of managing a project of this scale led creators to reorganize the Ansible content into three parts:

  • Core components, created by the Ansible team

  • Certified content, created by Red Hat’s business partners

  • Community content, created by thousands of enthusiasts worldwide

Ansible 2.9 has lots of built-in features, and later versions are more composable. This new setup makes it more easily maintainable as a whole.

The examples provided in this book should work in various versions of Ansible, but version changes in general call for testing, which we will address in Chapter 14.

What’s with the Name Ansible?

It’s a science-fiction reference. An ansible is a fictional communication device that can transfer information faster than the speed of light. Ursula K. Le Guin invented the concept in her book Rocannon’s World (Ace Books, 1966), and other sci-fi authors have since borrowed the idea, including Orson Scott Card. Ansible cofounder Michael DeHaan took the name Ansible from Card’s book Ender’s Game (Tor, 1985). In that book, the ansible was used to control many remote ships at once, over vast distances. Think of it as a metaphor for controlling remote servers.

Ansible: What Is It Good For?

Ansible is often described as a configuration management tool and is typically mentioned in the same breath as Puppet, Chef, and Salt. When IT professionals talk about configuration management, we typically mean writing some kind of state description for our servers, then using a tool to enforce that the servers are, indeed, in that state: the right packages are installed, configuration files have the expected values and have the expected permissions, the right services are running, and so on. Like other configuration management tools, Ansible exposes a domain-specific language (DSL) that you use to describe the state of your servers.

You can use these tools for deployment as well. When people talk about deployment, they are usually referring to the process of generating binaries or static assets (if necessary) from software written by in-house developers, copying the required files to servers, and starting services in a particular order. Capistrano and Fabric are two examples of open-source deployment tools. Ansible is a great tool for deployment as well as configuration management. Using a single tool for both makes life simpler for the folks responsible for system integration.

Some people talk about the need to orchestrate deployment. Orchestration is the process of coordinating deployment when multiple remote servers are involved and things must happen in a specific order. For example, you might need to bring up the database before bringing up the web servers, or take web servers out of the load balancer one at a time to upgrade them without downtime. Ansible is good at this as well, and DeHaan designed it from the ground up for performing actions on multiple servers. It has a refreshingly simple model for controlling the order in which actions happen.

Finally, you’ll hear people talk about provisioning new servers. In the context of public clouds such as Amazon EC2, provisioning refers to spinning up new virtual machine instances or cloud-native Software as a Service (SaaS). Ansible has got you covered here, with modules for talking to clouds including EC2, Azure,2
Digital Ocean, Google Compute Engine, Linode, and Rackspace,3
as well as any clouds that support the OpenStack APIs.

Note

Confusingly, the Vagrant tool, covered later in this chapter, uses the term provisioner to refer to a tool that does configuration management. It thus refers to Ansible as a kind of provisioner. Vagrant calls tools that create machines, such as VirtualBox and VMWare, providers. Vagrant uses the term machine to refer to a virtual machine and box to refer to a virtual machine image.

How Ansible Works

Figure 1-1 shows a sample use case of Ansible in action. A user we’ll call Alice is using Ansible to configure three Ubuntu-based web servers to run Nginx. She has written an Ansible script called webservers.yml. In Ansible, a script is called a playbook. A playbook describes which hosts (what Ansible calls remote servers) to configure, and an ordered list of tasks to perform on those hosts. In this example, the hosts are web1, web2, and web3, and the tasks are things such as these:

  • Install Nginx

  • Generate a Nginx configuration file

  • Copy over the security certificate

  • Start the Nginx service

In the next chapter, we’ll discuss what’s in this playbook; for now, we’ll focus on its role in the overall process. Alice executes the playbook by using the ansible-playbook command. Alice starts her Ansible playbook by typing two filenames on a terminal line: first the command, then the name of the playbook:

$ ansible-playbook webservers.yml

Ansible will make SSH connections in parallel to web1, web2, and web3. It will then execute the first task on the list on all three hosts simultaneously. In this example, the first task is installing the Nginx package, so the task in the playbook would look something like this:

- name: install nginx package:
    name: nginx

Ansible will do the following:

  1. Generate a Python script that installs the Nginx package

  2. Copy the script to web1, web2, and web3

  3. Execute the script on web1, web2, and web3

  4. Wait for the script to complete execution on all hosts

Ansible will then move to the next task in the list and go through these same four steps.

It’s important to note the following:

  1. Ansible runs each task in parallel across all hosts.

  2. Ansible waits until all hosts have completed a task before moving to the next task.

  3. Ansible runs the tasks in the order that you specify them.

Figure 1-1. Running an Ansible playbook to configure three web servers.

What’s So Great About Ansible?

There are several open-source configuration management tools out there to choose from, so why choose Ansible? Here are 27 reasons that drew us to it. In short: Ansible is simple, powerful, and secure.

Simple

Ansible was designed to have a dead simple setup process and a minimal learning curve.

Easy-to-Read Syntax

Ansible uses the YAML file format and Jinja2 templating, both of which are easy to pick up. Recall that Ansible configuration management scripts are called playbooks. Ansible actually builds the playbook syntax on top of YAML, which is a data format language that was designed to be easy for humans to read and write. In a way, YAML is to JSON what Markdown is to HTML.

Easy to Audit

You can inspect Ansible playbooks in several ways, like listing all actions and hosts involved. For dry runs, we often use ansible-playbook–check. With built-in logging it is easy to see who did what and where. The logging is pluggable and log collectors can easily ingest the logs.

Nothing to Install on the Remote Hosts

To manage servers with Ansible, Linux servers need to have SSH and Python installed, while Windows servers need WinRM enabled. On Windows, Ansible uses PowerShell instead of Python, so there is no need to preinstall an agent or any other software on the host.

On the control machine (that is, the machine that you use to control remote machines), it is best to install Python 3.8 or later. Depending on the resources you manage with Ansible, you might have external library prerequisites. Check the documentation to see whether a module has specific requirements.

Ansible Scales Down

The authors of this book use Ansible to manage hundreds of nodes. But what got us hooked is how it scales down. You can use Ansible on very modest hardware, like a Raspberry Pi or an old PC. Using it to configure a single node is easy: simply write a single playbook. Ansible obeys Alan Kay’s maxim: “Simple things should be simple; complex things should be possible.”

Easy to Share

We do not expect you to re-use Ansible playbooks across different contexts. In chapter 7, we will discuss roles, which are a way of organizing your playbooks, and Ansible Galaxy, an online repository of these roles.

The primary unit of reuse in the Ansible community nowadays is the collection. You can organize your modules, plugins, libraries, roles and even playbooks into a collection and share it on Ansible Galaxy. You can also share internally using Automation Hub, a part of Ansible Tower. Roles can be shared as individual repositories.

In practice, though, every organization sets up its servers a little bit differently, and you are best off writing playbooks for your organization rather than trying to reuse generic ones. We believe the primary value of looking at other people’s playbooks is to see how things work, unless you work with a particular product where the vendor is a certified partner or involved in the Ansible community.

System Abstraction

Ansible works with simple abstractions of system resources like files, directories, users, groups, services, packages, web services.

By way of comparison, let’s look at how to configure a directory in the shell. You would use these three commands:

mkdir -p /etc/skel/.ssh
chown root:root /etc/skel/.ssh
chmod go-wrx /etc/skel/.ssh

By contrast, Ansible offers the file module as an abstraction, where you define the parameters of the desired state. This one action has the same effect as the three shell commands combined.

- name: create .ssh directory in user skeleton
    file:
      path: /etc/skel/.ssh
      mode: 0700
      owner: root
      group: root
      state: directory

With this layer of abstraction, you can use the same configuration management scripts to manage servers running Linux distributions. For example, instead of having to deal with a specific package manager like dnf, yum or apt, Ansible has a “package” abstraction that you can use instead. But you can also use the system specific abstractions if you prefer.

If you really want to, you can write your Ansible playbooks to take different actions, depending on a variety of operating systems of the remote servers. But I try to avoid that when I can, and instead I focus on writing playbooks for the systems that are in use where I work: mostly Windows and Red Hat Linux, in my case.

Top to Bottom Tasks

Books on configuration management often mention the concept of convergence, or eventual consistent state. Convergence in configuration management is strongly associated with the configuration management system CFEngine by Mark Burgess. If a configuration management system is convergent, the system may run multiple times to put a server into its desired state, with each run bringing the server closer to that state.

Eventual consistent state does not really apply to Ansible, since it does not run multiple times to configure servers. Instead, Ansible modules work in such a way that running a playbook a single time should put each server into the desired state.

Powerful

Having Ansible at your disposal can bring huge productivity gains in several areas of systems management.

Batteries Included

You can use Ansible to execute arbitrary shell commands on your remote servers, but its real power comes from the wide variety of modules available. You use modules to perform tasks such as installing a package, restarting a service, or copying a configuration file.

As you will see later, Ansible modules are declarative; you use them to describe the state you want the server to be in. For example, you would invoke the user module like this to ensure there is an account named “deploy” in the web group:

user: 
  name: deploy
  group: web

Push Based

Chef and Puppet are configuration management systems that use agents. They are pull-based by default. Agents installed on the servers periodically check in with a central service and download configuration information from the service. Making configuration management changes to servers goes something like this:

  1. You: make a change to a configuration management script.

  2. You: push the change up to a configuration management central service.

  3. Agent on server: wakes up after periodic timer fires.

  4. Agent on server: connects to configuration management central service.

  5. Agent on server: downloads new configuration management scripts.

  6. Agent on server: executes configuration management scripts locally that change server state.

In contrast, Ansible is push-based by default. Making a change looks like this:

  1. You: make a change to a playbook.

  2. You: run the new playbook.

  3. Ansible: connects to servers and executes modules, which changes server state.

As soon as you run the ansible-playbook command, Ansible connects to the remote servers and does its thing.

Parallel Execution

The push-based approach has a significant advantage: you control when the changes happen to the servers. You do not need to wait around for a timer to expire. Each step in a playbook can target one or a group of servers. You get more work done instead of logging into the servers by hand.

Multi-tier Orchestration

Push-mode also allows you to use Ansible for multi-tier orchestration, managing distinct groups of machines for an operation like an update. You can orchestrate the monitoring system, the load balancers, the databases, and the webservers with specific instructions so they work in concert. That’s very hard to do with a pull-based system.

Master-less

Advocates of the pull-based approach claim that it is superior for scaling to large numbers of servers and for dealing with new servers that can come online anytime. A central system, however, slowly stops working when thousands of agents pull their configuration at the same time, especially when they need multiple runs to converge.

Pluggable and Embeddable

A sizable part of Ansible’s functionality comes from the Ansible Plugin System, of which the Lookup and Filter plugins are most used. Plugins augment Ansible’s core functionality with logic and features that are accessible to all modules. You can write your own plugins in Python (see Chapter 10).

You can integrate Ansible into other products, Kubernetes and Ansible Tower are examples of successful integration. Ansible-runner “is a tool and python library that helps when interfacing with Ansible directly or as part of another system whether that be through a container image interface, as a standalone tool, or as a Python module that can be imported.”

Using the ansible-runner library you can run an Ansible playbook from within a Python script:

#!/usr/bin/env python3
import ansible_runner
r = ansible_runner.run(private_data_dir='./playbooks', playbook='playbook.yml')
print("{}: {}".format(r.status, r.rc))
print("Final status:")
prinr(r.stats)

Works with Lots of Stuff

Ansible modules cater for a wide range of system administration tasks. This list has the categories of the kinds of modules that you can use. These link to the module index in the documentation.

Cloud

Files

Monitoring

Source Control

Clustering

Identity

Net Tools

Storage

Commands

Infrastructure

Network

System

Crypto

Inventory

Notification

Utilities

Database

Messaging

Packaging

Windows

Really Scalable

Large enterprises use Ansible successfully in production with tens of thousands of nodes and have excellent support for environments where servers are dynamically added and removed. Organizations with hundreds of software teams typically use AWX or a combination of Ansible Tower and Automation Hub to organize content, reach auditability and role-based access control. Separating projects, roles, collections, and inventories is a pattern that you will see often in larger organizations.

Secure

Automation with Ansible helps us to improve system security to security baselines and compliance standards.

Codified Knowledge

Your authors like to think of Ansible playbooks as executable documentation. They’re like the README files that used to describe the commands you had to type out to deploy your software, except that these instructions will never go out of date because they are also the code that executes. Product experts can create playbooks that takes best practices into account. When novices use such a playbook to install the product, they can be sure they’ll get a good result.

Reproducible systems

If you set up your entire system with Ansible, it will pass what Steve Traugott calls the “tenth-floor test”: “Can I grab a random machine that’s never been backed up and throw it out the tenth-floor window without losing sysadmin work?”

Equivalent environments

Ansible has a clever way to organize content that helps define configuration at the proper level. It is easy to create a setup for distinct development, testing, staging and production environments. A staging environment is designed to be as similar as possible to the production environment so that developers can detect any problems before going live.

Encrypted variables

If you need to store sensitive data such as passwords or tokens, then ansible-vault is an effective tool to use. We use it to store encrypted variables in git. We’ll discuss it in detail in Chapter 8.

Secure Transport

Ansible simply uses Secure Shell (SSH) for Linux and WinRM for Windows. We typically secure and harden these widely used systems-management protocols with strong configuration and firewall settings.

If you prefer using a pull-based model, Ansible has official support for pull mode, using a tool it ships with called ansible-pull. This book won’t cover pull mode, but you can read more about it in the official Ansible documentation.

Idempotency

Modules are also idempotent: if the deploy user does not exist, Ansible will create it. If it does exist, Ansible will not do anything. Idempotence is a nice property because it means that it is safe to run an Ansible playbook multiple times against a server. This is a vast improvement over the homegrown shell script approach, where running the shell script a second time might have a different (and unintended) effect.4

No Daemons

There is no Ansible agent listening on a port. Therefore, when you use Ansible, there is no attack surface.

What Is Ansible, Inc.’s Relationship to Ansible?

The name Ansible refers to both the software and the company that runs the open-source project. Michael DeHaan, the creator of Ansible the software, is the former CTO of Ansible the company. To avoid confusion, I refer to the software as Ansible and to the company as Ansible, Inc.

Ansible, Inc. sells training and consulting services for Ansible, as well as a web-based management tool called Ansible Tower, which I cover in Chapter 19. In October 2015, Red Hat bought Ansible, Inc.; IBM bought Red Hat in 2019.

Is Ansible Too Simple?

When Lorin was working an earlier edition of this book, the editor mentioned that “some folks who use the XYZ configuration management tool call Ansible a for-loop over SSH scripts.” If you are considering switching over from another configuration management tool, you might be concerned at this point about whether Ansible is powerful enough to meet your needs.

As you will soon learn, Ansible supplies a lot more functionality than shell scripts. In addition to idempotence, Ansible has excellent support for templating, as well as defining variables at different scopes. Anybody who thinks Ansible is equivalent to working with shell scripts has never had to support a nontrivial program written in shell. We will always choose Ansible over shell scripts for configuration management tasks if given a choice.

Worried about the scalability of SSH? Ansible uses SSH multiplexing to optimize performance, and there are folks out there who are managing thousands of nodes with Ansible (see chapter 12 of this book, as well as).

What Do I Need to Know?

To be productive with Ansible, you need to be familiar with basic Linux system administration tasks. Ansible makes it easy to automate your tasks, but it is not the kind of tool that “automagically” does things that you otherwise would not know how to do.

For this book, we have assumed that you are familiar with at least one Linux distribution (such as Ubuntu, RHEL/CentOS, or SUSE), and that you know how to:

  • Connect to a remote machine using SSH

  • Interact with the Bash command-line shell (pipes and redirection)

  • Install packages

  • Use the sudo command

  • Check and set file permissions

  • Start and stop services

  • Set environment variables

  • Write scripts (any language)

If these concepts are all familiar to you, you are good to go with Ansible.

We will not assume you have knowledge of any particular programming language. For instance, you do not need to know Python to use Ansible unless you want to publish your own module.

What Isn’t Covered

This book is not an exhaustive treatment of Ansible. It is designed get you working productively in Ansible as quickly as possible. It also describes how to perform certain tasks that are not obvious from the official documentation.

We don’t cover all of Ansible’s modules in detail: there are more than 3,500 of them. You can use the ansible-doc command-line tool with what you have installed to view the reference documentation and the module index mentioned above.

Chapter 8 covers only the basic features of Jinja2, the templating engine that Ansible uses, primarily because your authors memorize only basic features when we use Jinja2 with Ansible. If you need to use more advanced Jinja2 features in templates, check out the official Jinja2 documentation.

Nor do I go into detail about some features of Ansible that are mainly useful when you are running it on an older version of Linux.

Finally, there are several features of Ansible we don’t cover simply to keep the book a manageable length. These features include pull mode, logging, and using vars_prompt to prompt the user for passwords or input. We encourage you to check out the official documentation to find out more about these features.

Installing Ansible

All the major Linux distributions package Ansible these days, so if you work on a Linux machine, you can use your native package manager for a casual installation (although this might be an older version of Ansible). If you work on macOS, I recommend using the excellent Homebrew package manager to install Ansible:

$ brew install ansible

On any Unix/Linux/macOS machine, you can install Ansible using one of the Python package managers. This way you can add Python-based tools and libraries that work for you, provided you add ~/.local/bin to your PATH shell variable. If you want to work with Ansible Tower or AWX, then you should install the same version of ansible-core on your workstation. Python 3.8 is recommended on the machine where you run Ansible.

$ pip3 install --user ansible==2.9.20

Installing ansible>=2.10 installs ansible-base as well. Use ansible-galaxy to install the collections you need.

Note

As a developer, you should install Ansible into a Python virtualenv. This lets you avoid interfering with your system Python or cluttering your user environment. Using Python’s venv module and pip3, you can install just what you need to work on for each project.

$ python3 -m venv .venv --prompt A
$ source .venv/bin/activate
(A)

During activation of the environment, your shell prompt will change to (A)  as a reminder. Enter deactivate to leave the virtual environment.

Windows is not supported to run Ansible, but you can manage Windows remotely with Ansible.5

Loose Dependencies

Ansible plugins and modules might require that you install extra Python libraries.

(A) pip3 install pywinrm docker

In a way, the Python virtualenv was a precursor to containers: it creates a means to isolate libraries and avoid “dependency hell.”

Running Ansible in containers

Ansible-builder is a tool that aids in creating execution environments by controlling the execution of Ansible from within a container for single-purpose automation workflows. It is based on the directory layout of ansible-runner. This is an advanced subject, and outside the scope of this book. If you’d like to experiment with it, refer to the source code repository that complements this book.

Ansible Development

If you are feeling adventurous and want to use the bleeding-edge version of Ansible, you can grab the development branch from GitHub:

$ python3 -m venv .venv --prompt S
$ source .venv/bin/activate
(S) python3 -m pip install --upgrade pip
(S) pip3 install wheel
(S) git clone https://github.com/ansible/ansible.git --recursive
(S) pip3 install -r ansible/requirements.txt

If you are running Ansible from the development branch, you need to run these commands each time to set up your environment variables, including your PATH variable, so that your shell knows where the Ansible and ansible-playbooks programs are:

(S) cd ./ansible
(S) source ./hacking/env-setup

Setting Up a Server for Testing

You need to have SSH access and root privileges on a Linux server to follow along with the examples in this book. Fortunately, these days it’s easy to get low-cost access to a Linux virtual machine through most public cloud services.

Using Vagrant to Set Up a Test Server

If you prefer not to spend the money on a public cloud, I recommend you install Vagrant on your machine. Vagrant is an excellent open-source tool for managing virtual machines. You can use it to boot a Linux virtual machine inside your laptop, which you can use as a test server.

Vagrant has built-in support for provisioning virtual machines with Ansible: we’ll talk about that in detail in Chapter 3. For now, we’ll just manage a Vagrant virtual machine as if it were a regular Linux server.

Vagrant needs a hypervisor like VirtualBox installed on your machine. Download VirtualBox first, and then download Vagrant.

We recommend you create a directory for your Ansible playbooks and related files. In the following example, we’ve named ours “playbooks.” Directory layout is important for Ansible: if you place files in the right places, the bits and pieces come together.

Run the following commands to create a Vagrant configuration file (Vagrantfile) for an Ubuntu/Focal 64-bits virtual machine image, and boot it:

$ mkdir playbooks
$ cd playbooks
$ vagrant init ubuntu/focal64
$ vagrant up
Note

Note

The first time you use Vagrant, it will download the virtual machine image file. This might take a while, depending on your internet connection.

If all goes well, the output should look like this:

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'ubuntu/focal64'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'ubuntu/focal64' version '20210415.0.0' is up to date...
==> default: Setting the name of the VM: playbooks_default_1618757282413_78610
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
       default: Adapter 1: nat
==> default: Forwarding ports...
       default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
       default: SSH address: 127.0.0.1:2222
       default: SSH username: vagrant
       default: SSH auth method: private key
       default:
       default: Vagrant insecure key detected. Vagrant will automatically replace
       default: this with a newly generated keypair for better security.
       default:
       default: Inserting generated public key within guest...
       default: Removing insecure key from the guest if it's present...
       default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Mounting shared folders...
       default: /vagrant => /Users/lorin/dev/ansiblebook/ch01/playbooks

You should be able to log into your new Ubuntu 20.04 virtual machine by running the following:

$ vagrant ssh

If this works, you should see a login screen like this:

Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-72-generic x86_64)
  * Documentation: https://help.ubuntu.com
  * Management: https://landscape.canonical.com
  * Support: https://ubuntu.com/advantage
  System information as of Sun Apr 18 14:53:23 UTC 2021
  System load: 0.08 Processes: 118
  Usage of /: 3.2% of 38.71GB Users logged in: 0
  Memory usage: 20% IPv4 address for enp0s3: 10.0.2.15
  Swap usage: 0%
  
1 update can be installed immediately.
0 of these updates are security updates.
To see these additional updates run: apt list --upgradable

vagrant@ubuntu-focal:~$

A login with vagrant ssh lets you interact with the Bash shell, but Ansible needs to connect to the virtual machine by using the regular SSH client. Tell Vagrant to output its SSH configuration by typing the following:

$ vagrant ssh-config

On my machine, the output looks like this:

Host default
   HostName 127.0.0.1
   User vagrant
   Port 2222
   UserKnownHostsFile /dev/null
   StrictHostKeyChecking no
   PasswordAuthentication no
   IdentityFile                           /Users/lorin/dev/ansiblebook/ch01/playbooks/.vagrant/ machines/default/virtualbox/private_key
IdentitiesOnly yes
LogLevel FATAL

The important lines are shown here:

HostName 127.0.0.1
  User vagrant
  Port 2222
  IdentityFile 
/Users/lorin/dev/ansiblebook/ch01/playbooks/.vagrant/machines/default/virtualbox/private_key
Note

Note

Starting with version 1.7, Vagrant has changed how it manages private SSH keys: it now generates a new private key for each machine. Earlier versions used the same key, which was in the default location of ~/.vagrant.d/insecure_private_key. The examples in this book use Vagrant 2.2.14.

In your case, every field should be the same except for the path of the identity file.

Confirm that you can start an SSH session from the command line by using this information. The SSH command also works with a relative path from the playbooks directory.

$ ssh [email protected] -p 2222 -i .vagrant/machines/default/virtualbox/private_key

You should see the Ubuntu login screen. Type exit to quit the SSH session.

Telling Ansible About Your Test Server

Ansible can manage only the servers it explicitly knows about. You provide Ansible with information about servers by specifying them in an inventory. We usually create a directory called “inventory” to hold this information.

$ mkdir inventory

Each server needs a name that Ansible will use to identify it. You can use the hostname of the server, or you can give it an alias and pass other arguments to tell Ansible how to connect to it. We will give our Vagrant server the alias of testserver.

Create a text file in the inventory directory. Name the file vagrant.ini vagrant if you’re using a Vagrant machine as your test server; name it ec2.ini if you use machines in Amazon EC2.

The ini-files will serve as inventory for Ansible. They list the infrastructure that you want to manage under groups, which are denoted in square brackets. If you use Vagrant, your file should look like Example 1-1. The group [webservers] has one host: testserver. Here we see one of the drawbacks of using Vagrant: you need to pass extra vars data to Ansible to connect to the group. In most cases, you won’t need all this data.

Example 1-1. inventory/vagrant.ini
[webservers]
testserver ansible_port=2222
        
[webservers:vars]
ansible_host=127.0.0.1
ansible_user = vagrant
ansible_private_key_file = .vagrant/machines/default/virtualbox/private_key 

If you have an Ubuntu machine on Amazon EC2 with a hostname like ec2-203-0-113-120.compute-1.amazonaws.com, then your inventory file will look something like this:

[webservers]
testserver ansible_host=ec2-203-0-113-120.compute- 1.amazonaws.com
[webservers:vars]
ansible_user=ec2-user 
ansible_private_key_file=/path/to/keyfile.pem
Note

Ansible supports the ssh-agent program, so you don’t need to explicitly specify SSH key files in your inventory files. If you login with your own userid, then you don’t need to specify that either. See “SSH Agent” in appendix A for more details if you haven’t used ssh-agent before.

We’ll use the ansible command-line tool to verify that we can use Ansible to connect to the server. You won’t use the ansible command often; it’s mostly used for ad hoc, one-off things.

Let’s tell Ansible to connect to the server named testserver described in the inventory file named vagrant.ini and invoke the ping module:

$ ansible testserver -i inventory/vagrant.ini -m ping

If your local SSH client has host-key verification enabled, you might see something that looks like this the first time Ansible tries to connect to the server:

The authenticity of host '[127.0.0.1]:2222 ([127.0.0.1]:2222)' can't be established.
RSA key fingerprint is e8:0d:7d:ef:57:07:81:98:40:31:19:53:a8:d0:76:21.
Are you sure you want to continue connecting (yes/no)?

You can just type “yes.”

If it succeeds, the output will look like this:

testserver | SUCCESS => {
    "ansible_facts": {
         "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
Note

If Ansible did not succeed, add the -vvvv flag to see more details about the error:

$ ansible testserver -i inventory/vagrant.ini -m ping -vvvv

We can see that the module succeeded. The “changed”: false part of the output tells us that executing the module did not change the state of the server. The “ping”: “pong” output text is specific to the ping module.

The ping module doesn’t do anything other than check that Ansible can start an SSH session with the servers. It’s a tool for testing that Ansible can connect to the servers: very useful at the start of a big playbook.

Simplifying with the ansible.cfg File

You had to type a lot to use Ansible to ping your testserver. Fortunately, Ansible has ways to organize these sorts of variables, so you don’t have to put them all in one place. Right now, we’ll add one such mechanism, the ansible.cfg file, to set some defaults so we don’t need to type as much on the command line.

Where Should I Put My ansible.cfg File?

Ansible looks for an ansible.cfg file in the following places, in this order:

  1. File specified by the ANSIBLE_CONFIG environment variable

  2. ./ansible.cfg (ansible.cfg in the current directory)

  3. ~/.ansible.cfg (.ansible.cfg in your home directory)

  4. /etc/ansible/ansible.cfg

We typically put ansible.cfg in the current directory, alongside our playbooks. That way, we can check it into the same version-control repository that our playbooks are in.

Example 1-2 shows an ansible.cfg file that specifies the location of the inventory file (inventory) and sets parameters that affect the way Ansible runs, for instance how the output is presented.

Since the user you’ll log onto and its SSH private key depend on the inventory that you use, it is practical to use the vars block in the inventory file, rather than in the ansible.cfg file, to specify such connection parameter values. Another alternative is your ~/.ssh/config file.

Our example ansible.cfg configuration also disables SSH host-key checking. This is convenient when dealing with Vagrant machines; otherwise, we need to edit our ~/.ssh/known_hosts file every time we destroy and re-create a Vagrant machine. However, disabling host-key checking can be a security risk when connecting to other servers over the network. If you’re not familiar with host keys, see Appendix A.

Example 1-2. ansible.cfg
[defaults]
inventory = inventory/vagrant.ini
host_key_checking = false
stdout_callback = yaml
callback_enabled = timer
Note

Ansible and Version Control

Ansible uses /etc/ansible/hosts as the default location for the inventory file. However, Bas never uses this because he likes to keep his inventory files version-controlled alongside his playbooks. Also, he uses file extensions for things like syntax formatting in an editor.

Although we don’t cover version control in this book, we strongly recommend you commit to using the Git version-control system to save all changes to your playbooks. If you’re a developer, you’re already familiar with version-control systems. If you’re a systems administrator and aren’t using version control yet, this is a perfect opportunity for you to really start with infrastructure as code!

With your default values set, you can invoke Ansible without passing the -i hostname arguments, like so:

$ ansible testserver -m ping

We like to use the ansible command-line tool to run arbitrary commands on remote machines, like parallel SSH. You can execute arbitrary commands with the command module. When invoking this module, you also need to pass an argument to the module with the -a flag, which is the command to run.

For example, to check the uptime of your server, you can use this:

$ ansible testserver -m command -a uptime

Output should look like this:

testserver | CHANGED | rc=0 >>
  10:37:28 up 2 days, 14:11, 1 user, load average: 0.00, 0.00, 0.00 

The command module is so commonly used that it’s the default module, so you can omit it:

$ ansible testserver -a uptime

If your command has spaces, quote it so that the shell passes the entire string as a single argument to Ansible. For example, to view the last ten lines of the /var/log/dmesg logfile:

$ ansible testserver -a "tail /var/log/dmesg"

The output from our Vagrant machine looks like this:

testserver | CHANGED | rc=0 >>
[ 9.940870] kernel: 14:48:17.642147 main   VBoxService 6.1.16_Ubuntu r140961 (verbosity: 0) linux.amd64 (Dec 17 2020 22:06:23) release log
                    14:48:17.642148 main   Log opened 2021-04-18T14:48:17.642143000Z
[ 9.941331] kernel: 14:48:17.642623 main   OS Product: Linux
[ 9.941419] kernel: 14:48:17.642718 main   OS Release: 5.4.0-72-generic
[ 9.941506] kernel: 14:48:17.642805 main   OS Version: #80-Ubuntu SMP Mon Apr 12 17:35:00 UTC 2021
[ 9.941602] kernel: 14:48:17.642895 main   Executable: /usr/sbin/VBoxService
                    14:48:17.642896 main   Process ID: 751
                    14:48:17.642896 main   Package type: LINUX_64BITS_GENERIC (OSE)
[ 9.942730] kernel: 14:48:17.644030 main   6.1.16_Ubuntu r140961 started. Verbose level = 0
[ 9.943491] kernel: 14:48:17.644783 main   vbglR3GuestCtrlDetectPeekGetCancelSupport: Supported (#1)

If we need root access, pass in the -b flag to tell Ansible to become the root user. For example, accessing /var/log/syslog requires root access:

$ ansible testserver -b -a "tail /var/log/syslog"

The output looks something like this:

testserver | CHANGED | rc=0 >>
Apr 23 10:39:41 ubuntu-focal multipathd[471]: sdb: failed to get udev uid: Invalid argument
Apr 23 10:39:41 ubuntu-focal multipathd[471]: sdb: failed to get sysfs uid: No data available
Apr 23 10:39:41 ubuntu-focal multipathd[471]: sdb: failed to get sgio uid: No data available
Apr 23 10:39:42 ubuntu-focal multipathd[471]: sda: add missing path
Apr 23 10:39:42 ubuntu-focal multipathd[471]: sda: failed to get udev uid: Invalid argument
Apr 23 10:39:42 ubuntu-focal multipathd[471]: sda: failed to get sysfs uid: No data available
Apr 23 10:39:42 ubuntu-focal multipathd[471]: sda: failed to get sgio uid: No data available
Apr 23 10:39:43 ubuntu-focal systemd[1]: session-95.scope: Succeeded.
Apr 23 10:39:44 ubuntu-focal systemd[1]: Started Session 97 of user vagrant.
Apr 23 10:39:44 ubuntu-focal python3[187384]: ansible-command Invoked with _raw_params=tail /var/log/syslog warn=True _uses_shell=False stdin_add_newline=True strip_empty_ends=True argv=None chdir=None executable=None creates=None removes=None stdin=None

You can see from this output that Ansible writes to the syslog as it runs.

You are not restricted to the ping and command modules when using the ansible command-line tool: you can use any module that you like. For example, you can install Nginx on Ubuntu by using the following command:

$ ansible testserver -b -m package -a name=nginx
Note

If installing Nginx fails for you, you might need to update the package lists. To tell Ansible to do the equivalent of apt-get update before installing the package, change the argument from name=nginx to name=nginx update_cache=yes.

You can restart Nginx as follows:

$ ansible testserver -b -m service -a "name=nginx state=restarted"

You need the -b argument to become the root user because only root can install the Nginx package and restart services.

Kill your darlings

We will improve the setup of the test server in this book, so don’t become attached to your first virtual machine. Just remove it for now with:

$ vagrant destroy -f

Moving Forward

This introductory chapter covered the basic concepts of Ansible at a general level, including how it communicates with remote servers and how it differs from other configuration management tools. You’ve also seen how to use the Ansible command-line tool to perform simple tasks on a single host.

However, using Ansible to run commands against single hosts isn’t terribly interesting. The next chapter covers playbooks, where the real action is.

1 For more on building and maintaining these types of distributed systems, check out Thomas A. Limoncelli, Strata R. Chalup, and Christina J. Hogan, The Practice of Cloud System Administration, volumes 1 and 2 (Addison-Wesley, 2014), and Martin Kleppman, Designing Data-Intensive Applications (O’Reilly, 2017).

2 Yes, Azure supports Linux servers.

3 For example, see “Using Ansible at Scale to Manage a Public Cloud” (slide presentation, 2013), by Jesse Keating, formerly of Rackspace.

4 If you are interested in what Ansible’s original author thinks of the idea of convergence, see Michael DeHaan, “Idempotence, convergence, and other silly fancy words we use too often,” Ansible Project newsgroup post, November 23, 2013.

5 To learn why Windows is not supported on the controller, read Matt Davis, “Why no Ansible controller for Windows?” blog post, March 18, 2020.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
44.223.39.199