Группы хостов и их вложенность
Урок 5 - учимся группировать серверы, чтобы управлять ими пачками.
«Настроить nginx на всех веб-серверах одной командой - вот ради чего нужны группы».
Когда серверов много, перечислять их по одному неудобно. Ansible позволяет объединять хосты в группы - обычно по роли: веб-серверы, базы данных, балансировщики. Дальше ты можешь адресовать задачу не отдельному хосту, а целой группе.
Группы в INI
[web]
web1.example.com
web2.example.com
[db]
db1.example.com
db2.example.com
[loadbalancers]
lb1.example.com
Теперь ansible web -m ping проверит только веб-серверы, а ansible db -m ping - только базы. Один хост может входить в несколько групп одновременно.
Группы групп
Группы можно вкладывать друг в друга через :children. Например, объединить все продакшен-серверы:
[production:children]
web
db
loadbalancers
Теперь ansible production охватит все три группы разом. Это очень удобно для разделения окружений: production, staging, dev.
all
|
+----------+----------+
| | |
web db loadbalancers
/ \ / \ |
web1 web2 db1 db2 lb1
production:children = web + db + loadbalancers
Как работает под капотом
Ansible строит граф групп. Хост наследует принадлежность ко всем родительским группам. Когда ты адресуешь production, Ansible раскрывает дерево вниз до конкретных хостов и убирает дубликаты (один хост в двух группах попадёт в выборку один раз). Группа all - корень дерева.
Смоделируем раскрытие групп в плоский список хостов.
# Раскрытие вложенных групп в конкретные хосты
groups = {
"web": ["web1", "web2"],
"db": ["db1", "db2"],
"loadbalancers": ["lb1"],
# группа групп: ссылается на другие группы
"production": ["web", "db", "loadbalancers"],
}
def resolve(group, groups, seen=None):
seen = seen or set()
hosts = set()
for member in groups.get(group, []):
if member in groups: # это подгруппа
hosts |= resolve(member, groups, seen)
else: # это конкретный хост
hosts.add(member)
return hosts
print("web ->", sorted(resolve("web", groups)))
print("production ->", sorted(resolve("production", groups)))
Попробуй сам ▶ Видишь: production рекурсивно раскрылась в пять конкретных хостов без дублей. Точно так же Ansible определяет, к каким машинам реально применить задачу.
Частые ошибки
- Забыть :children для группы групп. Без него Ansible решит, что
webиdb- это имена хостов, а не подгрупп. - Слишком дробные группы. Не плоди группу на каждый сервер. Группируй по смыслу: роль, окружение, регион.
- Циклические группы. Группа A включает B, а B включает A - так делать нельзя.
Best practices
- Группируй по роли (
web,db) и по окружению (production,staging) - это два независимых среза. - Используй группы групп для окружений: меньше дублирования.
- Имена групп - в нижнем регистре, без пробелов, осмысленные.
В реальной работе
Хороший приём - проектировать сразу два независимых среза группировки. Первый по роли: web, db, cache - он определяет, что ставить на сервер. Второй по окружению: production, staging - он определяет, с какими параметрами. Один и тот же хост попадает в обе плоскости (например, в web и в production), а переменные из соответствующих group_vars складываются. Так роль nginx ставится на все веб-серверы одинаково, но порт и домен берутся из переменных окружения. Эта матрица «роль × окружение» - типовая архитектура инвентаря, которая масштабируется от трёх серверов до трёхсот без переписывания.
Итоги
Группы объединяют хосты по роли или окружению, а через :children можно строить группы групп. Ansible раскрывает дерево в плоский список хостов без дублей. Дальше - как раздавать этим группам переменные.