Initial commit: interview prep repo (resume, legend, stack cases, Kubernetes course)
This commit is contained in:
148
stack/ansible/CASE.md
Normal file
148
stack/ansible/CASE.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# Кейс: роль Ansible + тестирование в Molecule
|
||||
|
||||
Связь с легендой: [../../legend/LEGEND.md](../../legend/LEGEND.md) — раздел «АО ТНИИС», автоматизация установки/настройки серверов, роли и плейбуки, тестирование через Molecule перед CI (TeamCity).
|
||||
|
||||
## Что нужно реально сделать (домашний стенд)
|
||||
|
||||
Написать Ansible-роль, разворачивающую и конфигурирующую Nginx, и протестировать её через Molecule (Docker-драйвер) — без реальных серверов, но с реальным прогоном роли и проверкой результата.
|
||||
|
||||
### 1. Структура
|
||||
|
||||
```
|
||||
ansible-lab/
|
||||
├── ansible.cfg
|
||||
├── inventory/
|
||||
│ └── hosts.ini
|
||||
├── playbook.yml
|
||||
└── roles/
|
||||
└── nginx_deploy/
|
||||
├── defaults/main.yml
|
||||
├── tasks/main.yml
|
||||
├── handlers/main.yml
|
||||
├── templates/nginx.conf.j2
|
||||
└── molecule/
|
||||
└── default/
|
||||
├── molecule.yml
|
||||
├── converge.yml
|
||||
└── verify.yml
|
||||
```
|
||||
|
||||
### 2. roles/nginx_deploy/defaults/main.yml
|
||||
|
||||
```yaml
|
||||
nginx_worker_connections: 1024
|
||||
nginx_server_name: lab.local
|
||||
```
|
||||
|
||||
### 3. roles/nginx_deploy/tasks/main.yml
|
||||
|
||||
```yaml
|
||||
- name: Установить nginx
|
||||
ansible.builtin.package:
|
||||
name: nginx
|
||||
state: present
|
||||
|
||||
- name: Развернуть конфиг nginx из шаблона
|
||||
ansible.builtin.template:
|
||||
src: nginx.conf.j2
|
||||
dest: /etc/nginx/nginx.conf
|
||||
mode: "0644"
|
||||
notify: reload nginx
|
||||
|
||||
- name: Убедиться, что nginx запущен и в автозагрузке
|
||||
ansible.builtin.service:
|
||||
name: nginx
|
||||
state: started
|
||||
enabled: true
|
||||
```
|
||||
|
||||
### 4. roles/nginx_deploy/handlers/main.yml
|
||||
|
||||
```yaml
|
||||
- name: reload nginx
|
||||
ansible.builtin.service:
|
||||
name: nginx
|
||||
state: reloaded
|
||||
```
|
||||
|
||||
### 5. roles/nginx_deploy/templates/nginx.conf.j2
|
||||
|
||||
```nginx
|
||||
events {
|
||||
worker_connections {{ nginx_worker_connections }};
|
||||
}
|
||||
|
||||
http {
|
||||
server {
|
||||
listen 80;
|
||||
server_name {{ nginx_server_name }};
|
||||
|
||||
location / {
|
||||
return 200 'ok';
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. molecule/default/molecule.yml
|
||||
|
||||
```yaml
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: nginx-instance
|
||||
image: geerlingguy/docker-ubuntu2204-ansible:latest
|
||||
pre_build_image: true
|
||||
provisioner:
|
||||
name: ansible
|
||||
verifier:
|
||||
name: ansible
|
||||
```
|
||||
|
||||
### 7. molecule/default/converge.yml
|
||||
|
||||
```yaml
|
||||
- name: Converge
|
||||
hosts: all
|
||||
roles:
|
||||
- role: nginx_deploy
|
||||
```
|
||||
|
||||
### 8. molecule/default/verify.yml
|
||||
|
||||
```yaml
|
||||
- name: Verify
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: Проверить, что nginx слушает 80 порт
|
||||
ansible.builtin.wait_for:
|
||||
port: 80
|
||||
timeout: 5
|
||||
|
||||
- name: Проверить HTTP-ответ
|
||||
ansible.builtin.uri:
|
||||
url: http://localhost/
|
||||
status_code: 200
|
||||
```
|
||||
|
||||
### 9. Шаги воспроизведения
|
||||
|
||||
1. `pip install molecule molecule-plugins[docker] ansible`.
|
||||
2. `cd roles/nginx_deploy && molecule test` — это прогоняет полный цикл: create (поднять Docker-инстанс) → converge (применить роль) → idempotence (повторный прогон роли не должен ничего менять) → verify (проверки из verify.yml) → destroy.
|
||||
3. Специально сломать что-то в шаблоне (например, опечатку в директиве nginx) и посмотреть, как `molecule test` падает на этапе converge или verify — это и есть демонстрация того, зачем нужен этот шаг перед CI.
|
||||
4. Убедиться, что повторный прогон роли (idempotence check) не репортит изменений — если репортит, значит роль не идемпотентна, и это тоже частый вопрос на собеседовании.
|
||||
|
||||
### 10. Что это даёт в разговоре с интервьюером
|
||||
|
||||
- Реальное понимание, зачем нужен Molecule: тестировать роль в изолированном контейнере до того, как она попадёт в реальную инфраструктуру через CI.
|
||||
- Понимание идемпотентности — краеугольного принципа Ansible.
|
||||
- Практика с handlers, templates, defaults — стандартной структурой роли.
|
||||
- Понимание, как Molecule встраивается в CI-конвейер (у меня локально `molecule test`, в TeamCity — тот же шаг, но в build step с прогоном перед деплоем).
|
||||
|
||||
## Нагрузка и цифры
|
||||
|
||||
Ansible сам по себе не «нагрузочная» технология в смысле RPS/TPS — релевантная метрика тут время прогона плейбука на N хостов (параллелизм через `forks` в `ansible.cfg`). Цифры не зафиксированы отдельно — общий подход к честным цифрам масштаба (три слоя: публичные ориентиры / лабные замеры / реальные факты с работы) описан в [../../legend/CAPACITY.md](../../legend/CAPACITY.md).
|
||||
|
||||
## Как это ложится в легенду
|
||||
|
||||
В реальной работе (АО ТНИИС) — 15 скриптов/ролей Ansible с управлением конфигурациями через роли и модули, тестирование в CI/CD через Molecule и TeamCity. Домашний кейс воспроизводит тот же цикл в миниатюре на одной роли вместо полного набора инфраструктурных ролей.
|
||||
59
stack/ansible/QUESTIONS.md
Normal file
59
stack/ansible/QUESTIONS.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Вопросы: 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` или имя группы). На такой вопрос честный ответ — указать на это несоответствие и уточнить у интервьюера, что именно должно было стоять на этом месте, а не пытаться выдумать интерпретацию.
|
||||
Reference in New Issue
Block a user