В интернете полно статей про то, что такое Ansible и как с ним работать. Много информации для новичков. Но все они, на мой взгляд, очень тяжелы в освоении на начальном этапе знакомства с Ansible. По этому предлагаю вашему вниманию свою интерпретацию на эту тему.
Asible
Итак, Ansible – это система управления программным обеспечением на удаленных серверах. Есть ряд аналогичных систем (Chief, Puppet) с другими принципами работы, требующих установки клиента и пр. В чём плюс Ansible – для работы на клиенте достаточно ssh соединения с ним.
Задача Ansible – выполнить ряд операций на удаленной машине. При этом машин может быть сразу несколько.
Playbook
Все операции вы записываете в некий Ansible-скрипт. Действия выполняются последовательно. Логика работы похожа на обычный bash-скрипт, только на своих командах. Это позволяет отвязаться от дистрибутива, описав задачи на промежуточном языке. Такой Ansible-скрипт называется playbook (сценарий). Формат его очень простой и построен на yaml-разметке.
- name: "First step"
hosts: localhost
tasks:
- taskA
- taskB
Тут мы описали группу задач. Дали ей общее имя, name: “First step”. Указали на каких хостах выполнять, hosts: localhost (только на локальном хосте).
Далее следует перечень задач, которые надо выполнить: tasks: taskA, taskB. Такая группа задач называется play. Это что-то типа блока задач, которые вы выделяете по какой-то своей логике. В одном playbook-е может быть несколько блоков (play-ев):
- name: "First step"
hosts: localhost
tasks:
- taskA
- taskB
- name: "Second step"
hosts: all
tasks:
- taskC
- taskD
Заметьте, второй блок (Second step) мы поставили для всех хостов (hosts: all). То есть в первом мы могли, к примеру, что-либо подготовить локально (создать виртуальные машины, подготовить файлы, залить их), а потом применить для всех машин во втором блоке.
Task
Теперь давайте рассмотрим что из себя представляют сами команды (tasks).
Как правило это всего-лишь модули Ansible. То есть вызывая какой-либо task, мы на самом деле вызываем модуль Ansible с его параметрами. По этому богатство “языка” Ansible зависит от количества его модулей.
Записать task можно в двух видах: кратком, и полном. Вот пример команды установки переменной (set_fact) в кратком виде:
- set_fact: mytext="Hello world"
Тогда наш play с этим task выглядит так:
- name: "First step"
hosts: localhost
tasks:
- set_fact: mytext="Hello world"
В данном скрипте мы установили переменную mytext, равную “Hello world” для хоста localhost.
Следующей командой можно вывести на экран переменную, используя модуль (команду) debug:
- name: "First step"
hosts: localhost
tasks:
- set_fact: mytext="Hello world"
- debug: var: mytext
Существует возможность более полно описать команду (task), дать ему имя (name). Совсем как в блоках задач (play). Тогда получившийся текст выглядит как подзадача нашего основного блока задач. На мой взгляд так более наглядно:
- name: "First step"
hosts: localhost
tasks:
- name: "Set variable"
set_fact:
mytext="Hello world"
- name: "Show variable"
debug:
var: mytext
В этому случае сразу видно, что есть блок задач “First step“, в нем существует 2 подзадачи. Первая – с установкой переменной. Вторая – вызывает модуль debug с параметром var: и указанием имени переменной mytext.
Запуск
Теперь можете сохранить получившийся текст в файл “first.yml” и запустить его так:
ansible-playbook ./first.yml
Вывод у вас будет:
PLAY [First step] *************************************************************************************************************
TASK [Gathering Facts] ********************************************************************************************************
ok: [localhost]
TASK [Set variable] ***********************************************************************************************************
ok: [localhost]
TASK [Show variable] **********************************************************************************************************
ok: [localhost] => {
"mytext": "Hello world"
}
PLAY RECAP ********************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0
Тут вы видите наш блок задач (PLAY), называемый “First step”, а так же команды (TASK) по установке переменной (Set variable) и ее выводу (Show variable). Все задачи завершились успешно (ok=3, failed=0). Ничего не изменилось в состоянии хоста (changed=0), т.к. мы ничего пока не меняли (не устанавливали ПО, не изменяли файлы).
Дополнительная задача “Gathering Facts” – получение информации о хосте (такой как имя хоста и пр.), вызвалась автоматически. Она необходима для использования данных о хосте в своих сценариях. Можно запретить эту задачу, указав gather_facts : false
- name: "First step"
hosts: localhost
gather_facts: false
....
Inventory
Второй блок задач использовал у нас директиву hosts: all. Что это значит? Где все эти хосты, на которых должна примениться наша группа команд?
Это файл hosts. Так называемый файл инвентаризации, в котором у нас хранится перечень машин, с которыми будет работать Ansible.
Располагаться он может в вашей директории и иметь примерно такой вид:
[mail]
mail-01.burlutsky.su
mail-02.myserver.lan ansible_host=10.10.10.6
[web]
web-01.myserver.lan ansible_host=10.10.10.12
Тут, как видно, указываются группы, в них имена хостов, можно с указанием переменных для хоста. Вообще inventory-файл очень гибкий. Можно комбинировать группы, включать одну в другую, задавать переменные для групп, глобальные значения:
[all:vars]
ansible_user=root
Подробнее смотрите в официальной документации. Нам же достаточно того, что вместо hosts: all можно указать конкретную группу:
hosts: web
А затем вызвать наш playbook, указав параметром файл инвентаризации:
ansible-playbook ./first.yml -i ./hosts
Roles
Можно разбивать ваши задачи по разным playbook-файлам. К примеру вынести первичную настройку в common.yml, установку Apache в apache.yml, MySQL – в mysql.yml, PHP – в php.yml, потом все это включить в файл lamp.yml
- name: "LAMP"
hosts: web
tasks:
- name: "Include common"
include: common.yml
...
Но я предлагаю сразу перейти к ролевой модели. Роль – это как раз ваш LAMP в данном случае. Набор определенных типовых задач для конкретной машины. Ее роль.
Для того, чтобы определить роль, мы должны создать определенную файловую иерархию и разложить все по директориям. Вот пример такой иерархии для роли с именем “common”:
playbooks
├── roles
│ ├── common
│ │ ├── defaults
│ │ │ └── main.yml
│ │ ├── files
│ │ │ └── text.txt
│ │ └── tasks
│ │ └── main.yml
Смысл в том, чтобы разнести все раздельно по директориям.
- Переменные по умолчанию – в директорию defaults.
- Файлы, используемые в роли (к примеру которые надо загрузить на сервер) – в дирекорию files.
Таких определенных директорий много.
- Шаблоны – templates.
- Обработчики задач (вызываемые после успешного или неуспешного из завершения) – handlers.
- Прочие переменные – vars.
- Метаданные – meta.
И в каждой директории главный файл – main.yml. Но не обязательно создавать все эти директории сразу. Достаточно создать roles/имя_роли/tasks/main.yml и прописать в нем все задачи, характерные для данной роли. Далее в осномном playbook-файле вы уже можете ссылаться на эти роли:
- name: "LAMP deployment"
hosts: web
become: true
roles:
- role: "common"
- role: "php"
- role: "apache"
- role: "mysql"
Примерно так может выглядеть ваш playbook для установки LAMP. Теперь его можно “накатывать” сразу на кучу серверов в вашем hosts-файле!
Ссылки
В заключении приведу полезные, на мой взгляд, для начинающего ссылки:
- https://blog.amet13.name/2016/02/ansible-playbook-nginx.html
- https://habr.com/ru/company/infobox/blog/252461/
- https://ealebed.github.io/posts/2015/знакомство-с-ansible-часть-5-роли-условия-и-циклы/
Удачи в освоении автоматизации вместе с Ansible!