Ansible: идемпотентность на практике
Плейбук, который можно запускать сколько угодно раз, — и почему module beats shell.
Минимум устройства
Ansible приходит на машины по SSH — агентов ставить не нужно. Два файла запускают всё:
Inventory — список машин:
[web]
web1.example.com
web2.example.com
Playbook — желаемое состояние:
- hosts: web
become: true
tasks:
- name: nginx установлен
apt:
name: nginx
state: present
- name: конфиг сайта на месте
template:
src: site.conf.j2
dest: /etc/nginx/conf.d/site.conf
notify: reload nginx
- name: nginx запущен и в автозагрузке
service:
name: nginx
state: started
enabled: true
handlers:
- name: reload nginx
service:
name: nginx
state: reloaded
Идемпотентность — в модулях
Задачи описаны не глаголами («установи»), а состояниями (state: present). Модуль сам проверяет текущее состояние: nginx уже стоит — задача вернёт ok и ничего не тронет; нет — поставит и вернёт changed. Поэтому плейбук можно запускать после каждого изменения и по расписанию — второй запуск по уже настроенной машине пройдёт весь в ok.
Это же ломается, как только вместо модуля берут shell:
# плохо: не идемпотентно, каждый запуск — changed
- shell: echo "net.core.somaxconn=1024" >> /etc/sysctl.conf
# хорошо: строка либо есть, либо будет добавлена один раз
- sysctl:
name: net.core.somaxconn
value: "1024"
shell — последнее средство, когда модуля действительно нет. Если он всё же нужен — делайте его идемпотентным сами через creates: или changed_when.
Handlers: перезапуск только при изменении
notify срабатывает только если задача вернула changed. Конфиг не менялся — nginx не перезагружается. Это и есть причина, по которой плейбук безопасно гонять хоть каждый час.
Сухой прогон
Перед применением к проду:
ansible-playbook site.yml --check --diff
--check ничего не меняет, --diff показывает, что изменилось бы. Привычка смотреть diff до запуска ловит бо́льшую часть сюрпризов.