Структурирование сложных плейбуков Ansible на RHEL

AnsibleBeginner
Практиковаться сейчас

Введение

В этой лабораторной работе вы изучите основные методы структурирования сложных плейбуков Ansible на RHEL для создания более управляемой, масштабируемой и повторно используемой автоматизации. Вы перейдете от фундаментальных концепций к продвинутым стратегиям организации, сосредоточившись на том, как точно контролировать, на каких хостах будет выполняться ваша автоматизация, и как разбивать большие плейбуки на логические, модульные компоненты.

Вы начнете с освоения выбора хостов, используя базовые имена групп, подстановочные знаки, исключения и логические операторы для нацеливания на конкретные узлы в вашем инвентаре. Далее вы изучите модульность, перерабатывая задачи в отдельные файлы с помощью include_tasks и import_tasks. Наконец, вы научитесь составлять полный рабочий процесс из нескольких плейбуков с помощью import_playbook, кульминацией которого станет выполнение и проверка вашего полностью структурированного и модульного проекта Ansible.

Выбор хостов с использованием базовых шаблонов и шаблонов с подстановочными знаками

На этом этапе вы изучите основы нацеливания на конкретные хосты в вашей автоматизации Ansible. Основой этого является файл инвентаря Ansible, который перечисляет серверы, которыми вы управляете, и директива hosts в плейбуке, которая указывает, на каких из этих серверов должны выполняться задачи. Мы начнем с установки Ansible, создания базового инвентаря и плейбука, а затем рассмотрим, как выбирать хосты с использованием имен групп и шаблонов с подстановочными знаками.

Сначала убедитесь, что Ansible установлен в вашей среде. Ansible не установлен по умолчанию, поэтому вам нужно установить его с помощью менеджера пакетов DNF. Пакет ansible-core предоставляет основные инструменты командной строки Ansible, включая ansible-playbook. Выполните следующую команду:

sudo dnf install -y ansible-core

Вы должны увидеть вывод, похожий на этот:

...
Installed:
  ansible-core-2.16.x-x.el9.x86_64
  ...
Complete!

Далее создадим выделенный каталог для этого упражнения, чтобы наши файлы были организованы. Все последующие действия на этом этапе будут выполняться в этом новом каталоге:

mkdir -p ~/project/ansible_patterns
cd ~/project/ansible_patterns

Теперь создайте файл инвентаря. Инвентарь — это текстовый файл, который определяет хосты и группы хостов, на которых работают команды, модули и задачи Ansible. Мы будем использовать формат INI из-за его простоты.

Используйте редактор nano для создания файла с именем inventory:

nano inventory

Добавьте следующее содержимое в файл inventory. Это определяет две группы, webservers и dbservers, каждая из которых содержит два хоста:

[webservers]
web1.example.com
web2.example.com

[dbservers]
db1.lab.net
db2.lab.net

Сохраните файл и выйдите из nano, нажав Ctrl+X, затем Y и Enter.

Теперь, когда наш инвентарь готов, давайте создадим простой плейбук. Этот плейбук будет использовать модуль ansible.builtin.debug для вывода сообщения, подтверждающего, на каком хосте выполняется задача. Это отличный способ протестировать шаблоны хостов без внесения каких-либо фактических изменений в систему.

Создайте новый файл с именем playbook.yml:

nano playbook.yml

Добавьте следующее содержимое в формате YAML. Изначально он нацелен на все хосты в группе webservers:

---
- name: Test Host Patterns
  hosts: webservers
  gather_facts: false
  tasks:
    - name: Display the inventory hostname
      ansible.builtin.debug:
        msg: "This task is running on {{ inventory_hostname }}"

Сохраните и выйдите из редактора. Теперь запустите плейбук с помощью ansible-playbook. Флаг -i указывает наш пользовательский файл инвентаря:

ansible-playbook playbook.yml -i inventory

Вывод должен выглядеть примерно так:

PLAY [Test Host Patterns] ******************************************************

TASK [Display the inventory hostname] ******************************************
ok: [web1.example.com] => {
    "msg": "This task is running on web1.example.com"
}
ok: [web2.example.com] => {
    "msg": "This task is running on web2.example.com"
}

PLAY RECAP *********************************************************************
web1.example.com           : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
web2.example.com           : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Как вы можете видеть, были выбраны только хосты из группы webservers. Теперь давайте изменим плейбук, чтобы использовать подстановочный знак (*). Подстановочные знаки позволяют более гибко сопоставлять шаблоны.

