Files
resume/stack/ansible/QUESTIONS.md

60 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Вопросы: Ansible
Опираются на кейс: [CASE.md](CASE.md).
### Что такое идемпотентность и почему это важно в Ansible?
Идемпотентность — свойство операции давать один и тот же результат при повторном запуске, не ломая и не дублируя изменения. В Ansible это значит, что повторный прогон плейбука на уже настроенном хосте не должен ничего менять (задачи должны показывать `ok`, а не `changed`). Я это проверял на своей роли nginx_deploy через `molecule test` — там есть отдельный шаг idempotence, который прогоняет converge второй раз и падает, если что-то опять помечено как changed.
### Чем роль (role) отличается от плейбука (playbook)?
Плейбук — это конкретный сценарий: какие хосты, какие роли/задачи применить и в каком порядке. Роль — переиспользуемый, самодостаточный блок автоматизации со своей структурой папок (tasks, handlers, templates, defaults, vars), который можно подключать в разные плейбуки. У меня playbook.yml просто вызывает роль nginx_deploy — так удобнее переиспользовать её в других плейбуках без копирования кода.
### Зачем нужны handlers и чем они отличаются от обычных задач?
Handler — задача, которая выполняется только по уведомлению (`notify`) от другой задачи, и только один раз в конце плейбука, даже если её нотифицировали несколько раз. Типичный пример — перезагрузка сервиса только если поменялся конфиг: в моей роли задача `template` нотифицирует хендлер `reload nginx`, и nginx перезагружается только тогда, когда конфиг реально изменился, а не при каждом прогоне.
### Что такое defaults/vars и в чём разница между ними по приоритету?
`defaults/main.yml` — переменные с самым низким приоритетом, задуманы как значения по умолчанию, которые легко переопределить снаружи роли (в inventory, playbook, group_vars). `vars/main.yml` — переменные роли с более высоким приоритетом, обычно используются для внутренних констант роли, которые не предполагается менять снаружи. В своей роли я вынес `nginx_worker_connections` и `nginx_server_name` в defaults именно потому, что это то, что вызывающий плейбук может захотеть переопределить.
### Зачем нужен Molecule, если можно просто запустить playbook на тестовом сервере?
Molecule автоматизирует весь цикл проверки роли: поднимает изолированное окружение (у меня — Docker-контейнер), прогоняет роль, проверяет идемпотентность, гоняет verify-тесты и уничтожает окружение — и всё это одной командой, без ручного создания и удаления тестовых серверов. Это то, что можно встроить в CI (в моей практике — TeamCity), чтобы роль проверялась автоматически на каждый коммит до попадания в прод.
### Чем отличается модуль `command`/`shell` от специализированных модулей вроде `service`, `package`, `template`?
Специализированные модули декларативны и идемпотентны из коробки — они сами проверяют текущее состояние и меняют его только при необходимости (например, `service` не будет перезапускать уже запущенный сервис). `command`/`shell` выполняют произвольную команду безусловно и по умолчанию всегда репортят `changed`, что ломает идемпотентность, если не добавлять вручную `creates`/`changed_when`. В своей роли я использовал `package`, `template`, `service` именно чтобы не терять идемпотентность.
### Как Ansible понимает, какие хосты — цель для выполнения?
Через inventory — статический файл (INI/YAML со списком хостов и групп) или динамический inventory-скрипт/плагин, который генерирует список из внешнего источника (облако, CMDB и т.п.). В playbook секция `hosts:` указывает, на какую группу/хост из inventory применять роль. В лабе у меня Molecule сам генерирует временный inventory на Docker-инстанс, в проде — обычно статический или динамический inventory отдела.
### Как безопасно хранить секреты (пароли, токены) в Ansible?
Через `ansible-vault` — шифрование файлов с чувствительными переменными, которые потом расшифровываются на лету при прогоне плейбука с паролем/ключом vault. Секреты не должны лежать в открытом виде в репозитории с ролями.
### Что произойдёт, если задача в роли завершится с ошибкой на середине выполнения?
По умолчанию Ansible останавливает выполнение плейбука на этом хосте (fail fast) — дальнейшие задачи для этого хоста не выполняются, но выполнение на других хостах продолжается независимо (если запуск на несколько хостов). Это поведение можно переопределить через `ignore_errors`, `block/rescue` для обработки ошибок или `any_errors_fatal` для остановки всего прогона.
### Как вы тестировали конкретно свою роль и что бы произошло, если бы шаблон был некорректным?
Я специально ломал `nginx.conf.j2` (опечатка в директиве) и запускал `molecule test` — прогон падал на этапе converge, потому что Ansible применяет конфиг, а nginx не стартует/не резолвит синтаксис, что видно в выводе `service` модуля. Это и есть основная ценность прогона перед CI — ошибка находится сразу, а не после деплоя на реальный сервер.
### Где в Ansible живут переменные — в ролях, плейбуках или инвентарях, и зачем это разделение?
Переменные могут жить на всех трёх уровнях, и у каждого — своя роль. В роли (`defaults/main.yml`, `vars/main.yml`) — значения, специфичные для самой роли (см. вопрос про defaults/vars выше). В плейбуке (`vars:` секция) — значения, специфичные для конкретного сценария применения роли. В inventory (`group_vars/`, `host_vars/`) — значения, специфичные для конкретной группы хостов или отдельного хоста (например, разный `nginx_server_name` для разных окружений). Разделение нужно, чтобы одна и та же роль оставалась переиспользуемой: логика роли не меняется, а конкретные значения приходят снаружи в зависимости от того, где и для чего роль применяется.
### Как переопределить переменную роли?
Проще всего — передать её на более высоком приоритете, чем `defaults/main.yml` (самый низкий приоритет намеренно, чтобы легко переопределять): через `group_vars`/`host_vars` в inventory, через `vars:` в самом плейбуке при вызове роли, или через `-e` в командной строке (`ansible-playbook playbook.yml -e "nginx_worker_connections=2048"``-e` имеет наивысший приоритет из всех источников). В своей роли `nginx_deploy` я именно поэтому вынес `nginx_worker_connections` в `defaults`, а не в `vars` — чтобы вызывающий плейбук/inventory мог его свободно переопределить без правки самой роли.
### Могут ли роль и плейбук существовать по отдельности, и что будет, если убрать один из них?
Плейбук без роли — вполне рабочий сценарий: можно писать задачи прямо в плейбуке (`tasks:` напрямую), просто без переиспользуемой структуры роли — так делают для одноразовых/простых сценариев. Роль без плейбука сама по себе не выполнится — роль не имеет собственного понятия «на каких хостах запускаться», это просто набор задач/шаблонов/дефолтов; ей обязательно нужен плейбук (или `ansible-console`/adhoc-вызов через `include_role` из другого плейбука), который укажет `hosts:` и подключит роль. То есть роль — переиспользуемый строительный блок, плейбук — то, что решает, где и когда этот блок применить.
### Разбор команды: `ansible -m shell -i inventory.yml {{ansible_hostname}}` — что делает эта команда с ключами поэтапно?
По шагам: `ansible` — adhoc-режим (разовая команда без плейбука, в отличие от `ansible-playbook`); `-i inventory.yml` — указывает, какой inventory-файл использовать для определения хостов и их переменных; `-m shell` — указывает модуль `shell` (выполнить произвольную команду через шелл на целевом хосте, в отличие от специализированных идемпотентных модулей — см. вопрос про command/shell выше); `{{ ansible_hostname }}` в этом месте выглядит некорректно синтаксически как есть — Jinja2-конструкции `{{ }}` предназначены для использования внутри YAML/шаблонов и аргументов модулей, а не как позиционный аргумент-паттерн хостов в CLI-вызове `ansible` (туда обычно подставляется паттерн хостов из inventory, например `all` или имя группы). На такой вопрос честный ответ — указать на это несоответствие и уточнить у интервьюера, что именно должно было стоять на этом месте, а не пытаться выдумать интерпретацию.