Ansible — сравнительно молодая система управления конфигурацией, его история насчитывает чуть более трех лет. Но, несмотря на это, он стремительно и быстро ворвался в мир систем управления конфигурацией, потеснив Chef, Puppet и SaltStack.
Давайте посмотрим на него внимательно, чтобы понять, почему он так любим технарями.
Итак, чем же хорош ansbile:
Начать пользоваться ansible можно за пару минут. Допустим, вы используете OSX.
Теперь создадим файл
Поехали:
Что мы сделали? Для всех хостов (параметр
Если модуль (ключ
Помимо утилиты ansible, есть еще утилита ansible-playbook, которой вы будете пользоваться наиболее часто.
Для дальнейших примеров я завел машинку t2.micro на aws с Ubuntu 14.04 и прописал ее в hosts.
Также, чтобы не вводить каждый раз в командную строку параметры, я в директории проекта создал файл
После этого создадим наш первый сценарий (playbook в терминологии ansible)
Запускаем наш первый сценарий, который обновляет кэш apt, а потом ставит два пакета: nginx и postgresql.
Фактически, мы два раза вызвали модуль apt, только с разными параметрами. Сам же файл сценарии представляет себя файл на языке Yaml с вкраплениями шаблонизатора Jinja2.
Действительно, для того, чтобы управлять машиной, на ней должен быть установлен Python (а он стоит по-умолчанию на всех современных linux системах) и должен быть доступ по ssh. Сравните это с остальными системами, где необходимо поставить клиента, которому нужны определенные версии различных языков и библиотек. Именно этот факт, кстати, делает старт с ansible гораздо более простым, чем для остальных систем.
Модуль можно написать на любом языке, он должен уметь принимать параметры на вход и выдавать json в ответ. Но зачем писать новый модуль, если уже есть 242 модуля на все случаи жизни (в версии 1.8.4). На случай, если вам действительно чего-то не хватает, есть хорошее описание того, как написать свой модуль.
Делать огромную простыню сценария не хотелось бы, поэтому давайте разобьем сценарий на части, используя механизм ролей.
Для тех, кому лень что-то писать самому, уже есть тысячи готовых ролей на сайте ansible galaxy, и они вполне достойного качества, чтобы пользоваться ими в бою.
Мы же создадим чистую роль.
Доработаем файл
Обратите внимание на ключ
А в файле
Используя роли, можно повторно использовать код (хотя является ли Yaml кодом — философский вопрос). Конечно, пример намеренно упрощен, но из него становится ясно, как организовывать на ansible проекты с большим количеством компонент.
В ansible есть встроенный модуль
Модуль setup собирает различные данные для ноды, это аналог ohai в chef (кстати, ansible тоже может использовать ohai для сбора информации). Все эти данные потом можно использовать в сценариях и шаблонах. Например, ip адрес по-умолчанию можно получить, обратившись к переменной
В ansible есть шаблоны, которые используют шаблонизатор Jinja2. Давайте сделаем 2 шаблона.
Обратите внимание на переменные в двойных фигурных скобках (например,
Для того, чтобы отрабатывать какие-то действия асинхронно, в ansible есть хендлеры (обработчики), которые можно вызвать из задач. Создадим свой хендлер, который перезагружает nginx.
Доработаем теперь файл задач роли nginx.
Как понятно из описания, данная задача ставит nginx, запускает его, удаляет файл конфигурации nginx по-умолчанию, создает файл конфигурации из шаблона
Итак, запустим получившийся сценарий.
Как видите, в конце nginx перезагрузил конфигурацию. Заглянем в браузер.

Вуаля! Мы установили и запустили nginx с нужным нам файлом конфигурации, сгенерировали для него страницу с данными из ansible, и перезагрузили nginx.
И чтобы вы не подумали, что ansible идеальный продукт, которому некуда развиваться, я расскажу о его недостатках.
Давайте посмотрим на него внимательно, чтобы понять, почему он так любим технарями.
Итак, чем же хорош ansbile:
- низкий порог входа;
- декларативный язык описания конфигурации;
- на управляемые узлы не нужно устанавливать никакого дополнительного ПО;
- просто написать дополнительный модуль.
Низкий порог входа
Начать пользоваться ansible можно за пару минут. Допустим, вы используете OSX.
$ brew install ansible
$ ansible --version
ansible 1.8.4
configured module search path = None
Теперь создадим файл
hosts
:[test]
localhost ansible_connection=local
Поехали:
$ ansible -i hosts -m ping all
localhost | success >> {
"changed": false,
"ping": "pong"
}
Что мы сделали? Для всех хостов (параметр
all
) из файла hosts выполнить модуль ping. Давайте посмотрим еще что-нибудь.ansible -i hosts -a 'ls -lah' all
$ ansible -i hosts -a "ls -lah" all
localhost | success | rc=0 >>
total 12K
drwxr-xr-x 5 brun staff 170 Apr 1 11:50 .
drwxr-xr-x 91 brun staff 3.1K Apr 1 11:37 ..
-rw-r--r-- 1 brun staff 230 Apr 1 12:07 export.sh
-rw-r--r-- 1 brun staff 42 Apr 3 14:48 hosts
-rw-r--r-- 1 brun staff 376 Apr 1 12:49 playbook.yml
Если модуль (ключ
-m
) не задан, то используется модуль command. Фактически, ansible можно использовать не только как систему управления конфигурацией, но и как фреймворк для распределенного выполнения команд.Декларативный язык описания конфигурации
Помимо утилиты ansible, есть еще утилита ansible-playbook, которой вы будете пользоваться наиболее часто.
Для дальнейших примеров я завел машинку t2.micro на aws с Ubuntu 14.04 и прописал ее в hosts.
# hosts
[web]
111.111.111.111
Также, чтобы не вводить каждый раз в командную строку параметры, я в директории проекта создал файл
ansible.cfg
.# ansible.cfg
[defaults]
hostfile = hosts
После этого создадим наш первый сценарий (playbook в терминологии ansible)
web.yml
.web.yml
# web.yml
---
- hosts: all
user: ubuntu
tasks:
- name: Update apt cache
apt: update_cache=yes
sudo: yes
- name: Install required packages
apt: name={{ item }}
sudo: yes
with_items:
- nginx
- postgresql
Запускаем наш первый сценарий, который обновляет кэш apt, а потом ставит два пакета: nginx и postgresql.
ansible-playbook web.yml
$ ansible-playbook web.yml
PLAY [all] ********************************************************************
GATHERING FACTS ***************************************************************
ok: [111.111.111.111]
TASK: [Update apt cache] ******************************************************
ok: [111.111.111.111]
TASK: [Install required packages] *********************************************
changed: [111.111.111.111] => (item=nginx,postgresql)
PLAY RECAP ********************************************************************
111.111.111.111 : ok=3 changed=1 unreachable=0 failed=0
Фактически, мы два раза вызвали модуль apt, только с разными параметрами. Сам же файл сценарии представляет себя файл на языке Yaml с вкраплениями шаблонизатора Jinja2.
На управляемые узлы не нужно устанавливать никакого дополнительного ПО
Действительно, для того, чтобы управлять машиной, на ней должен быть установлен Python (а он стоит по-умолчанию на всех современных linux системах) и должен быть доступ по ssh. Сравните это с остальными системами, где необходимо поставить клиента, которому нужны определенные версии различных языков и библиотек. Именно этот факт, кстати, делает старт с ansible гораздо более простым, чем для остальных систем.
Просто написать дополнительный модуль
Модуль можно написать на любом языке, он должен уметь принимать параметры на вход и выдавать json в ответ. Но зачем писать новый модуль, если уже есть 242 модуля на все случаи жизни (в версии 1.8.4). На случай, если вам действительно чего-то не хватает, есть хорошее описание того, как написать свой модуль.
Что-нибудь серьезное
Делать огромную простыню сценария не хотелось бы, поэтому давайте разобьем сценарий на части, используя механизм ролей.
Для тех, кому лень что-то писать самому, уже есть тысячи готовых ролей на сайте ansible galaxy, и они вполне достойного качества, чтобы пользоваться ими в бою.
Мы же создадим чистую роль.
ansible-galaxy init nginx -p roles
$ ansible-galaxy init nginx -p roles
- nginx was created successfully
$ tree
├── roles
│ └── nginx
│ ├── README.md
│ ├── defaults
│ │ └── main.yml
│ ├── files
│ ├── handlers
│ │ └── main.yml
│ ├── meta
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ ├── templates
│ └── vars
│ └── main.yml
Доработаем файл
web.yml
.web.yml
---
- hosts: all
user: ubuntu
sudo: yes
roles:
- nginx
Обратите внимание на ключ
sudo: yes
. Он позволяет не писать в каждом задании эту строку. Большинство административных задач все равно должны выполняться с правами root
, поэтому имеет смысл оставить его здесь.А в файле
roles/nginx/tasks/main.yml
напишем следующее:roles/nginx/tasks/main.yml
---
- name: Update apt cache
apt: update_cache=yes
- name: Install required packages
apt: name=nginx
Используя роли, можно повторно использовать код (хотя является ли Yaml кодом — философский вопрос). Конечно, пример намеренно упрощен, но из него становится ясно, как организовывать на ansible проекты с большим количеством компонент.
Факты
В ansible есть встроенный модуль
setup
, который выполняется первым для всех управляемых нод. Давайте посмотрим, что он делает.ansible -m setup all -u ubuntu
$ ansible -m setup all -u ubuntu
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"172.31.7.80"
],
"ansible_architecture": "x86_64",
"ansible_bios_date": "12/03/2014",
"ansible_bios_version": "4.2.amazon",
"ansible_cmdline": {
"BOOT_IMAGE": "/boot/vmlinuz-3.13.0-44-generic",
"console": "ttyS0",
"ro": true,
"root": "UUID=fd803688-5c41-4188-8a06-382a65a520bf"
},
"ansible_default_ipv4": {
"address": "172.31.7.80",
"alias": "eth0",
"gateway": "172.31.0.1",
"interface": "eth0",
"macaddress": "06:a8:07:41:47:a5",
"mtu": 9001,
"netmask": "255.255.240.0",
"network": "172.31.0.0",
"type": "ether"
}
... и так далее
Модуль setup собирает различные данные для ноды, это аналог ohai в chef (кстати, ansible тоже может использовать ohai для сбора информации). Все эти данные потом можно использовать в сценариях и шаблонах. Например, ip адрес по-умолчанию можно получить, обратившись к переменной
ansible_default_ipv4
.# web.yml
tasks:
- debug: msg={{ansible_default_ipv4}}
ansible-playbook web.yml
$ ansible-playbook web.yml
PLAY [all] ********************************************************************
GATHERING FACTS ***************************************************************
ok: [111.111.111.111]
TASK: [debug msg="{{ansible_default_ipv4}}"] **********************************
ok: [111.111.111.111] => {
"msg": "{u'macaddress': u'06:a8:07:41:47:a5', u'network': u'172.31.0.0', u'mtu': 9001, u'alias': u'eth0', u'netmask': u'255.255.240.0', u'address': u'172.31.7.80', u'interface': u'eth0', u'type': u'ether', u'gateway': u'172.31.0.1'}"
}
Шаблоны
В ansible есть шаблоны, которые используют шаблонизатор Jinja2. Давайте сделаем 2 шаблона.
# roles/nginx/templates/ansible.conf.j2
server {
listen 80 default_server;
root /usr/share/nginx/html;
index index.html index.htm;
}
# roles/nginx/templates/index.html.j2
<html>
<body>
<pre>
{{ ansible_default_ipv4 }}
{{ ansible_env }}
</pre>
</body>
</html>
Обратите внимание на переменные в двойных фигурных скобках (например,
{{ ansible_env }}
) — это переменные ansible, которые мы собрали с помощью уже известного нам модуля setup
.Хендлеры
Для того, чтобы отрабатывать какие-то действия асинхронно, в ansible есть хендлеры (обработчики), которые можно вызвать из задач. Создадим свой хендлер, который перезагружает nginx.
# roles/nginx/handlers/main.yml
---
# handlers file for nginx
- name: reload nginx
service: name=nginx state=reloaded
Доработаем теперь файл задач роли nginx.
roles/nginx/tasks/main.yml
# roles/nginx/tasks/main.yml
---
- name: Update apt cache
apt: update_cache=yes
- name: Install required packages
apt: name=nginx
- name: Start nginx service
service: name=nginx state=started
- name: Delete default nginx site
file: path=/etc/nginx/sites-enabled/default state=absent
notify: reload nginx
- name: Create default nginx site
template: src=ansible.conf.j2 dest=/etc/nginx/sites-enabled/ansible owner=www-data group=www-data
notify: reload nginx
- name: Create index.html file
template: src=index.html.j2 dest=/usr/share/nginx/html/index.html owner=www-data group=www-data
Как понятно из описания, данная задача ставит nginx, запускает его, удаляет файл конфигурации nginx по-умолчанию, создает файл конфигурации из шаблона
ansible.conf.j2
, который мы написали выше, и генерирует файл index.html
из шаблона, который мы описали выше. Обратите внимание, что некоторые задачи посылают нотификацию notify: reload nginx
. Причем, в данном случае должно быть послано 2 нотификации на перезагрузку nginx, но, по факту, они объединятся в одну, как будет видно ниже. Нотификации необходимы, чтобы перезагружать nginx (или любой другой сервис) только в случае, если шаблон (или что-то другое) изменился, чтобы не делать это каждый раз при запуске ansible.Итак, запустим получившийся сценарий.
ansible-playbook web.yml
$ ansible-playbook web.yml
PLAY [all] ********************************************************************
GATHERING FACTS ***************************************************************
ok: [111.111.111.111]
TASK: [nginx | Update apt cache] **********************************************
ok: [111.111.111.111]
TASK: [nginx | Install required packages] *************************************
ok: [111.111.111.111]
TASK: [nginx | Start nginx service] *******************************************
ok: [111.111.111.111]
TASK: [nginx | Delete default nginx site] *************************************
changed: [111.111.111.111]
TASK: [nginx | Create default nginx site] *************************************
changed: [111.111.111.111]
TASK: [nginx | Create index.html file] ****************************************
changed: [111.111.111.111]
TASK: [debug msg="{{ansible_default_ipv4}}"] **********************************
ok: [111.111.111.111] => {
"msg": "{u'macaddress': u'06:a8:07:41:47:a5', u'network': u'172.31.0.0', u'mtu': 9001, u'alias': u'eth0', u'netmask': u'255.255.240.0', u'address': u'172.31.7.80', u'interface': u'eth0', u'type': u'ether', u'gateway': u'172.31.0.1'}"
}
NOTIFIED: [nginx | reload nginx] **********************************************
changed: [111.111.111.111]
PLAY RECAP ********************************************************************
111.111.111.111 : ok=9 changed=4 unreachable=0 failed=0
Как видите, в конце nginx перезагрузил конфигурацию. Заглянем в браузер.

Вуаля! Мы установили и запустили nginx с нужным нам файлом конфигурации, сгенерировали для него страницу с данными из ansible, и перезагрузили nginx.
Недостатки
И чтобы вы не подумали, что ansible идеальный продукт, которому некуда развиваться, я расскажу о его недостатках.
- Отсутствие менеджера зависимостей. Сейчас все роли можно хранить в репозитарии, но как работать с зависимостями и обновлять роли — внятного ответа нет. Все пользуются этим подходом (так же было с Chef, например, 3 года назад), но при разрастании проекта очевидны недостатки такого подхода. UPDATE Менеджер зависимостей в каком-то виде есть, непонятно только почему об этом написано «снизу мелким шрифтом».
- Неполная документация. Ответы на некоторые очевидные вопросы приходится гуглить, и зачастую они находятся в частных блогах и тикетах на github.
- Режим ansible-pull для больших инсталляций потребует серьезной «доработки напильником».
- Неудобный дебаг. Даже при вызове с ключом
-vvvv
не всегда понятно, какая же именно команда выполнялась на сервере и что помешало ей выполниться. - Быстрая разработка. Конечно, у этого недостатка есть обратная сторона — ansible действительно быстро развивается. Но я наткнулся на баг, когда версия 1.8.1 работала, а 1.8.4 — сломалась.