Отредактируйте playbook.yml и измените строку hosts на hosts: "*.lab.net". Не забудьте заключить шаблоны, содержащие подстановочные знаки, в кавычки:

---
- name: Test Host Patterns
  hosts: "*.lab.net"
  gather_facts: false
  tasks:
    - name: Display the inventory hostname
      ansible.builtin.debug:
        msg: "This task is running on {{ inventory_hostname }}"

Запустите плейбук снова:

ansible-playbook playbook.yml -i inventory

Вы должны увидеть вывод, похожий на этот:

PLAY [Test Host Patterns] ******************************************************

TASK [Display the inventory hostname] ******************************************
ok: [db1.lab.net] => {
    "msg": "This task is running on db1.lab.net"
}
ok: [db2.lab.net] => {
    "msg": "This task is running on db2.lab.net"
}

PLAY RECAP *********************************************************************
db1.lab.net                : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
db2.lab.net                : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

На этот раз плей выполнился только на хостах, имена которых заканчиваются на .lab.net. Наконец, давайте используем специальное ключевое слово all, чтобы нацелиться на каждый хост, определенный в инвентаре.

Измените playbook.yml в последний раз и измените строку hosts на hosts: all:

---
- name: Test Host Patterns
  hosts: all
  gather_facts: false
  tasks:
    - name: Display the inventory hostname
      ansible.builtin.debug:
        msg: "This task is running on {{ inventory_hostname }}"

Запустите плейбук, чтобы увидеть результат:

ansible-playbook playbook.yml -i inventory

Вывод покажет, что нацелены все хосты:

PLAY [Test Host Patterns] ******************************************************

TASK [Display the inventory hostname] ******************************************
ok: [web1.example.com] => {
    "msg": "This task is running on web1.example.com"
}
ok: [web2.example.com] => {
    "msg": "This task is running on web2.example.com"
}
ok: [db1.lab.net] => {
    "msg": "This task is running on db1.lab.net"
}
ok: [db2.lab.net] => {
    "msg": "This task is running on db2.lab.net"
}

PLAY RECAP *********************************************************************
db1.lab.net                : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
db2.lab.net                : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
web1.example.com           : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
web2.example.com           : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Теперь плейбук выполняется на всех четырех хостах из вашего инвентаря, демонстрируя мощь шаблона all.

Уточнение выбора хостов с помощью исключений и логических операторов

На этом этапе вы усовершенствуете свои навыки выбора хостов, научившись использовать исключения и логические операторы. Эти функции позволяют осуществлять очень точное нацеливание, что крайне важно при управлении сложными средами. Вы научитесь исключать хосты с помощью оператора ! (NOT) и объединять группы с помощью оператора & (AND). Мы продолжим работать с файлами inventory и playbook.yml из предыдущего шага.

Сначала убедитесь, что вы находитесь в правильном рабочем каталоге:

cd ~/project/ansible_patterns

Чтобы эффективно продемонстрировать логические операторы, нам нужно создать некоторое пересечение в наших группах хостов. Давайте отредактируем файл inventory, чтобы добавить новую группу под названием production, которая содержит один веб-сервер и один сервер баз данных:

nano inventory

Добавьте группу [production] и ее членов в конец файла:

[webservers]
web1.example.com
web2.example.com

[dbservers]
db1.lab.net
db2.lab.net

[production]
web1.example.com
db1.lab.net

Сохраните файл и выйдите из nano, нажав Ctrl+X, затем Y и Enter.

Теперь давайте попрактикуемся в исключении. Оператор ! (НЕ) позволяет исключить хост или группу из выбора. Измените ваш playbook.yml, чтобы нацелиться на все хосты, кроме тех, что входят в группу dbservers:

nano playbook.yml

Обновите строку hosts, как показано ниже. Шаблон all,!dbservers выбирает каждый хост, а затем удаляет любые, которые находятся в группе dbservers:

---
- name: Test Host Patterns
  hosts: all,!dbservers
  gather_facts: false
  tasks:
    - name: Display the inventory hostname
      ansible.builtin.debug:
        msg: "This task is running on {{ inventory_hostname }}"

Сохраните и выйдите из редактора, затем запустите плейбук:

ansible-playbook playbook.yml -i inventory

Вы должны увидеть, что нацелены только веб-серверы:

PLAY [Test Host Patterns] ******************************************************

TASK [Display the inventory hostname] ******************************************
ok: [web1.example.com] => {
    "msg": "This task is running on web1.example.com"
}
ok: [web2.example.com] => {
    "msg": "This task is running on web2.example.com"
}

