ИТ Блог. Администрирование серверов на основе Linux (Ubuntu, Debian, CentOS, openSUSE)
Понедельник, 31 марта, 2025
Сегодня у нас 1 праздник:
Международный День Резервного Копирования (World Backup Day). Пользователи сайта социальных новостей reddit предложили сделать дату 31.03 Международным днём резервного копирования, аргументируя это тем, что никогда заранее нельзя узнать, какие сюрпризы преподнесёт 1.04

Ansible. Циклы

Краткое руководство: Как установить и настроить ansible в Linux для автоматизации

Иногда вам может потребоваться повторить задание несколько раз. Например, вы можете создать несколько пользователей, запустить/остановить несколько служб или сменить владельца для нескольких файлов на управляемых хостах.

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

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

Также приветствуется знание основ YAML.

 

Цикл по спискам

Ansible использует цикл ключевых слов для перебора элементов списка. Для демонстрации давайте создадим очень простую книгу с именем print-list.yml, которая покажет вам, как распечатать элементы в списке:

[destroyer@andreyex]$ cat print-list.yml 
---
- name: print list
  hosts: node1
  vars:
    prime: [2,3,5,7,11]
  tasks:
    - name: Show first five prime numbers
      debug:
        msg: "{{ item }}"
      loop: "{{ prime }}"

 

Обратите внимание, что мы используем переменную item с циклами Ansible. Задача будет выполняться пять раз, что равно количеству элементов в простом списке.

При первом запуске переменная элемента будет установлена на первый элемент в массиве простых чисел (2). При втором запуске переменная item будет установлена ​​на второй элемент в массиве простых чисел (3) и так далее.

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

[destroyer@andreyex]$ ansible-playbook print-list.yml 

PLAY [print list] **************************************************************

TASK [Gathering Facts] *********************************************************
ok: [node1]

TASK [Show first five prime numbers] *******************************************
ok: [node1] => (item=2) => {
    "msg": 2
}
ok: [node1] => (item=3) => {
    "msg": 3
}
ok: [node1] => (item=5) => {
    "msg": 5
}
ok: [node1] => (item=7) => {
    "msg": 7
}
ok: [node1] => (item=11) => {
    "msg": 11
}

PLAY RECAP *********************************************************************
node1                      : ok=2    changed=0    unreachable=0    failed=0

 

Теперь вы применяете циклы к реальному приложению. Например, вы можете создать playbook add-users.yml, который добавит нескольких пользователей на все хосты в группе dbservers:

[destroyer@andreyex]$ cat add-users.yml 
---
- name: Add multiple users
  hosts: dbservers
  vars:
    dbusers:
      - username: brad
        pass: pass1
      - username: david
        pass: pass2
      - username: jason
        pass: pass3
  tasks: 
    - name: Add users
      user:
        name: "{{ item.username }}"
        password: "{{ item.pass | password_hash('sha512') }}"
      loop: "{{ dbusers }}"

 

Сначала мы создали список dbusers, который в основном представляет собой список хешей/словарей. Затем мы использовали пользовательский модуль вместе с циклом, чтобы добавить пользователей и установить пароли для всех пользователей в списке dbusers.

Обратите внимание, что мы также использовали точечную нотацию item.username и item.pass для доступа к значениям ключей внутри хешей/словарей списка dbusers.

Также стоит отметить, что мы использовали фильтр password_hash (‘sha512’) для шифрования паролей пользователей с помощью алгоритма хеширования sha512, поскольку пользовательский модуль не позволял устанавливать незашифрованные пароли пользователей.

Теперь запустим playbook add-users.yml :

[destroyer@andreyex]$ ansible-playbook add-users.yml 

PLAY [Add multiple users] ******************************************************

TASK [Gathering Facts] *********************************************************
ok: [node4]

