Инструменты пользователя

Инструменты сайта


linux:ansible:loops

Это старая версия документа!


===== Как использовать 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'

Copy

  • servers is a dictionary where each key is a server name and the value is the role of that server.
  • We're using the dict2items filter to convert the servers dictionary into a list of items. Each item is a smaller dictionary with a 'key' (the server name) and a 'value' (the role).
  • ansible.builtin.debug is a module that prints messages to the console. We're using it to print out each server and its corresponding role.
  • msg: «Server: {{ item.key }}, Role: {{ item.value }}» is where we tell Ansible what to print. It takes each server name and its role and prints them in a readable format.

When you run this playbook, Ansible will print out a message for each server, showing the role assigned to it. This is a straightforward way to loop over dictionaries in Ansible when each key has a single corresponding value.

?  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"
}

Copy

Complex Loops

Looping over Nested Lists

Product Filter

you can use the product filter for creating a Cartesian Product of the given lists, which means every item of the first list is paired with every item of the second list. This can be useful in scenarios where you need to perform a task with every possible combination of items from multiple lists. Here's an example using the product filter.

---
- 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 }}"

Copy

?  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"

Copy

??

If you're anything like me, you might've seen the term 'Cartesian Product' and immediately thought, 'Wow, what's that?' - When we talk about the Cartesian product in the context of programming, we usually refer to combining each item of one list (or set) with each item of another list (or set).

Subelements

In Ansible, you can also loop over nested lists using the subelements filter. This is useful when you want to perform a task for each combination of elements in the outer and inner lists. Here's a basic structure of how to use the subelements filter.

---
- 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

Copy

  • servers is a list of dictionaries. Each dictionary has a 'name' key for the server name and a 'packages' key for the list of packages.
  • We're using the subelements('packages') filter to loop over both the servers and their packages.
  • ansible.builtin.debug is a module that prints messages to the console. We're using it to print out each server-package combination.
  • msg: «Installing {{ item.1 }} on {{ item.0.name }}» is where we tell Ansible what to print. It takes each server name and each package and prints them in a readable format.
?  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"

Copy

Pausing within a Loop

Sometimes, when performing actions in a loop, we might need to pause between iterations. This could be because we're waiting for a server to restart, for a file to become available, or for any number of reasons. You canloop_control with pause to add a delay between each loop iteration.

---
- 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

Copy

  • loop: [1, 2, 3] specifies that we want to loop over the numbers 1, 2, and 3.
  • ansible.builtin.debug module prints the current number.
  • loop_control: pause: 3 pauses for 3 seconds after each task in the loop.

When you run this playbook, Ansible will print each number, pause for 3 seconds, and then proceed to the next iteration of the loop.

Closing Thoughts

So, there you have it. We've learned that Ansible loops are a handy tool that helps us automate repetitive tasks. Please let me know in the comments if you have any questions or feedback. В 

References

linux/ansible/loops.1703791646.txt.gz · Последние изменения: 2023/12/28 22:27 — werwolf