Ansible
Редактировать на GitHubТеоретические вопросы
Как в плейбуке перезаписать дефолтные переменные?
Есть несколько способов, так как у переменных в Ansible разные уровни приоритета:
- Через extra-vars (CLI, самый высокий приоритет)
ansible-playbook site.yml -e "my_var=cli_value"- Через
vars:в самом плейбуке
- hosts: all
roles:
- myrole
vars:
my_var: 'new_value'- Через
vars_files
- hosts: all
roles:
- myrole
vars_files:
- override.yml # внутри: my_var: "value_from_file"- Через
set_fact(во время выполнения)
- hosts: all
tasks:
- set_fact:
my_var: 'runtime_value'- Через
group_varsилиhost_vars(group_vars/web.yml)
my_var: 'from_inventory'Для чего нужен ad hoc в ansible?
Это режим работы ансибл когда запрос к серверу выполняется напрямую из командной строки, без создания дополнительных файлов.
Что такое роли?
Роли имеют свою структуру каталогов, которая выглядит так:
rolename
- files
- handlers
- meta
- templates
- tasks
- varsНазначение директорий:
files: содержит файлы, которые будут скопированы на настраиваемые хосты; так же – может содержать скрипты, которые позже будут запускаться на хостах;handlers: обработчики, которые будут использоваться при выполнении задач;meta: описание зависимостей, т.е. – ролей, которые должны быть обработаны перед запуском настраиваемой роли и мета-данных, таких как автор, описание продукта и прочее;templates: шаблоны файлов с переменными;tasks: все задачи, которые ранее были описаны в Playbookе;vars: переменные для шаблонов.
Что такое идемпотентность? Приведи пример таких операций, и противоположных им
Идемпотентность это когда делаем одну и ту же операцию много раз, и при многократном ее повторении результат будет таким же, как в первый раз. Некоторые операции не являются идемпотентными сами по себе, и там потребуется дополнительная логика. Например, делаем запрос к странице, просто ее качаем - это идемпотентно. Потому что результат не изменится. Допустим делаем DELETE запрос. Он сам по себе тоже идемпотентен, ибо удаляет то, что мы хотели изначально удалить. Однако, сначала сервер ответил 200, потом ответил 404. Но общее состояние всё то же, файл же уже удалён.
В ансибле есть идемпотентность в модулях. Если я скажу создать файл при первом запуске он его создаст. При втором он увидит, что создано, и создавать не будет. Но может быть проблема например с распаковкой архива. Мы можем просто его распаковывать и распаковывать в какую-то директорию. Это уже ближе к неидемпотентному сценарию.
Ответ в контексте ансибла Идемпотентность - это такая характеристика действия, согласно которой повторное выполнение этого действия будет давать тот же результат, что и первый запуск. В контексте систем управления конфигурациями это означает, что сценарии, написанные с соблюдением такого подхода, не изменят, не сломают и не выдадут ошибок на управляемом хосте при повторном запуске.
Например, нам нужно добавить пользователя на хост, для bash это будет выглядеть так:
useradd newuser -ms /bin/bashНо если мы запустим команду второй раз, то получим ошибку
useradd newuser -ms /bin/bash
useradd: user 'newuser' already existsПоэтому нам нужно дополнительно добавлять проверку, например так:
id newuser || useradd newuser -ms /bin/bashВ случае Ansible мы только декларируем состояние, например:
- hosts: localhost
gather_facts: no
tasks:
- user:
name: newuser
shell: /bin/bash
state: presentПри первом запуске плейбук Ansible ответит:
TASK [user] ***
changed: [localhost] => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": true,
"comment": "",
"create_home": true,
"group": 1001,
"home": "/home/newuser",
"name": "newuser",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 1001
}При повторном:
TASK [user] ***
ok: [localhost] => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"append": false,
"changed": false,
"comment": "",
"group": 1001,
"home": "/home/newuser",
"move_home": false,
"name": "newuser",
"shell": "/bin/bash",
"state":
"present",
"uid": 1001
}Обратите внимание, что статус задания сменился с changed на ok и повторный запуск вернул значение "changed": false
Таким образом, сценарии, написанные с учетом идемпотентности, реализуют ту самую декларативность в описании состояния инфраструктуры, а инфраструктура соответствует состоянию, описанному в коде нашего скрипта. Это и есть IaС - Infrastructure as Code - инфраструктура как код
Для чего нужны хендлеры, handlers?
Задачу можно дополнить обработчиками, которые будут срабатывать, если задача была выполнена успешно.
Например, мы установили nginx и хотим его запустить
---
- hosts: testbox
sudo: yes
tasks:
- name: Install Nginx
yum: pkg=nginx state=latest
notify:
- NGINX start
handlers:
- name: NGINX start
service: name=nginx state=startedВ чем разница pull и push модели?
Pull-модель преполагает, что управляемые хосты подтягивают инструкции с мастер-сервера. Push-модель реализует подход, когда инструкции по желанию или по событию доставляются с управляющего на управляемые хосты.
Ansible чаще всего используется для push-модели управления, но умеет и pull-модель.
В чем плюсы ансибла?
Выгодные отличия Ansible от других систем управления конфигурациями:
- быстро осваивается, достаточно поверхностного понимания синтаксиса YAML и Jinja
- нет необходимости устанавливать специальное ПО на хосты, нужен только SSH и python
- подробная и наглядная документация
- большое количество модулей
- позволяет реализовать принцип идемпотентности в управлении состояниями хостов
Опишите основные примитивы Ansible
Инвентарь (Inventory)
- cписок хостов, может быть статичным в виде текcтового файла в формате
.iniили динамическим в виде скрипта или плагина, который подгружает структуру данных из стороннего источника, например, Openstack API или база LDAP.
$ cat hosts
[web]
nginx01
nginx02
nginx03
[mysql]
mysql01
mysql02
mysql03
[prod:children]
web
mysql
[devel]
dev-nginx01
dev-mysql01Данный пример описывает группы хостов web, mysql, prod и devel, группа prod наследует содержимое групп web и mysql.
Задание (Task)
- атомарная операция, выполняемая на управляемом хосте, например:
apt:
package: nginx
state: presentДанный пример аналогичен команде apt install nginx
Сценарий (Play) или Плейбук (Playbook)
- сценарий или скрипт, содержащий одно или несколько заданий на выполнение, например:
$ cat prod-playbook.yml
---
- hosts: nginx
tasks:
- apt:
package: nginx
state: present
- shell: whoamiРоль (Role)
- более сложная абстракция, выглядит как структура директорий и файлов, которые описывают набор дефолтных переменных, зависимостей от других ролей, может содержать файлы и темплейты, содержит задания(Tasks). Факты (Facts)
- структура данных, которая содержит информацию о хосте, например, версию дистрибутива, IP адреса и файловые системы. Ansible забирает эту информацию с хоста, и на нее можно ссылаться в коде плейбуков и ролей.
Сбор фактов хоста занимает некоторое время, поэтому их можно кэшировать или отключить их сбор при выполнении плейбука.
В чем разница между модулем и плагином
Модули что-то делают на хостах. Модули вызываются в задачах (tasks) плейбука Плагины расширяют функционал ансибла
Практические вопросы
Какие зависимости (явные и неявные) используются в этой роли
Явные зависимости (объявлены напрямую)
---
galaxy_info:
role_name: rhel_python_sub
description: Locate or install actual Python version on EL6/7
min_ansible_version: "2.11"
platforms:
- name: EL
versions:
- "6"
- "7"
dependencies:
- role: southbridge.aux.repo_southbridge
...- Ansible ≥ 2.11
- Поддерживаемые платформы: EL6 и EL7
- Роль-зависимость:
southbridge.aux.repo_southbridge
➜ То есть перед использованием rhel_python_sub должна быть применена роль southbridge.aux.repo_southbridge (вероятно, она подключает нужные репозитории для Python).
Неявные зависимости (следуют из логики роли)
- Зависимость от фактов ОС (EL6/EL7) и vars-файлов
- name: OS version-specific vars included
include_vars:
file: "el{{ ansible_distribution_major_version }}.yml"- Роль ожидает корректно собранные факты (
ansible_distribution_major_version) - И наличие соответствующих файлов:
vars/el6.yml,vars/el7.yml.
- Зависимость от файловой иерархии/бинарей Python
- name: Python binaries found
register: __rhel_python_sub_find
find:
patterns: "{{ rhel_python_sub__set }}"
paths: "{{ rhel_python_sub__paths }}"Имена бинарей и пути берутся из vars/el6.yml / vars/el7.yml.
Примеры:
el6.yml:rhel_python_sub__paths: [/opt/rh/rh-python36/root/usr/bin, /usr/bin],rhel_python_sub__set: [python3.6, python3.5, python2.7]el7.yml:rhel_python_sub__paths: [/usr/bin],rhel_python_sub__set: [python3.8, python3.7, python3.6]
- Контракт для других задач/ролей (факт про интерпретатор)
- name: Fact about interpreter path defined from find results
when: __rhel_python_sub_find.files|length
set_fact:
_rhel_python_sub_interpreter: "{{ __rhel_python_sub_find.files|map(attribute='path')|list|first }}"- Роль выставляет
_rhel_python_sub_interpreter— это неявный контракт, что дальше потребители будут его использовать.
- Требование повышенных привилегий (в тестах)
gather_subset: [ 'min' ]
become: true
roles:
- southbridge.aux.rhel_python_sub- Для установки пакетов нужен
become: true.
- Отсутствие обязательных входных параметров
argument_specs:
main:
short_description: The main entry point for the rhel_python_sub role.
options: {}- Все входы — необязательные; роль полагается на факты ОС и свои defaults/vars.
Возможные другие места для поиска (в других ролях)
meta/main.yml—dependencies,platforms,min_ansible_version.galaxy.yml/MANIFEST.json(в коллекциях) — зависимости коллекций.tasks/**/*.yml— используемые модули (apt/yum/dnf/package/pip,get_url,command/shell), отсюда видны системные и сетевые зависимости.defaults/main.ymlиvars/**— какие переменные роль ожидает (обязательные/дефолтные).templates/**иfiles/**— конфиги/скрипты, которые намекают на пакеты, пути, сервисы.handlers/main.yml— какие сервисы перезапускаются (косвенно — какие пакеты ставятся).molecule/**и CI (.github/workflows,.gitlab-ci.yml) — на каких образах/ОС роль тестируется, какие пакеты докатывают в CI.- README / CHANGELOG — нередко прямо перечисляют внешние требования (репозитории, подписки, версии Python и т.п.).
Последнее обновление: 14 окт. 2025 г., 12:08:27