Материал открыт для чтения

Войдите, чтобы сохранять прогресс и результаты.

Войти
Ansible: автоматизация серверов с нуля

Inventory, группы серверов и первые команды

чтение ~14 минУрок 2 из 21

Inventory, группы серверов и первые команды

module ansible basics Image02

Чтобы подключиться к одному серверу вручную, достаточно знать его адрес, пользователя и 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 делает больше:

  1. Берёт параметры сервера из inventory.
  2. Подключается по SSH.
  3. Запускает небольшой Python-модуль.
  4. Возвращает ответ 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. Ты:

  1. Посмотришь реальный inventory.
  2. Найдёшь в нём адрес target.
  3. Подключишься к этой машине вручную.
  4. Проверишь подключение модулем Ansible ping.
  5. Выполнишь команду и изменишь файл на target.

После лабораторной inventory перестанет быть абстрактным термином: это будет знакомый файл, который связывает понятное имя target1 с отдельной виртуальной машиной.

Материал изучен?

Войдите, чтобы сохранить прогресс и перейти к практической лаборатории.

Войти и продолжить