TASK [Add users] ***************************************************************
changed: [node4] => (item={'username': 'brad', 'pass': 'pass1'})
changed: [node4] => (item={'username': 'david', 'pass': 'pass2'})
changed: [node4] => (item={'username': 'jason', 'pass': 'pass3'})

PLAY RECAP *********************************************************************
node4                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0

 

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

[destroyer@andreyex]$ ansible dbservers -m command -a "tail -3 /etc/passwd"
node4 | CHANGED | rc=0 >>
brad:x:1001:1004::/home/brad:/bin/bash
david:x:1002:1005::/home/david:/bin/bash
jason:x:1003:1006::/home/jason:/bin/bash

 

Перебирать словари

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

Например, если вы запустите следующую книгу воспроизведения print-dict.yml:

[destroyer@andreyex]$ cat print-dict.yml 
---
- name: Print Dictionary
  hosts: node1
  vars:
    employee: 
      name: "destroyer Alderson"
      title: "Penetration Tester"
      company: "Linux AndreyEx"
  tasks:
    - name: Print employee dictionary
      debug:
        msg: "{{ item }}"
      loop: "{{ employee }}"

 

Вы получите следующую ошибку:

[destroyer@andreyex]$ ansible-playbook print-dict.yml 

PLAY [Print Dictionary] ********************************************************

TASK [Gathering Facts] *********************************************************
ok: [node1]

TASK [Print employee dictionary] ***********************************************
fatal: [node1]: FAILED! => {"msg": "Invalid data passed to 'loop', it requires a list, got this instead: {'name': 'destroyer Alderson', 'title': 'Penetration Tester', 'company': 'Linux AndreyEx'}. Hint: If you passed a list/dict of just one element, try adding wantlist=True to your lookup invocation or use q/query instead of lookup."}

 

Как видите, ошибка явно говорит о том, что требуется список.

Чтобы исправить эту ошибку; вы можете использовать фильтр dict2items для преобразования словаря в список. Итак, в playbook print-dict.yml, отредактируйте строку:

loop: "{{ employee }}"

 

и примените фильтр dict2items следующим образом:

loop: "{{ employee | dict2items }}"

 

Затем снова запустите playbook:

[destroyer@andreyex]$ ansible-playbook print-dict.yml 

PLAY [Print Dictionary] ********************************************************

TASK [Gathering Facts] *********************************************************
ok: [node1]

TASK [Print employee dictionary] ***********************************************
ok: [node1] => (item={'key': 'name', 'value': 'destroyer Alderson'}) => {
    "msg": {
        "key": "name",
        "value": "destroyer Alderson"
    }
}
ok: [node1] => (item={'key': 'title', 'value': 'Penetration Tester'}) => {
    "msg": {
        "key": "title",
        "value": "Penetration Tester"
    }
}
ok: [node1] => (item={'key': 'company', 'value': 'Linux AndreyEx'}) => {
    "msg": {
        "key": "company",
        "value": "Linux AndreyEx"
    }
}

PLAY RECAP *********************************************************************
node1                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0

 

Успех! Были отображены пары ключ/значение словаря сотрудников.

 

Цикл по диапазону чисел

Вы можете использовать функцию range() вместе с фильтром списка для циклического перебора диапазона чисел.

Например, следующая задача распечатает все числа от 0 до 9:

- name: Range Loop
  debug:
    msg: "{{ item }}"
  loop: "{{ range(10) | list }}"

 

Вы также можете начать свой диапазон с числа, отличного от нуля. Например, следующая задача напечатает все числа от 5 до 14:

- name: Range Loop
  debug:
    msg: "{{ item }}"
  loop: "{{ range(5,15) | list }}"

 

По умолчанию шаг установлен на 1. Однако вы можете установить другой шаг.

Например, следующая задача распечатает все четные IP-адреса в подсети 192.168.1.x:

- name: Range Loop
  debug:
    msg: 192.168.1.{{ item }}
  loop: "{{ range(0,256,2) | list }}"

 

Где начало = 0, конец <256 и шаг = 2.

 