PLAY RECAP *********************************************************************
web1.example.com           : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
web2.example.com           : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Как и ожидалось, были выбраны только хосты из группы webservers.

Далее давайте изучим логический оператор И. Оператор & выбирает только те хосты, которые присутствуют в обеих указанных группах (пересечение). Давайте изменим плейбук, чтобы нацелиться на хосты, которые находятся в группе webservers И также в группе production:

nano playbook.yml

Измените строку hosts на webservers,&production:

---
- name: Test Host Patterns
  hosts: webservers,&production
  gather_facts: false
  tasks:
    - name: Display the inventory hostname
      ansible.builtin.debug:
        msg: "This task is running on {{ inventory_hostname }}"

Сохраните и запустите плейбук:

ansible-playbook playbook.yml -i inventory

На этот раз будет выбрано только пересечение обеих групп:

PLAY [Test Host Patterns] ******************************************************

TASK [Display the inventory hostname] ******************************************
ok: [web1.example.com] => {
    "msg": "This task is running on web1.example.com"
}

PLAY RECAP *********************************************************************
web1.example.com           : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Вывод корректно показывает, что был выбран только web1.example.com, поскольку это единственный хост, который является членом как групп webservers, так и production. Эти операторы дают вам точный контроль над тем, на какие хосты влияет ваша автоматизация.

Модульное разделение плейбука с помощью include_tasks и import_tasks

На этом этапе вы научитесь структурировать более крупные проекты Ansible, разбивая их на более мелкие, повторно используемые файлы. По мере роста плейбуков становится трудно управлять всеми задачами в одном файле. Ansible предоставляет две основные директивы для этого: import_tasks и include_tasks. Обе позволяют включать задачи из другого файла.

  • import_tasks является статическим. Он обрабатывается при первом разборе плейбука Ansible. Это лучше всего подходит для безусловных, структурных частей вашего плейбука.
  • include_tasks является динамическим. Он обрабатывается во время выполнения плейбука. Это делает его подходящим для использования с циклами и условными операторами.

Теперь мы переработаем наш плейбук для использования обоих. Сначала убедитесь, что вы находитесь в каталоге проекта:

cd ~/project/ansible_patterns

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

nano inventory

Замените содержимое следующей конфигурацией, которая сопоставляет примеры хостов с localhost:

[webservers]
web1.example.com ansible_host=localhost ansible_connection=local
web2.example.com ansible_host=localhost ansible_connection=local

[dbservers]
db1.lab.net ansible_host=localhost ansible_connection=local
db2.lab.net ansible_host=localhost ansible_connection=local

Сохраните и выйдите из редактора. Эта конфигурация использует ansible_host=localhost для перенаправления соединений на локальную машину и ansible_connection=local для предотвращения попыток SSH-соединения.

Распространенной практикой является хранение повторно используемых файлов задач в выделенном подкаталоге. Давайте создадим один с именем tasks:

mkdir tasks

Теперь давайте создадим файл для общих задач настройки, которые могут применяться ко многим серверам. Мы поместим сюда задачу установки пакета веб-сервера httpd:

nano tasks/web_setup.yml

Добавьте следующее содержимое. Обратите внимание, что этот файл является просто списком задач; он не содержит полной структуры плейбука (например, hosts: или name:):

- name: Install the httpd package
  ansible.builtin.dnf:
    name: httpd
    state: present
  become: true

Сохраните и выйдите из nano. Затем создайте второй файл задач для простого шага проверки:

nano tasks/verify_config.yml

Добавьте в этот файл следующую задачу отладки:

- name: Display a verification message
  ansible.builtin.debug:
    msg: "Configuration tasks applied to {{ inventory_hostname }}"

Сохраните и выйдите из редактора. Теперь давайте изменим основной playbook.yml для использования этих новых файлов задач. Мы будем использовать import_tasks для статической настройки и include_tasks для динамического сообщения проверки:

nano playbook.yml

Замените все содержимое playbook.yml следующим. Этот плейбук теперь нацелен на группу webservers и использует модульные файлы задач:

---
- name: Configure Web Servers
  hosts: webservers
  gather_facts: false
  tasks:
    - name: Import web server setup tasks
      import_tasks: tasks/web_setup.yml

    - name: Include verification tasks
      include_tasks: tasks/verify_config.yml

Сохраните файл и запустите плейбук:

ansible-playbook playbook.yml -i inventory

Вы должны увидеть выполнение модульных задач:

PLAY [Configure Web Servers] ***************************************************

TASK [Import web server setup tasks] *******************************************
imported: /home/labex/project/ansible_patterns/tasks/web_setup.yml

