
Лень — двигатель прогресса. Именно по этой причине Ansible — лучший друг любого админа, который не хочет руками применять настройки к 1000 серверов. Я использую его на регулярной основе, но при этом именно тема инвентарных файлов каждый раз умудряется меня удивить. Поэтому в этой статье решил собрать всю общую информацию, начиная с inventory.ini и заканчивая плагинами для динамических инвентарей.
Инвентарь — это не просто список серверов. Это карта вашей инфраструктуры, в которой отражено, где что находится, как ко всему подключаться и какие настройки применять. Правильно организованный инвентарь может сэкономить кучу времени, а неправильный — превратить деплой в бесконечную отладку.
На чём держится инвентарь?
Давайте разберёмся с базовыми концепциями. Ansible использует push-модель: control node (машина, на которой мы запускаем Playbook) подключается к managed nodes (целевые хосты) и выполняет на них задачи. Инвентарь — это дорожная карта, которая отражает куда, как и когда можно идти.
По умолчанию Ansible ищет инвентарь в /etc/ansible/hosts, но мы можем указать альтернативный источник через флаг -i:
# Использовать конкретный файл инвентаря
ansible-playbook -i inventory.yml site.yml
# Использовать директорию с несколькими инвентарями
ansible-playbook -i inventory/ site.yml
# Комбинировать несколько источников
ansible-playbook -i static-hosts.yml -i aws_ec2.yml site.yml
Инвентарь решает три ключевые задачи:
Определяет цели — какие хосты существуют и доступны для автоматизации.
Организует структуру — как хосты логически группируются.
Хранит конфигурацию — переменные для подключения и настройки каждого хоста.
Статические инвентари: INI vs YAML
Статические инвентари — это обычные файлы, содержащие списки целевых хостов. Ansible поддерживает два основных формата:
INI,
YAML.
INI формат
INI-формат прост и понятен для небольших инвентарей:
# Хосты без группы (ungrouped)
noname.example.com ansible_host=192.0.2.50 ansible_port=5555
# Группа веб-серверов
[webservers]
web1.example.com
web2.example.com ansible_host=10.0.1.11
web[3:5].example.com # Диапазон: web3, web4, web5
# Группа баз данных с переменными
[dbservers]
db1.example.com http_port=80 maxconn=808
db2.example.com http_port=8080 maxconn=909
# Переменные для всей группы
[webservers:vars]
ansible_user=deploy
nginx_worker_processes=4
# Группа групп (children)
[production:children]
webservers
dbservers
[production:vars]
env=production
Важная особенность INI: значения, объявленные рядом с хостом (например, http_port=80), интерпретируются как переменные в Python. То есть http_port=80 станет числом 80, а debug=True — булевым True. Но при этом в секции :vars всё интерпретируется как строки, поэтому debug=True станет строкой "True".
YAML формат
YAML знакомый нам всем имеет древовидную структуру, которая более читаемая и гибкая.
---
all:
children:
webservers:
hosts:
web1.example.com:
web2.example.com:
ansible_host: 10.0.1.11
web3.example.com:
web4.example.com:
web5.example.com:
vars:
ansible_user: deploy
nginx_worker_processes: 4
dbservers:
hosts:
db1.example.com:
http_port: 80
maxconn: 808
db2.example.com:
http_port: 8080
maxconn: 909
production:
children:
webservers:
dbservers:
vars:
env: production
Преимущество YAML: Типы данных обрабатываются предсказуемо, нет путаницы между обычными и :vars значениями. Настоятельно рекомендую использовать YAML для всех новых проектов.
Диапазоны и паттерны
Ansible поддерживает удобные паттерны для определения множества хостов:
all:
children:
webservers:
hosts:
# Числовые диапазоны
web[01:10].example.com: # web01, web02, ..., web10
# Числовые диапазоны с шагом
web[11:20:3].example.com: # web11, web14, web17, web20
# Алфавитные диапазоны
web[a:f].example.com: # weba, webb, webc, webd, webe, webf
# IPv4 диапазоны
192.168.1.[1:5]:
Проверим, что получилось:
# Показать все хосты в инвентаре
ansible-inventory -i inventory.yml --list
{
"_meta": {
"hostvars": {},
"profile": "inventory_legacy"
},
"all": {
"children": [
"ungrouped",
"webservers"
]
},
"webservers": {
"hosts": [
"web01.example.com",
"web02.example.com",
"web03.example.com",
"web04.example.com",
"web05.example.com",
"web06.example.com",
"web07.example.com",
"web08.example.com",
"web09.example.com",
"web10.example.com",
"web11.example.com",
"web14.example.com",
"web17.example.com",
"web20.example.com",
"weba.example.com",
"webb.example.com",
"webc.example.com",
"webd.example.com",
"webe.example.com",
"webf.example.com",
"192.168.1.1",
"192.168.1.2",
"192.168.1.3",
"192.168.1.4",
"192.168.1.5"
]
}
}
# Графическое представление групп
ansible-inventory -i inventory.yml --graph
@all:
|--@ungrouped:
|--@webservers:
| |--web01.example.com
| |--web02.example.com
| |--web03.example.com
| |--web04.example.com
| |--web05.example.com
| |--web06.example.com
| |--web07.example.com
| |--web08.example.com
| |--web09.example.com
| |--web10.example.com
| |--web11.example.com
| |--web14.example.com
| |--web17.example.com
| |--web20.example.com
| |--weba.example.com
| |--webb.example.com
| |--webc.example.com
| |--webd.example.com
| |--webe.example.com
| |--webf.example.com
| |--192.168.1.1
| |--192.168.1.2
| |--192.168.1.3
| |--192.168.1.4
| |--192.168.1.5
Уже выглядит неплохо, но если мы хотим решать серьёзные задачи, то нужны и механизмы посерьёзнее.
Группы и иерархия
Ansible даёт нам возможность организации хостов через группы и подгруппы.
Специальные группы
Ansible имеет две встроенные группы:
all— включает абсолютно все хосты в инвентаре,ungrouped— хосты, которые не принадлежат ни к одной группе (кромеall).
Вложенные группы (children)
all:
children:
# Географические группы
usa:
children:
southeast:
children:
atlanta:
hosts:
host1.example.com:
host2.example.com:
raleigh:
hosts:
host3.example.com:
vars:
timezone: "America/New_York"
regional_server: foo.southeast.example.com
west:
children:
california:
hosts:
host4.example.com:
vars:
timezone: "America/Los_Angeles"
Правило наследования: переменные дочерних групп имеют более высокий приоритет и переопределяют переменные родительских групп.
Паттерны селекторов хостов
Когда мы запускаем playbook, можно использовать хосты очень гибко:
# Одна группа
ansible-playbook -i inventory.yml site.yml --limit webservers
# Несколько групп (ИЛИ)
ansible-playbook site.yml --limit "webservers,dbservers"
# Пересечение групп (И)
ansible-playbook site.yml --limit "webservers:&production"
# Исключение (НЕ)
ansible-playbook site.yml --limit "production:!dbservers"
# Комбинированные паттерны
ansible-playbook site.yml --limit "webservers:&production:!web1"
# Диапазоны внутри группы
ansible-playbook site.yml --limit "webservers[0:5]"
# По регулярным выражениям
ansible-playbook site.yml --limit "~web[0-9]+"
Наша дорожная карта уже включает в себя множество объектов, но им явно не хватает описания, которое отлично реализуется через переменные.
Переменные: иерархия и приоритет
Переменные в Ansible — это очень мощный инструмент, но требующий внимательности. Потому что очень тяжело с первого раза догадаться, откуда именно мы получили такое значение, и на каком уровне она была переопределена в последний раз.
Определение переменных в инвентаре
all:
children:
webservers:
hosts:
web1:
# Переменные на уровне хоста
ansible_host: 192.168.1.10
http_port: 8080
max_connections: 1000
web2:
ansible_host: 192.168.1.11
http_port: 8081
vars:
# Переменные на уровне группы
ansible_user: deploy
ansible_connection: ssh
Ключевое правило: Host variables переопределяют group variables. В целом это правило легко запомнить, так как везде общие переменные имеют более низкий приоритет.
group_vars и host_vars
Для больших проектов хранить переменные прямо в инвентаре неудобно. Ansible поддерживает внешние файлы переменных:
ansible_project/
├── inventory/
│ └── production
├── group_vars/
│ ├── all.yml # Переменные для всех хостов
│ ├── webservers.yml # Переменные для группы webservers
│ └── dbservers.yml # Переменные для группы dbservers
└── host_vars/
├── web1.yml # Переменные для конкретного хоста web1
└── db1.yml # Переменные для хоста db1
Ansible ищет group_vars/ и host_vars/ в двух местах:
Относительно inventory файла.
Относительно playbook.
Важно: если переменные определены в обоих местах, то выше приоритет у тех, что определены на уровне playbook.
Давайте посмотрим на пример:
# group_vars/webservers.yml
---
nginx_port: 80
ssl_enabled: true
application_config:
database_host: "db.example.com"
database_port: 5432
Продвинутая организация с поддиректориями
Для сложной архитектуры можно разбить переменные по файлам:
group_vars/
├── all/
│ ├── 00_main.yml # Загружается первым
│ ├── 10_network.yml
│ ├── 20_security.yml
│ └── 99_vault.yml # Загружается последним
└── webservers/
├── nginx.yml
├── app.yml
└── vault.yml
Ansible загружает файлы в лексикографическом порядке. Если в файлах есть одинаковые переменные, последний загруженный файл выигрывает, поэтому префиксы 00_, 10_, 20_ полезны для контроля порядка загрузки.
Приоритет переменных: полная картина
Теперь самое интересное. Ansible имеет 22 уровня приоритета переменных. От низшего к высшему:
Приоритет |
Источник |
Область применения |
|---|---|---|
1 |
command line values (например, |
CLI опции (НЕ переменные) |
2 |
role defaults |
role/defaults/main.yml |
3 |
inventory file group vars |
Переменные групп в инвентаре |
4 |
inventory group_vars/all |
group_vars/all относительно inventory |
5 |
playbook group_vars/all |
group_vars/all относительно playbook |
6 |
inventory group_vars/* |
group_vars групп относительно inventory |
7 |
playbook group_vars/* |
group_vars групп относительно playbook |
8 |
inventory file host vars |
Переменные хостов в инвентаре |
9 |
inventory host_vars/* |
host_vars относительно inventory |
10 |
playbook host_vars/* |
host_vars относительно playbook |
11 |
host facts / cached set_facts |
Собранные факты о системе |
12 |
play vars |
Переменные в play |
13 |
play vars_prompt |
Интерактивный ввод |
14 |
play vars_files |
Файлы переменных |
15 |
role vars |
role/vars/main.yml |
16 |
block vars |
Переменные блока |
17 |
task vars |
Переменные задачи |
18 |
include_vars |
Динамически загруженные |
19 |
set_facts / registered vars |
Установленные в runtime |
20 |
role (and include_role) params |
Параметры роли |
21 |
include params |
Параметры include |
22 |
extra vars ( |
Абсолютный чемпион |
Очень важно помнить, что -e — это последняя точка правды, эта информация очень сильно может Вас выручить при отладке роли.
Приоритет групп одного уровня
Если хост принадлежит нескольким группам одного уровня, Ansible загружает их в алфавитном порядке. Последняя загруженная группа побеждает:
a_group:
vars:
test_var: a
b_group:
vars:
test_var: b
Результат: testvar == b
Но также можно управлять приоритетом через ansible_group_priority:
a_group:
vars:
testvar: a
ansible_group_priority: 10
b_group:
vars:
testvar: b
Теперь testvar == a
Важно: ansible_group_priority можно устанавливать только в inventory source, в group_vars/ политика не работает.
Отладка переменных
Ряд полезных для дебага команд:
# Показать все переменные для конкретного хоста
ansible-inventory -i inventory.yml --host web1
# Вывод в YAML формате (читабельнее)
ansible-inventory -i inventory.yml --host web1 --yaml
# Показать переменную через ad-hoc команду
ansible -i inventory.yml web1 -m debug -a "var=http_port"
# Показать все переменные хоста
ansible -i inventory.yml web1 -m debug -a "var=hostvars[inventory_hostname]"
Или в playbook:
- hosts: webservers
tasks:
- name: Debug конкретной переменной
debug:
msg: "HTTP port is {{ http_port | default('not set') }}"
- name: Debug всех переменных хоста
debug:
var: hostvars[inventory_hostname]
verbosity: 2
Динамические инвентари — выход для самых ленивых
Если Вам уже и инвентарь самому писать лень, то можно сложить эту обязанность на плагин. Ну а если быть серьёзным, то в реалиях постоянно меняющейся в облаке инфраструктуры это просто лучший вариант достучаться до всех интересующих Вас узлов.
Inventory Plugins
Inventory plugins — это современный и рекомендуемый способ работы с динамическими инвентарями. Преимущества:
Стандартизированный формат конфигурации (YAML).
Встроенное кэширование.
Фильтрация и группировка из коробки.
Поддержка от облачных провайдеров.
Не нужно писать код.
AWS EC2 Plugin
Самый популярный плагин для работы с AWS. Сначала установим коллекцию:
ansible-galaxy collection install amazon.aws
pip3 install boto3 botocore
Создаём файл конфигурации aws_ec2.yml (имя ОБЯЗАТЕЛЬНО должно заканчиваться на aws_ec2.yml или aws_ec2.yaml):
---
plugin: amazon.aws.aws_ec2
# Регионы для сканирования
regions:
- us-east-1
- eu-west-1
# Фильтрация инстансов
filters:
# Только running инстансы
instance-state-name: running
# По тегам
tag:Environment:
- production
- staging
# Группировка по тегам
keyed_groups:
# Группа по тегу Name: tag_Name_webserver
- key: tags.Name
prefix: tag_Name
separator: "_"
# Группа по тегу Role: role_frontend
- key: tags.Role
prefix: role
# Группа по типу инстанса: instance_type_t2_micro
- prefix: instance_type
key: instance_type
# Группа по региону: aws_region_us_east_1
- key: placement.region
prefix: aws_region
# Группировка через условия
groups:
# Группа development для хостов с тегом env=devel
development: "'devel' in (tags.Environment|lower)"
# Группа webservers для хостов с тегом Role=web
webservers: "tags.Role == 'web'"
# Настройка hostname
hostnames:
- dns-name # Предпочитаем DNS имя
- private-ip-address # Fallback на private IP
# Создание переменных
compose:
# ansible_host будет private IP
ansible_host: private_ip_address
# Кастомные переменные
ec2_region: placement.region
ec2_az: placement.availability_zone
Если у вас Ansible control node внутри AWS с IAM ролью, плагин автоматически использует роль. Если снаружи — используем креды:
---
plugin: amazon.aws.aws_ec2
# Явное указание credentials
aws_access_key: ...
aws_secret_key: ...
# Или использовать профиль
boto_profile: production
# Или assume role
iam_role_arn: arn:aws:iam::123456789:role/ansible-role
GCP Compute Plugin
Для Google Cloud похожий подход:
ansible-galaxy collection install google.cloud
pip3 install requests google-auth
Создаём service account в GCP и скачиваем JSON ключ. Затем конфигурация:
---
plugin: google.cloud.gcp_compute
# GCP проект
projects:
- my-gcp-project-id
# Аутентификация через service account
auth_kind: serviceaccount
service_account_file: /path/to/service-account.json
# Фильтрация
filters:
- status = RUNNING
- labels.environment = production
# Группировка по лейблам
keyed_groups:
- key: labels.role
prefix: role
- key: labels.environment
prefix: env
- key: zone
prefix: zone
# Hostname
hostnames:
- name
compose:
ansible_host: networkInterfaces[0].accessConfigs[0].natIP
Azure Plugin
ansible-galaxy collection install azure.azcollection
pip3 install azure-cli
Аутентификация через Azure CLI:
az login
Конфигурация azure_rm.yml:
---
plugin: azure.azcollection.azure_rm
# Включить все подписки
include_vm_resource_groups:
- my-resource-group
# Фильтрация
exclude_host_filters:
- powerstate != 'running'
# Группировка
keyed_groups:
- prefix: tag
key: tags
- prefix: azure_loc
key: location
- prefix: azure_os
key: os_profile.system
# Hostname
hostnames:
- name
- public_ip_name
compose:
ansible_host: public_ipv4_addresses[0]
NetBox Plugin
NetBox — это отличный open source вариант для ssot Вашей инфраструктуры, поэтому грех не воспользоваться им как источником информации для инвентаря.
ansible-galaxy collection install netbox.netbox
pip3 install pynetbox
Создаём файл конфигурации netbox_inventory.yml:
---
plugin: netbox.netbox.nb_inventory
# NetBox API endpoint и токен
api_endpoint: https://netbox.example.com
token: your-netbox-api-token
# Или через переменные окружения
# export NETBOX_API=https://netbox.example.com
# export NETBOX_TOKEN=your-token
# SSL проверка
validate_certs: true
# Фильтрация устройств
device_query_filters:
# По статусу: active, offline, planned, staged, failed
- status: active
# По ролям устройств
- role:
- server
- network
# По сайтам/локациям
- site:
- datacenter-1
- datacenter-2
# По тегам
- tag:
- production
- ansible-managed
# Фильтрация виртуальных машин
virtual_machine_query_filters:
- status: active
- cluster: production-cluster
# Группировка по атрибутам NetBox
group_by:
- site
- device_role
- platform
- manufacturer
- tags
- tenant
- cluster
- device_type
# Группировка через keyed_groups
keyed_groups:
# По custom fields: cf_environment_production
- key: custom_fields.environment
prefix: cf_environment
separator: "_"
# По региону из custom field: region_eu_west
- key: custom_fields.region
prefix: region
# Комбинация site + role: dc1_webservers
- key: 'site.slug ~ "_" ~ device_role.slug'
prefix: ""
# По rack: rack_a01
- key: rack.name
prefix: rack
separator: "_"
# Группировка через условия
groups:
# Production серверы
production_servers: >-
'production' in tags and device_role.slug == 'server'
# Критические системы
critical: >-
custom_fields.criticality == 'high' or 'critical' in tags
# Физические серверы
physical: "device_role is defined"
# Виртуальные машины
virtual: "cluster is defined"
# Требуют обновления
needs_update: >-
custom_fields.os_version is defined and
custom_fields.os_version < '22.04'
# Настройка hostname
compose:
# Primary IP как ansible_host (убираем маску подсети)
ansible_host: primary_ip4.address | regex_replace('/.*', '')
# Альтернатива через интерфейсы
# ansible_host: interfaces[0].ip_addresses[0].address | regex_replace('/.*', '')
# Custom fields как переменные
ansible_user: custom_fields.ansible_user | default('ansible')
ansible_port: custom_fields.ssh_port | default(22)
# Дополнительные переменные
environment: custom_fields.environment | default('development')
os_version: custom_fields.os_version
backup_enabled: custom_fields.backup_enabled | default(true)
monitoring_enabled: custom_fields.monitoring | default(true)
# Site-specific настройки
ntp_server: '"ntp." ~ site.slug ~ ".company.com"'
# NetBox ID для обратной интеграции
netbox_device_id: id
Композиция источников
Ansible инструмент для ленивых, но свободных, поэтому предлагает комбинировать разные типы инвентарей:
# Комбинировать несколько источников
ansible-playbook -i static-hosts.yml -i aws_ec2.yml -i azure_rm.yml deploy.yml
Или создать директорию инвентаря:
inventory/
├── 01-static.yml
├── 02-aws_ec2.yml
├── 03-azure_rm.yml
└── 04-gcp.yml
Как уже говорилось ранее, префиксы 01-, 02- помогают контролировать порядок, последний загруженный источник может переопределять переменные предыдущих.
ansible-playbook -i inventory/ deploy.yml
Кэширование динамических инвентарей
Динамические инвентари делают API вызовы, которые не всегда могут похвастаться скоростью и стабильностью. Для решения этой проблемы мы можем включить кэширование:
ansible.cfg:
[defaults]
inventory = ./inventory/
[inventory]
enable_plugins = amazon.aws.aws_ec2, azure.azcollection.azure_rm, google.cloud.gcp_compute
cache = True
cache_plugin = jsonfile
cache_connection = /tmp/ansible_inventory_cache
cache_timeout = 3600
Или в конфигурации плагина:
---
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
# Кэширование
cache: true
cache_timeout: 3600
cache_connection: /tmp/aws_inventory_cache
Отладка и troubleshooting
Даже с правильно организованным инвентарём иногда что-то идёт не так. Вот мой золотой набор команд для отладки:
# Показать весь инвентарь в JSON
ansible-inventory -i inventory.yml --list
# Показать граф групп
ansible-inventory -i inventory.yml --graph
# Показать граф с переменными
ansible-inventory -i inventory.yml --graph --vars
# Показать переменные конкретного хоста
ansible-inventory -i inventory.yml --host web1
# Вывод в YAML (читабельнее)
ansible-inventory -i inventory.yml --list --yaml
# Проверка connectivity
ansible -i inventory.yml all -m ping
# Проверка конкретной группы
ansible -i inventory.yml webservers -m ping
# Ad-hoc команда для проверки переменной
ansible -i inventory.yml web1 -m debug -a "var=http_port"
# Собрать facts о хосте
ansible -i inventory.yml web1 -m setup
Некоторые из них уже встречались ранее, но это скорее как общая шпаргалка
Оптимизация производительности
Для больших инвентарей производительность становится важным аспектом, особенно, когда роль запускается по расписанию. Все настройки, указанные здесь, применяются в ansible.cfg
SSH оптимизация
[ssh_connection]
# ControlMaster для переиспользования SSH соединений
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
control_path = /tmp/ansible-ssh-%%h-%%p-%%r
# SSH pipelining (меньше SSH соединений)
pipelining = True
Параллелизм
[defaults]
# Количество параллельных процессов (default: 5)
forks = 50
Умный сбор фактов
[defaults]
# Собирать факты, только когда нужно
gathering = smart
# Кэширование фактов
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400
В playbook можно отключить или ограничить факты:
- hosts: all
gather_facts: false # Не собирать совсем
tasks:
- setup:
gather_subset:
- '!all'
- '!any'
- min # Только минимальный набор
Стратегии выполнения
- hosts: webservers
# Linear: выполнить task на всех хостах, перейти к следующей
strategy: linear
- hosts: appservers
# Free: хосты выполняют задачи независимо
strategy: free
- hosts: dbservers
# Батчами: по 5 хостов одновременно
serial: 5
- hosts: production
# Батчами: 20% хостов за раз
serial: "20%"
# Прервать, если >25% хостов упали
max_fail_percentage: 25
Best Practices: чек-лист
✅ Организация
Используйте YAML формат.
Структурируйте по принципу What-Where-When (webservers/atlanta/production).
Разделяйте окружения в отдельные директории для крупных проектов (
inventories/dev/,inventories/prod/).Используйте
group_vars/иhost_vars/вместо переменных в инвентаре.Документируйте структуру.
✅ Переменные
Host vars переопределяют group vars (общего всегда менее важно).
Playbook vars переопределяют inventory vars.
Extra vars (
-e) переопределяют всё.Используйте
{{ var | default('value') }}для optional переменных.Документируйте переменные в
defaults/main.ymlролей.
✅ Динамические инвентари
Включите кэширование для ускорения.
Префиксуйте файлы цифрами для контроля порядка загрузки.
Комбинируйте статические и динамические источники.
❌ Чего избегать
Не смешивайте переменные разных окружений.
Не храните всё в одном огромном файле.
Не используйте одинаковые имена для разных концепций.
Не игнорируйте warnings от ansible-inventory.
© 2025 ООО «МТ ФИНАНС»