Зацикливание на запасах

Вы можете использовать встроенную переменную групп Ansible, чтобы перебрать все ваши хосты инвентаризации или только их подмножество. Например, чтобы перебрать все ваши инвентарные хосты; ты можешь использовать:

loop: "{{ groups['all'] }}"

 

Если вы хотите перебрать все хосты в группе веб-серверов, вы можете использовать:

loop: "{{ groups['webservers'] }}"

 

Чтобы увидеть, как это работает в playbook; взгляните на следующую книгу loop-inventory.yml:

[destroyer@andreyex]$ cat loop-inventory.yml 
---
- name: Loop over Inventory 
  hosts: node1
  tasks:
    - name: Ping all hosts
      command: ping -c 1 "{{ item }}"
      loop: "{{ groups['all'] }}"

 

Этот сценарий проверяет, может ли node1 пинговать все остальные хосты в вашем инвентаре. Идите вперед и запустите playbook:

[destroyer@andreyex]$ ansible-playbook loop-inventory.yml 

PLAY [Loop over Inventory] *****************************************************

TASK [Gathering Facts] *********************************************************
ok: [node1]

TASK [Ping all hosts] **********************************************************
changed: [node1] => (item=node1)
changed: [node1] => (item=node2)
changed: [node1] => (item=node3)
changed: [node1] => (item=node4)

PLAY RECAP *********************************************************************
node1                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0

 

Если вы получите какие-либо ошибки; это будет означать, что ваши управляемые хосты не могут пинговать (достигать) друг друга.

 

Пауза в циклах

Вы можете сделать паузу на определенное время между каждой итерацией цикла. Для этого вы можете использовать директиву pause вместе с ключевым словом loop_control.

Чтобы продемонстрировать это, давайте напишем playbook countdown.yml, который будет просто делать десятисекундный обратный отсчет перед отображением сообщения «С днем рождения!» на экране:

[destroyer@andreyex]$ cat countdown.yml 
---
- name: Happy Birthday Playbook
  hosts: node1
  tasks:
  - name: Ten seconds countdown
    debug:
      msg: "{{ 10 - item }} осталось несколько секунд ..."
    loop: "{{ range(10) | list }}"
    loop_control: 
      pause: 1

  - name: Display Happy Birthday
    debug:
      msg: "С днем рождения!"

 

Идите вперед и запустите playbook:

[destroyer@andreyex]$ ansible-playbook countdown.yml 

PLAY [С днем рождения Playbook] *************************************************

TASK [Сбор Фактов] *********************************************************
ok: [node1]

TASK [Подождите десять секунд] ****************************************************
ok: [node1] => (item=0) => {
    "msg": "10 осталось несколько секунд ..."
}
ok: [node1] => (item=1) => {
    "msg": "9 осталось несколько секунд ..."
}
ok: [node1] => (item=2) => {
    "msg": "8 осталось несколько секунд ..."
}
ok: [node1] => (item=3) => {
    "msg": "7 осталось несколько секунд ..."
}
ok: [node1] => (item=4) => {
    "msg": "6 осталось несколько секунд ..."
}
ok: [node1] => (item=5) => {
    "msg": "5 осталось несколько секунд ..."
}
ok: [node1] => (item=6) => {
    "msg": "4 осталось несколько секунд ..."
}
ok: [node1] => (item=7) => {
    "msg": "3 осталось несколько секунд ..."
}
ok: [node1] => (item=8) => {
    "msg": "2 осталось несколько секунд ..."
}
ok: [node1] => (item=9) => {
    "msg": "1 осталось несколько секунд ..."
}

TASK [Дисплей: с днем рождения] **************************************************
ok: [node1] => {
    "msg": "С днем рождения!"
}

PLAY RECAP *********************************************************************
node1                      : ok=3    changed=0    unreachable=0    failed=0    skipped=0

 

Итак, вы познакомились с циклами в Ansible.

Exit mobile version