TASK [Install the httpd package] ***********************************************
changed: [web1.example.com]
changed: [web2.example.com]

TASK [Include verification tasks] **********************************************
included: /home/labex/project/ansible_patterns/tasks/verify_config.yml for web1.example.com, web2.example.com

TASK [Display a verification message] ******************************************
ok: [web1.example.com] => {
    "msg": "Configuration tasks applied to web1.example.com"
}
ok: [web2.example.com] => {
    "msg": "Configuration tasks applied to web2.example.com"
}

PLAY RECAP *********************************************************************
web1.example.com           : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
web2.example.com           : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Обратите внимание, как вывод четко указывает, когда задачи импортируются и включаются из соответствующих файлов. Этот модульный подход делает вашу автоматизацию более чистой и простой в обслуживании.

Создание рабочего процесса с помощью import_playbook

На этом этапе вы научитесь оркестрировать целые плейбуки для формирования сложного рабочего процесса с использованием import_playbook. В то время как import_tasks и include_tasks предназначены для повторного использования списков задач в рамках одного плейбука, import_playbook работает на более высоком уровне. Он позволяет создавать мастер-плейбук, который выполняет другие, самодостаточные плейбуки в определенном порядке. Это стандартный способ управления крупномасштабной автоматизацией, такой как подготовка всего стека приложений.

Сначала давайте убедимся, что мы находимся в правильном каталоге, и организуем наш проект для этой новой структуры:

cd ~/project/ansible_patterns

Хорошей практикой является хранение отдельных плейбуков компонентов в выделенном подкаталоге. Давайте создадим каталог с именем playbooks:

mkdir playbooks

Теперь переместим плейбук, который мы создали на предыдущем шаге, для настройки веб-серверов, в этот новый каталог. Переименование его для большей ясности также является хорошей идеей:

mv playbook.yml playbooks/web_configure.yml

Однако, поскольку мы переместили плейбук в подкаталог, нам нужно обновить относительные пути к файлам задач. Файлы задач по-прежнему находятся в каталоге tasks/ относительно основного каталога проекта, поэтому нам нужно скорректировать пути:

nano playbooks/web_configure.yml

Обновите пути в плейбуке, чтобы использовать ../tasks/ вместо tasks/:

---
- name: Configure Web Servers
  hosts: webservers
  gather_facts: false
  tasks:
    - name: Import web server setup tasks
      import_tasks: ../tasks/web_setup.yml

    - name: Include verification tasks
      include_tasks: ../tasks/verify_config.yml

Сохраните и выйдите из редактора.

Давайте протестируем исправленный плейбук, чтобы убедиться, что пути работают правильно:

ansible-playbook playbooks/web_configure.yml -i inventory

Вы должны увидеть успешное выполнение плейбука с исправленными путями.

Далее создайте новый, отдельный плейбук для настройки ваших серверов баз данных. Этот плейбук будет нацелен на группу dbservers и установит пакет mariadb:

nano playbooks/db_setup.yml

Добавьте следующее содержимое в файл. Это полный, автономный плейбук:

---
- name: Configure Database Servers
  hosts: dbservers
  gather_facts: false
  tasks:
    - name: Install mariadb package
      ansible.builtin.dnf:
        name: mariadb
        state: present
      become: true

    - name: Display a confirmation message
      ansible.builtin.debug:
        msg: "Database server {{ inventory_hostname }} configured."

Сохраните и выйдите из редактора. Теперь у вас есть два плейбука компонентов: один для веб-серверов и один для серверов баз данных.

Наконец, создайте основной плейбук верхнего уровня. Этот файл сам по себе не будет содержать никаких хостов или задач. Его единственная задача — импортировать другие плейбуки в правильном порядке, чтобы определить общий рабочий процесс:

nano main.yml

Добавьте следующее содержимое. Это создает рабочий процесс, который сначала настраивает веб-серверы, а затем настраивает серверы баз данных:

---
- name: Import the web server configuration play
  import_playbook: playbooks/web_configure.yml

- name: Import the database server configuration play
  import_playbook: playbooks/db_setup.yml

Сохраните и выйдите из nano. Теперь вы готовы выполнить весь рабочий процесс, запустив плейбук main.yml:

ansible-playbook main.yml -i inventory

Вывод покажет выполнение обоих плейбуков последовательно:

PLAY [Configure Web Servers] ***************************************************

TASK [Import web server setup tasks] *******************************************
imported: /home/labex/project/ansible_patterns/playbooks/../tasks/web_setup.yml

TASK [Install the httpd package] ***********************************************
ok: [web1.example.com]
ok: [web2.example.com]

