There’s a new kid on the block in the configuration management world that claims to be lean, simple and easy to understand. We’re using it internally for some exciting new projects, and have found that it lives up to it’s promise.
Ansible’s goal is to unify two similar but traditionally separate tools: configuration management and deployment. Instead of using a combination of Puppet + Capistrano or Chef + Fabric, you can now use a single tool to update your configuration, deploy new code or execute ad-hoc tasks against groups of servers. There is no requirement to setup any daemons or any software at all on the target servers – as long as you can reach them over SSH, you’re good. There is also very little setup on the machine running Ansible, since it only relies on a few standard Python libraries. There’s a detailed comparison page in the FAQ that compares Ansible to these various other tools.
To illustrate some of the features, we’ve created a playbook that sets up an Ubuntu 12.04 server as a multi-user LAMP developer environment. Our old method of creating a server would be to perform these steps once, document them on a wiki, and then anytime we want another similar server, clone the first one or repeat the steps manually. This procedure is fragile over time and in the case of cloning, virtualization-platform specific. It’s far better to create a reusable, automated set of steps that can be tweaked and improved, and executed against any fresh Ubuntu 12.04 server, running anywhere. The example is documented and fully functional, although it’s not 100% secured so be careful.
Playbooks
The tasks that need to be performed are recorded in YAML formatted Ansible playbooks, which are then executed against the target host. The main components of a ‘play’ are:
- Hosts against which the actions are performed.
- Variables that can be used in the play or in file templates.
- Actions that will be performed when the play runs.
- Handlers that respond to change events from the actions.
Here is an abbreviated example from our server build playbook file, setup.yml, that will apply php.ini and restart Apache if the file has changed:
setup.yml
--- - hosts: all user: root vars_files: - vars/settings-default.yml tasks: - name: PHP configuration file, php.ini action: template src=templates/etc-php5-apache2-php-ini.j2 dest=/etc/php5/apache2/php.ini notify: Restart Apache handlers: - name: Restart Apache action: service name=apache2 state=restarted
The first two directives, ‘host’ and ‘user’, are easy – they specify that the play should by default run against all hosts, as the root user. In the following sections we’ll cover what the others do.
Variables
The vars_files section lists files that will be imported into the current play. The variables specified in these files are then available to use as value substitution or logic control in both the play and in the templates.
In our case, the settings-default.yml file contains a list of configuration variables for the services like Apache, MySQL, etc. They’re all organized into their respective sections, but looking at the file as a whole we get a great birds-eye view of how the play will be modifying the default server configuration. Here is an excerpt:
settings-default.yml
--- # php.ini php_max_execution_time: '90' php_display_errors: 'On' ... # my.cnf mysql_max_allowed_packet: '128M' mysql_character_set_server: 'utf8' ...
Any value that will be modified from it’s default out-the-box state is recorded in this one place. This makes changing settings really easy – no more grepping through thousands of lines of configuration in the master template file.
Tasks & Handlers
tasks: - name: PHP configuration file, php.ini action: template src=templates/etc-php5-apache2-php-ini.j2 dest=/etc/php5/apache2/php.ini notify: Restart Apache
These are the meat of the playbook. Each task is given a descriptive name and an action. In the example above, the action is ‘template’ and the parameters ‘src’ and ‘dest’ point to a source template on the local host, and a destination location on the target host, respectively.
To execute this step, the Ansible ‘template’ module will be invoked, passing the etc-php5-apache2-php-ini.j2 file through the Jinja2 templating engine, which performs variable substitutions in the appropriate place. For example, in this template we can insert the value of the variable php_max_execution_time (sourced from settings-default.yml above) in it’s correct place:
etc-php5-apache2-php-ini.j2 which becomes php.ini
; Maximum execution time of each script, in seconds max_execution_time = {{ php_max_execution_time }} ...
The great thing about Ansible modules is that they’re idempotent, meaning no changes will be made if the new file is identical to the old one. And since we have this data available, we can trigger events when changes are actually made. That’s what the ‘notify’ section does – when Ansible detects this file has changed, it will call ‘Restart Apache’, which is defined at the end of the play and does exactly what you might think it does.
Modules
When Ansible executes a task, it creates an SSH connection to the target server and copies the required module over. The module is then executed on the target with the correct parameters, and all the module has to do is return a JSON object containing pass/fail and other optional status information.
This has several advantages: for one, modules can be written in any scripting language, as long as the target can execute that code from the shell. It also means your modules can be quite sophisticated, and since they’re running locally on the target server as opposed to sending individual commands over the wire, they run fast.
There are git, apt, yum and service modules, just to name a few. Developing extra modules is easy and there is a growing collection of ‘contrib’ modules that come from the community but are not part of core Ansible.
Command mode
In addition to creating playbooks, you can also execute ad-hoc tasks against your servers using the exact same modules and syntax. For example, I could execute from the command line:
ansible webservers -m shell -a '/sbin/reboot now'
That would reboot all servers defined in the ‘webservers’ pool. This shared syntax and configuration is one of Ansible’s strengths, we can reuse a single server cluster specification for all tasks.
Project Status
Michael DeHaan is the project lead, and he has a lot of experience in this area, having worked at Red Hat & Puppet Labs, co-created Func and developed Cobbler.
Ansible is still very new, having only been released this year. However, it’s already used in production and there is a strong community forming. Michael announced recently that he’d written almost zero code in the upcoming 0.5 release, a sure sign of good community momentum.
Resources
Check out the pedantically commented playbook example, that covers almost all the features in one place.
The Ansible mailing list is active and friendly.
Making the web a better place to teach, learn, and advocate starts here...
When you subscribe to our newsletter!