Inventory, группы серверов и первые команды
Чтобы подключиться к одному серверу вручную, достаточно знать его адрес, пользователя и SSH-ключ:
ssh -i ~/.ssh/id_ed25519 root@10.22.0.15
Для десяти серверов придётся где-то хранить уже десять адресов. Кроме адресов важно помнить назначение машин: какие обслуживают сайт, где работает база данных, какие относятся к тестовому окружению, а какие к рабочему. Если записывать всё это прямо в каждую команду или playbook, адреса начнут повторяться, а любое изменение потребует исправлять много файлов.
Ansible отделяет список серверов от действий над ними. Список хранится в обычном текстовом файле, который называется inventory. По смыслу это адресная книга инфраструктуры: в ней каждому серверу дают понятное имя, указывают данные для подключения и объединяют машины в группы.
Inventory не устанавливается как отдельная программа и не является базой данных Ansible. В простом варианте это файл, который можно открыть через cat, изменить в редакторе и хранить в Git.
В лабораторных все учебные файлы хранятся в отдельном каталоге /root/ansible, чтобы playbook, шаблоны и переменные не смешивались с системными файлами управляющей VM. Перед началом лабораторной платформа создаёт этот каталог и помещает в него готовый inventory:
cat /root/ansible/inventory.ini
Пример его содержимого:
[targets]
target1 ansible_host=10.22.0.15 ansible_user=root
[all:vars]
ansible_ssh_private_key_file=/root/.ssh/id_ed25519
ansible_python_interpreter=/usr/bin/python3
Разберём этот небольшой файл по частям.
Понятное имя и реальный адрес
Строка с сервером выглядит так:
target1 ansible_host=10.22.0.15 ansible_user=root
target1 - имя, под которым сервер известен внутри Ansible. Его ещё называют inventory hostname. Это не обязательно настоящее имя Linux-машины и не обязательно DNS-адрес. Это удобное обозначение, которое придумала команда.
ansible_host=10.22.0.15 указывает реальный сетевой адрес для SSH-подключения. Благодаря такому разделению в командах и playbook можно использовать стабильное имя target1. Если IP-адрес изменится, достаточно исправить inventory, а логика настройки останется прежней.
ansible_user=root сообщает, под каким пользователем подключаться. В рабочей инфраструктуре здесь чаще будет отдельный пользователь, например ansible или deploy, а повышенные права будут включаться только для нужных задач.
Можно не задавать ansible_host, если имя уже разрешается через DNS или /etc/hosts:
[web]
web01.example.internal
web02.example.internal
Но учебные target VM создаются динамически и получают временные IP-адреса, поэтому платформа явно записывает их в ansible_host.
Общие параметры подключения
Следующий раздел применяется ко всем серверам inventory:
[all:vars]
ansible_ssh_private_key_file=/root/.ssh/id_ed25519
ansible_python_interpreter=/usr/bin/python3
ansible_ssh_private_key_file задаёт SSH-ключ. Вместо повторения пути в каждой строке он записан один раз для всех машин.
ansible_python_interpreter указывает, какой Python запускать на target. Ansible передаёт на управляемый сервер свои модули и обычно выполняет их через Python. Явный путь полезен, когда на машине установлено несколько версий Python или автоматическое определение выбрало не тот интерпретатор.
Часто используемые параметры подключения:
ansible_host адрес сервера
ansible_port SSH-порт, если используется не 22
ansible_user SSH-пользователь
ansible_ssh_private_key_file путь к закрытому ключу
ansible_python_interpreter путь к Python на target
Пароль не стоит записывать открытым текстом в inventory. Секреты хранят в Ansible Vault, в защищённых переменных CI/CD или во внешнем менеджере секретов.
Зачем нужны группы
На практике редко требуется настроить абсолютно все серверы одинаково. Веб-серверам нужен nginx, серверам базы данных - PostgreSQL, а тестовое окружение может получать изменения раньше рабочего.
Чтобы обращаться не к отдельным адресам, а к серверам одного назначения, inventory объединяет их в группы. Имя группы записывается в квадратных скобках:
[web]
web01 ansible_host=10.10.0.11
web02 ansible_host=10.10.0.12
[db]
db01 ansible_host=10.10.0.21
Теперь команда для группы web выполнится на web01 и web02:
ansible -i inventory.ini web -m ansible.builtin.ping
А имя db выберет только сервер базы данных:
ansible -i inventory.ini db -m ansible.builtin.ping
Встроенная группа all включает все серверы inventory:
ansible -i inventory.ini all -m ansible.builtin.ping
Один сервер может состоять сразу в нескольких группах. Например, web01 может относиться к группам web, production и moscow. Это не создаёт копии сервера: группы просто дают разные способы выбрать одну и ту же машину.
Группы из других групп
Допустим, в рабочее окружение входят веб-серверы и базы данных. Вместо повторного перечисления всех машин можно создать родительскую группу:
[production:children]
web
db
Слово children означает, что внутри перечислены другие группы. Команда для production выберет все серверы из web и db:
ansible -i inventory.ini production --list-hosts
Так inventory описывает не только адреса, но и устройство инфраструктуры. По его группам можно понять, какие типы серверов существуют и к какому окружению они относятся.
Переменные для группы и отдельного сервера
Кроме параметров SSH, inventory может хранить значения, которые нужны настройке. Например, общий порт веб-серверов:
[web]
web01 ansible_host=10.10.0.11
web02 ansible_host=10.10.0.12
[web:vars]
app_port=8080
Значение из [web:vars] получат все машины группы web. Если одному серверу нужно другое значение, его можно указать прямо в строке:
web02 ansible_host=10.10.0.12 app_port=8081
Для большого проекта переменные обычно выносят из inventory в отдельные каталоги group_vars и host_vars. Мы сделаем это позже. Сейчас достаточно увидеть основной принцип: inventory не только перечисляет серверы, но и может передавать Ansible данные, относящиеся к конкретной машине или группе.
Inventory в формате YAML
Inventory можно записать не только в INI, но и в YAML:
all:
children:
web:
hosts:
web01:
ansible_host: 10.10.0.11
web02:
ansible_host: 10.10.0.12
vars:
app_port: 8080
Смысл не меняется: здесь тоже есть общая группа, дочерняя группа web, два сервера и переменная группы.
INI короче и хорошо читается в небольшом статическом списке. YAML удобнее, когда появляется много вложенных групп и сложных значений. В курсе используется INI, чтобы в первых лабораторных было проще отделить устройство inventory от синтаксиса YAML в playbook.
Как убедиться, что Ansible понял файл
Ошибка в inventory может выбрать не те серверы или оставить часть машин без переменных. До выполнения изменений полезно посмотреть, как Ansible разобрал файл.
Показать структуру групп:
ansible-inventory -i /root/ansible/inventory.ini --graph
Показать всё содержимое в формате JSON:
ansible-inventory -i /root/ansible/inventory.ini --list
Показать итоговые переменные одного сервера:
ansible-inventory -i /root/ansible/inventory.ini --host target1
Последняя команда особенно полезна, когда значение может прийти из нескольких мест. Она показывает не только строку сервера, а итоговый набор данных, который Ansible назначил target1.
Как выбрать нужные серверы
В команде после inventory указывается сервер или группа, на которых нужно работать:
ansible -i inventory.ini web --list-hosts
Это правило выбора называется pattern. Самые понятные варианты:
ansible -i inventory.ini all --list-hosts
ansible -i inventory.ini web --list-hosts
ansible -i inventory.ini web01 --list-hosts
Можно объединять и исключать группы:
ansible -i inventory.ini 'web:&production' --list-hosts
ansible -i inventory.ini 'all:!db' --list-hosts
web:&production выбирает серверы, которые одновременно входят в web и production. all:!db выбирает все машины, кроме группы db.
Сложный pattern лучше сначала запускать с --list-hosts. Эта команда ничего не меняет и позволяет увидеть точный список до настоящего запуска.
Первая ad-hoc команда
Когда inventory готов, можно выполнить одно действие без создания отдельного файла с задачами. Такой разовый запуск называется ad-hoc командой:
ansible -i /root/ansible/inventory.ini targets \
-m ansible.builtin.command \
-a 'hostname'
Разберём параметры:
-i /root/ansible/inventory.iniуказывает файл со списком серверов;targetsвыбирает группу;-mзадаёт модуль, то есть готовую операцию Ansible;-aпередаёт этой операции параметры.
В данном случае модуль command запускает на target программу hostname. Если в группе две машины, Ansible выполнит команду на обеих и покажет два отдельных результата.
Почему Ansible ping не равен обычному ping
Для первой проверки часто используют:
ansible -i /root/ansible/inventory.ini targets -m ansible.builtin.ping
Обычная системная команда ping отправляет сетевые ICMP-пакеты и проверяет, отвечает ли адрес. Модуль Ansible ping делает больше:
- Берёт параметры сервера из inventory.
- Подключается по SSH.
- Запускает небольшой Python-модуль.
- Возвращает ответ
pong.
Поэтому успешный ansible.builtin.ping подтверждает не только доступность адреса, но и базовую готовность сервера к управлению через Ansible.
Если проверка не прошла, причина обычно указана в результате: неправильный адрес, ошибка SSH-ключа, недоступный порт, неизвестный пользователь или отсутствующий Python.
command и shell
Модуль command запускает программу напрямую:
ansible -i inventory.ini targets \
-m ansible.builtin.command \
-a 'cat /etc/os-release'
Он не передаёт строку через оболочку, поэтому в нём не работают конвейеры |, перенаправления >, логические операторы && и подстановки shell.
Если действительно нужны возможности оболочки, используется модуль shell:
ansible -i inventory.ini targets \
-m ansible.builtin.shell \
-a "grep '^NAME=' /etc/os-release | head -1"
shell даёт больше свободы, но Ansible хуже понимает результат такой команды. Для создания файла, пользователя, пакета или службы лучше выбрать специальный модуль. Он умеет проверить текущее состояние и не повторять изменение без необходимости.
Изменяем файл через ad-hoc команду
В ad-hoc команде можно использовать не только command и shell, но и модули, которые управляют состоянием сервера. Например, модуль copy создаст на target1 файл с заданным содержимым и правами:
ansible -i /root/ansible/inventory.ini target1 \
-m ansible.builtin.copy \
-a 'dest=/tmp/ansible-managed.txt content=managed-by-ansible mode=0644'
При первом запуске Ansible создаст файл и покажет changed. При повторном запуске с теми же параметрами содержимое и права уже будут правильными, поэтому результатом станет ok без нового изменения.
Удаление тоже описывается как требуемое состояние. Значение state=absent означает, что указанного файла или каталога быть не должно:
ansible -i /root/ansible/inventory.ini target1 \
-m ansible.builtin.file \
-a 'path=/tmp/ansible-managed.txt state=absent'
Если файл существует, модуль удалит его. Если файла уже нет, команда всё равно завершится успешно без изменения. В первой лабораторной ты сравнишь такой запуск с ручным созданием файла через SSH.
Как найти подходящий модуль
В Ansible много готовых модулей. Их документацию можно открыть прямо в терминале:
ansible-doc ansible.builtin.file
ansible-doc ansible.builtin.copy
ansible-doc -l
Имя ansible.builtin.file состоит из трёх частей и называется Fully Qualified Collection Name, или FQCN. Оно показывает, что модуль file находится во встроенной коллекции ansible.builtin.
Короткая запись file во многих случаях тоже работает. Полное имя удобнее в проекте: читатель сразу понимает источник модуля, а одинаковые короткие имена из разных коллекций не конфликтуют.
В документации модуля есть описание параметров и примеры. Перед использованием нового модуля полезно проверить:
- какие параметры обязательны;
- какое состояние он создаёт;
- поддерживает ли режим проверки;
- что возвращает в результате;
- какие требования есть на target.
Статический и динамический inventory
Файл, который человек поддерживает вручную, называется статическим inventory. Он подходит, когда список серверов меняется редко или создаётся самой учебной платформой.
В облаке виртуальные машины могут появляться и удаляться автоматически. Ручной файл быстро устареет: Ansible попытается подключиться к уже удалённому адресу или не увидит новый сервер. В таком случае список получают через API облака, систему учёта инфраструктуры или специальный плагин. Это называется динамическим inventory.
Для Ansible результат остаётся тем же: набор серверов, групп и переменных. Отличается только источник данных. К работе с большими и часто меняющимися инфраструктурами мы вернёмся ближе к концу курса.
Что будет в первой лабораторной
Платформа уже подготовила control VM, target VM, SSH-ключ и /root/ansible/inventory.ini. Ты:
- Посмотришь реальный inventory.
- Найдёшь в нём адрес target.
- Подключишься к этой машине вручную.
- Проверишь подключение модулем Ansible
ping. - Выполнишь команду и изменишь файл на target.
После лабораторной inventory перестанет быть абстрактным термином: это будет знакомый файл, который связывает понятное имя target1 с отдельной виртуальной машиной.