TASK [Include verification tasks] **********************************************
included: /home/labex/project/ansible_patterns/playbooks/../tasks/verify_config.yml for web1.example.com, web2.example.com

TASK [Display a verification message] ******************************************
ok: [web1.example.com] => {
    "msg": "Configuration tasks applied to web1.example.com"
}
ok: [web2.example.com] => {
    "msg": "Configuration tasks applied to web2.example.com"
}

PLAY [Configure Database Servers] **********************************************

TASK [Install mariadb package] *************************************************
changed: [db1.lab.net]
changed: [db2.lab.net]

TASK [Display a confirmation message] ******************************************
ok: [db1.lab.net] => {
    "msg": "Database server db1.lab.net configured."
}
ok: [db2.lab.net] => {
    "msg": "Database server db2.lab.net configured."
}

PLAY RECAP *********************************************************************
db1.lab.net                : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
db2.lab.net                : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
web1.example.com           : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
web2.example.com           : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Вывод четко показывает, что два отдельных плейбука выполняются последовательно, демонстрируя, как import_playbook эффективно составляет более крупный рабочий процесс из небольших, управляемых частей.

Выполнение и проверка полного модульного плейбука

На этом заключительном этапе вы выполните созданный вами полный модульный рабочий процесс и, что более важно, научитесь проверять, достигла ли автоматизация желаемого состояния на целевых системах. Успешное выполнение плейбука — это хорошо, но подтверждение результата имеет решающее значение для надежной автоматизации.

Сначала убедитесь, что вы находитесь в основном каталоге проекта:

cd ~/project/ansible_patterns

Прежде чем запускать финальный плейбук, давайте визуализируем полную структуру проекта, которую вы создали. Команда tree отлично подходит для этого. Если она не установлена, вы можете добавить ее с помощью dnf:

sudo dnf install -y tree
tree .

Вы должны увидеть структуру, подобную этой:

.
├── inventory
├── main.yml
├── playbooks
│   ├── db_setup.yml
│   └── web_configure.yml
└── tasks
    ├── verify_config.yml
    └── web_setup.yml

2 directories, 6 files

Эта структура с основной точкой входа (main.yml), отдельными файлами плейбуков и повторно используемыми файлами задач является масштабируемым и поддерживаемым способом управления проектами Ansible.

Теперь выполните весь рабочий процесс, запустив ваш основной плейбук main.yml:

ansible-playbook main.yml -i inventory

После успешного завершения плейбука следующим важным шагом является проверка. Вам нужно подтвердить, что система находится в том состоянии, которое вы предполагали. Наш плейбук был разработан для установки пакета httpd на веб-серверы и пакета mariadb на серверы баз данных. Поскольку все задачи в этой лаборатории выполняются на вашем локальном компьютере, мы можем проверить их установку напрямую с помощью команды rpm.

Сначала проверьте, был ли установлен пакет httpd в рамках настройки веб-сервера:

rpm -q httpd

Вы должны увидеть вывод, подтверждающий установку пакета:

httpd-2.4.xx-x.el9.x86_64

Затем проверьте установку пакета mariadb из конфигурации сервера базы данных:

rpm -q mariadb

Аналогично, вы должны увидеть подтверждение установки mariadb:

mariadb-10.5.xx-x.el9.x86_64

Видя названия пакетов в выводе, вы подтверждаете, что ваш плейбук Ansible успешно настроил систему в соответствии с вашими намерениями. Теперь вы успешно создали, выполнили и проверили модульный проект Ansible от начала до конца.

Резюме

В этой лаборатории вы изучили основные методы структурирования сложных плейбуков Ansible на RHEL. Вы начали с основ выбора хостов, используя базовые имена групп, подстановочные знаки, исключения и логические операторы для точного нацеливания узлов, определенных в файле инвентаризации. Затем фокус сместился на модульность, где вы практиковались в разбиении больших плейбуков на более управляемые и повторно используемые компоненты, используя как include_tasks для динамического включения, так и import_tasks для статического включения.

Опираясь на эти навыки, вы научились составлять полный, многоэтапный рабочий процесс, связывая отдельные плейбуки вместе с помощью import_playbook. Практический процесс включал установку Ansible, создание структуры проекта и постепенную рефакторизацию простого плейбука в сложную, многофайловую структуру. Лаборатория завершилась выполнением финального составного плейбука и проверкой успешного выполнения всего автоматизированного рабочего процесса на правильно нацеленных хостах, демонстрируя организованный и масштабируемый подход к автоматизации.