Ответ
В наших проектах мы используем ролевую структуру, которая обеспечивает переиспользование и четкое разделение ответственности. Вот типичная структура для развертывания стека (например, Nginx + PHP-FPM):
ansible-infrastructure/
├── ansible.cfg # Локальные настройки Ansible (inventory, roles_path)
├── inventory/ # Динамический инвентарь
│ ├── production/ # Группы для prod
│ │ ├── group_vars/
│ │ │ └── webservers.yml # Переменные для всех веб-серверов
│ │ └── host_vars/
│ │ └── web01.yml # Специфичные переменные для хоста
│ └── staging/
│ └── ...
├── playbooks/ # Плейбуки верхнего уровня
│ ├── site.yml # Главный плейбук, включает остальные
│ ├── webservers.yml # Плейбук для настройки веб-серверов
│ └── database.yml
├── roles/ # Сердце проекта - роли
│ ├── common/ # Базовая настройка всех хостов (users, ssh, updates)
│ │ ├── tasks/main.yml
│ │ ├── handlers/main.yml
│ │ └── templates/
│ ├── nginx/
│ │ ├── tasks/main.yml # Установка, конфигурация, сервис
│ │ ├── handlers/main.yml # handler: restart nginx
│ │ ├── templates/
│ │ │ └── nginx.conf.j2 # Jinja2 шаблон конфига
│ │ ├── defaults/main.yml # Дефолтные переменные (nginx_version: 1.22)
│ │ └── vars/main.yml # Переменные с более высоким приоритетом
│ └── php-fpm/
│ └── ...
├── collections/requirements.yml # Зависимости - внешние коллекции
└── requirements.yml # Зависимости - роли из Ansible Galaxy
Ключевые принципы, которые мы используем:
- Роли инкапсулируют логику настройки одного компонента. Это позволяет легко тестировать
role/nginxотдельно. defaults/main.yml— для переменных, которые можно и нужно переопределять.vars/main.yml— для переменных, которые не должны меняться.- Динамический инвентарь: Часто пишем скрипт на Python (
inventory/aws_ec2.py), который получает список хостов из AWS EC2 API, что исключает ручное обновление списка IP-адресов. - Использование Collections: Для работы с облачными провайдерами (например,
amazon.aws) используем collections, которые устанавливаются черезansible-galaxy collection install -r collections/requirements.yml.