Ansible is an open-source software for infrastructure management i.e. configuring and managing computers. Ansible is agent-less: a single controlling machine manages nodes over SSH connection; those nodes do not require additional software nor any background daemons. It has minimal software requirements i.e. Python 2.4 or later. It uses YAML as a description language which makes it self-documenting and easier to read than custom DSLs (additional layer of abstraction). In result, it is easier to use than similar, but older tools such as Puppet, Chef or Saltstack.
Install
You need Python 2.4 or later. The best way to install is through pip
package manager.
pip install ansible
Hands-on
Make a directory
mkdir foo && cd foo
Create an inventory file
# ~/foo/hosts
[local]
127.0.0.1
Create a playbook
# ~/foo/playbook.yml
- hosts: local
user: root
tasks:
- name: install curl
apt: pkg=curl state=present
- name: write to a file
shell: echo hello > /tmp/foo
Run the playbook (Debian/Ubuntu specific)
ansible-playbook -i hosts playbook.yml
Concepts
Playbooks are collections of steps, i.e. configuration policies to be applied to defined groups of hosts, written in YAML and executed in order they are written.
Playbooks are declarative and idempotent while shell scripts are imperative. It’s difficult to replay a shell script because it is very hard to write idempotent commands. Contrarily, Ansible only performs the tasks in the playbook that need to be performed: if the condition is already satisfied, there's nothing to do.
---
- hosts: localhost
connection: local
vars:
- foo: Hello
tasks:
- command: echo “{{foo}}”
An inventory defines a list of hosts; they can be defined on a system level with /etc/ansible/hosts
or with the environment variable ANSIBLE_HOSTS=<path-to-inventory>
, or per invocation with -i <path-to-inventory>
parameter.
An inventory file can define host groups using square brackets. If you declare a host in the inventory by hostname, it must be resolvable either in your /etc/hosts
file, or by a DNS lookup. E.g.
[webservers]
192.168.100.1 set_hostname=foo # how to set per-host variables.
[dbservers]
dbserver-1
Modules control system resources: services, files, commands e.g. apt/yum, file/copy, command/shell/script, service, ec2, etc.
---
- name: Install Python PIP & boto library
user: ubuntu
sudo: true
hosts: all
tasks:
- name: Install python-pip
apt: pkg=python-pip state=latest
- name: Install boto w/ PIP
pip: name=boto state=latest
Usage
Task Mode
In task mode, an Ansible module command is executed on a target node. A module is specified with -m
parameter along with its arguments specified with -a
parameter.
ansible web -m copy -a “src=script.sh dest=/usr/bin/script owner=root group=root mode=0755”
ansible web -m service -a “name=nginx state=restarted”
Groups can be combined
A:B
designates the union of groups A and BA:&B
designates the intersection of groups A and BA:!B
designates the difference, all from A without those in B
ansible -m ping web
ansible -m ping web:db
ansible -m ping web:&db
ansible -m ping web:!db
Playbook Mode
In playbook mode, commands are executed according to specified playbook. When Ansible runs a playbook, it will execute the tasks in the order they appear in that playbook - the process is meant to be repeated without differences.
For better organisation, a playbook should be composed of smaller, reusable parts. Those parts can be combined into a playbook either using include
statement or with roles.
An include
statement specifies a file with plays to include into that playbook.
# site.yml
---
- name: Install Python PIP & boto library
user: ubuntu
sudo: true
hosts: all
tasks:
- include: install-python.yml
# install-python.yml
- name: install python-pip
apt: pkg=python-pip state=latest
- name: install boot with PIP
pip: name=boto state=latest
Roles are built around include
statement (file references handling and opinionated structure) and provide the best way to organise playbooks. They encapsulate configuration/management steps related to a single thing. In general, a role consists of the following subdirectories, "files", “vars”, "handlers", "meta", "tasks" and "templates”.
# site.yml
---
- name: Install Redis
user: ubuntu
sudo: true
hosts: all
roles:
- redis
# roles/redis/tasks/main.yml
---
- name: Add the Redis PPA
apt_repository: repo='ppa:rwky/redis' update_cache=yes
- name: Install Redis from PPA
apt: pkg=redis-server state=installed
- name: Start Redis
service: name=redis state=started
Loop-like abstraction
with_item
allows to pass a list of elements into a module.
- name: install default packages
apt: pkg={{ item }} state=installed
with_items:
- curl
- vim
- htop
Private key
A private key used to log in into nodes can be specified with —private-key
parameter.
ansible-playbook acme.yml --private-key=/home/smith/.ssh/acme.priv