===== Как использовать loops в Ansible =====
В этом сообщении в блоге мы поговорим о 'циклах Ansible'. Мы начнем с того, что такое 'циклы', используя простой повседневный пример, чтобы каждый мог понять. Затем мы объясним, почему мы используем циклы в Ansible и как они могут нам помочь. Мы рассмотрим самый базовый тип цикла, а затем рассмотрим несколько различных видов. Мы покажем, как эти циклы используются в реальных ситуациях. И, наконец, мы поделимся некоторыми распространенными ошибками, которых следует избегать при использовании циклов в Ansible.
===== Почему мы используем циклы в Ansible? =====
Циклы в Ansible подобны ярлыкам для многократного выполнения одной и той же задачи. Вместо того чтобы повторять одни и те же шаги снова и снова, мы используем цикл, чтобы выполнять задачу столько раз, сколько нам нужно. Это экономит время и делает наши задачи менее подверженными ошибкам, потому что нам нужно написать инструкции только один раз.
- name: Install nginx
apt:
name: nginx
state: present
- name: Install mysql
apt:
name: mysql
state: present
- name: Install php
apt:
name: php
state: present
Это работает, но это повторяющийся процесс, и им трудно управлять. Если вам нужно установить десять или двадцать пакетов, руководство становится длинным и его трудно читать.
Теперь давайте используем цикл для выполнения той же задачи. Вы перечисляете все пакеты, которые хотите установить, а затем пишете единственную задачу, которая перебирает список.t.
- name: Install Packages
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- mysql
- php
Этот сборник задач выполняет точно такую же работу, как и первый, но он намного короче и понятнее. Если вам нужно добавить больше пакетов, вы просто добавляете их в список. Цикл позаботится об установке каждого из них. В этом сила циклов в Ansible.
В циклах Ansible термин '%%{{ item }}%%' используется в качестве заполнителя для каждого элемента в списке, который вы просматриваете. Мы рассмотрим это более подробно позже в этом посте.
==== Перебор простого списка ====
Ansible предоставляет простой и читаемый способ перебора набора задач с использованием ключевого слова 'loop'. Цель цикла - многократное повторение одной и той же задачи, что упрощает сценарий и сокращает количество повторений. Вот базовый синтаксис цикла Ansible.
tasks:
- name: Task description
ansible_module:
key: "{{ item }}"
loop: [item1, item2, item3]
* 'ansible_module' относится к модулю, который вы используете для выполнения задачи. В Ansible есть множество модулей для различных задач, таких как 'apt' для управления пакетами, 'file' для управления файлами и т.д.
* %%'ключ: '{{item }}''%% - это место, где мы указываем Ansible, что нужно изменить в каждом цикле. '%%{{ item }}%%' - это заполнитель, который Ansible заменяет каждым элементом в списке циклов.
* 'цикл' - это ключевое слово, которое запускает цикл, за которым следует список элементов для повторения цикла.
Возвращаясь к предыдущему примеру установки пакета, без цикла нам нужно было бы написать отдельную задачу для каждого пакета. С помощью цикла мы можем установить все пакеты с помощью одной задачи.
---
- name: Install packages using Ansible
hosts: servers
tasks:
- name: Install Packages
apt:
pkg: "{{ item }}"
state: present
loop:
- nginx
- mysql
- php
* Мы используем модуль 'apt', который управляет пакетами в системах на базе Debian.
* Строка pkg: %%{{ item }}%% сообщает Ansible, что устанавливать на каждой итерации цикла. Ansible заменяет '%% {{ item }}%%' на каждое имя пакета из списка.
* Ключевое слово 'loop' запускает цикл, за которым следует список пакетов, которые мы хотим установить.
Когда вы запустите этот сборник задач, Ansible установит пакеты 'nginx', 'mysql' и 'php' один за другим. Если вам нужно установить больше пакетов, вы просто добавляете их в список. Вот почему циклы настолько мощны в Ansible: они позволяют нам управлять несколькими элементами в рамках одной задачи, независимо от того, сколько элементов у нас есть.
==== Перебор словаря ====
Чтобы перебрать этот словарь, вы можете использовать фильтр 'dict2items', который преобразует словарь в список элементов. Каждый элемент в этом списке представляет собой еще один словарь с 'ключом' и 'значением'.
---
- name: Assign roles to servers
hosts: localhost
gather_facts: no
tasks:
- name: Print server role info
ansible.builtin.debug:
msg: "Server: {{ item.key }}, Role: {{ item.value }}"
loop: "{{ servers | dict2items }}"
vars:
servers:
server1: 'web'
server2: 'database'
* 'серверы' - это словарь, где каждый ключ представляет собой имя сервера, а значение - роль этого сервера.
* Мы используем фильтр 'dict2items' для преобразования словаря 'серверы' в список элементов. Каждый элемент представляет собой словарь меньшего размера с 'ключом' (именем сервера) и 'значением' (ролью).
* 'ansible.builtin.debug' - это модуль, который выводит сообщения на консоль. Мы используем его для распечатки каждого сервера и соответствующей ему роли.
* сообщение: Сервер: %%{{ item.key }}%%, Роль: %%{{ item.value }}%% - это место, где мы указываем Ansible, что печатать. Он берет имя каждого сервера и его роль и печатает их в удобочитаемом формате.
Когда вы запускаете этот сборник задач, Ansible распечатывает сообщение для каждого сервера с указанием назначенной ему роли. Это простой способ перебирать словари в Ansible, когда каждый ключ имеет единственное соответствующее значение.
➜ ansible_local ansible-playbook loop_dict.yml
PLAY [Assign roles to servers] ********************************************************************************************************
TASK [Print server role info] *********************************************************************************************************
ok: [127.0.0.1] => (item={'key': 'server1', 'value': 'web'}) => {
"msg": "Server: server1, Role: web"
}
ok: [127.0.0.1] => (item={'key': 'server2', 'value': 'database'}) => {
"msg": "Server: server2, Role: database"
}
===== Сложные циклы =====
=== Перебор вложенных списков ====
=== Фильтр продукта ===
вы можете использовать фильтр '**продукт **' для создания ** декартова произведения ** данных списков, что означает, что каждый элемент первого списка сопряжен с каждым элементом второго списка. Это может быть полезно в сценариях, когда вам нужно выполнить задачу со всеми возможными комбинациями элементов из нескольких списков. Вот пример использования фильтра 'продукт'.
---
- name: Install packages on servers
hosts: localhost
gather_facts: no
tasks:
- name: Install packages
ansible.builtin.debug:
msg: "Installing {{ item.1 }} on {{ item.0 }}"
loop: "{{ ['server1', 'server2'] | product(['nginx', 'mysql', 'php']) | list }}"
➜ ansible_local ansible-playbook nested_loop_product.yml
PLAY [Install packages on servers] ****************************************************************************************************
TASK [Install packages] ***************************************************************************************************************
ok: [127.0.0.1] => (item=['server1', 'nginx']) => {
"msg": "Installing nginx on server1"
}
ok: [127.0.0.1] => (item=['server1', 'mysql']) => {
"msg": "Installing mysql on server1"
}
ok: [127.0.0.1] => (item=['server1', 'php']) => {
"msg": "Installing php on server1"
}
ok: [127.0.0.1] => (item=['server2', 'nginx']) => {
"msg": "Installing nginx on server2"
}
ok: [127.0.0.1] => (item=['server2', 'mysql']) => {
"msg": "Installing mysql on server2"
}
ok: [127.0.0.1] => (item=['server2', 'php']) => {
"msg": "Installing php on server2"
=== Подэлементы ===
В Ansible вы также можете перебирать вложенные списки с помощью фильтра **subelements**. Это полезно, когда вы хотите выполнить задачу для каждой комбинации элементов во внешнем и внутреннем списках. Вот базовая структура использования фильтра **subelements**.
---
- name: Install packages on servers
hosts: localhost
gather_facts: no
tasks:
- name: Install packages
ansible.builtin.debug:
msg: "Installing {{ item.1 }} on {{ item.0.name }}"
loop: "{{ servers | subelements('packages') }}"
vars:
servers:
- name: server1
packages:
- nginx
- mysql
- name: server2
packages:
- httpd
- postgresql
* 'серверы' - это список словарей. У каждого словаря есть ключ **name ** для имени сервера и ключ ** packages ** для списка пакетов.
* Мы используем фильтр %%subelements('пакеты')%% для циклического просмотра как серверов, так и их пакетов.
* 'ansible.builtin.debug' - это модуль, который выводит сообщения на консоль. Мы используем его для распечатки каждой комбинации сервер-пакет.
* 'сообщение: 'Установка %%{{ item.1 }}%% на %%{{ item.0.name }}%%'' - это место, где мы указываем Ansible, что печатать. Он берет имя каждого сервера и каждого пакета и печатает их в удобочитаемом формате.
➜ ansible_local ansible-playbook nested_list_loop.yml
PLAY [Install packages on servers] ****************************************************************************************************
TASK [Install packages] ***************************************************************************************************************
ok: [127.0.0.1] => (item=[{'name': 'server1', 'packages': ['nginx', 'mysql']}, 'nginx']) => {
"msg": "Installing nginx on server1"
}
ok: [127.0.0.1] => (item=[{'name': 'server1', 'packages': ['nginx', 'mysql']}, 'mysql']) => {
"msg": "Installing mysql on server1"
}
ok: [127.0.0.1] => (item=[{'name': 'server2', 'packages': ['httpd', 'postgresql']}, 'httpd']) => {
"msg": "Installing httpd on server2"
}
ok: [127.0.0.1] => (item=[{'name': 'server2', 'packages': ['httpd', 'postgresql']}, 'postgresql']) => {
"msg": "Installing postgresql on server2"
===== Приостановка в цикле =====
Иногда при выполнении действий в цикле нам может потребоваться сделать паузу между итерациями. Это может быть связано с тем, что мы ожидаем перезапуска сервера, появления файла или по ряду других причин. Вы можете использовать 'loop_control' с 'pause', чтобы добавить задержку между каждой итерацией цикла.
---
- name: Pausing within a loop using loop_control
hosts: localhost
gather_facts: no
tasks:
- name: Print number with pause
ansible.builtin.debug:
msg: "{{ item }}"
loop: [1, 2, 3]
loop_control:
pause: 3
* **loop: [1, 2, 3]** указывает, что мы хотим выполнить цикл по числам 1, 2 и 3.
* модуль **ansible.builtin.debug** выводит текущий номер.
* **loop_control: pause: 3** приостанавливается на 3 секунды после каждой задачи в цикле.
Когда вы запускаете этот сборник задач, Ansible печатает каждое число, делает паузу на 3 секунды, а затем переходит к следующей итерации цикла.