Автор статьи: Рустем Галиев

IBM Senior DevOps Engineer & Integration Architect. Официальный DevOps ментор и коуч в IBM

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

Простыми словами — это то, как у нас будет выполняться плейбук. Давайте взглянем на условный плейбук:

-
  name: Deploy a web application
  hosts: host1
  tasks:
	- name: Install dependencies
  	apt: name={{ item }} state=installed
  	with_items:
   	- python
   	- python-setuptools
   	- python-dev
   	- build-essential
   	- python-pip
   	- python-mysqldb

	- include: ./tasks/deploy_db.yml

	- name: Install Python Flask dependencies
  	pip:
    	name: '{{ item }}'
    	state: present
  	with_items:
   	- flask
   	- flask-mysql

	- name: Copy web-server code
  	copy: src=app.py dest=/opt/app.py

	- name: Start web-application
  	shell: FLASK_APP=/opt/app.py nohup flask run --host=0.0.0.0 &

Когда Ansible запускает плейбук, он выполняет один таск за другим в порядке очереди, т.е. из кода выше он сперва установит зависимости, потом задеплоит БД, установит flask, скопирует код и запустит веб-приложение. Но это когда у нас один хост.

А если у нас несколько хостов? Скажем, группа из 3-5 инстансов.

Inventory.ini

[web_servers]
web_server1
web_server2
web_server3
web_server4
web_server5

Playbook.yml: 

-
  name: Deploy a web application
  hosts: web_servers
  tasks: …

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

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

Это называется стандартной стратегией или же default.

Также у нас есть следующая стратегия, которая называется free. Для того, чтобы менять стратегию плейбука, нужно добавить директиву strategy и указать значение. Если мы не укажем стратегию в плейбуке, то Ansible будет использовать дефолтную стратегию.

-
  name: Deploy a web application
  strategy: free
  hosts: web_servers
  tasks: …

Так что же это за тип стратегии free?

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

Также есть еще один тип стратегии (хотя не совсем стратегии, скорее дополнение к линейному дефолту). Допустим, у нас есть пять серверов, но мы хотели бы, чтобы Ansible запускал плейбук на трех одновременно. Для этого мы используем стандартную стратегию, но с дополнительной опцией serial, где мы указываем, на скольких хостах мы хотим выполнять плейбук одновременно.

-
  name: Deploy a web application
  hosts: web_servers
  serial: 3

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

Давайте сделаем еще один шаг вперед: представим, что у нас инвентарь состоит из 10 хостов. Директива serial имеет несколько расширенных вариантов использования. Например, мы могли бы сначала запустить плей на 2 серверах, затем на 3, и в конце последние 5 вместе, предоставив массив в качестве входных данных для последовательной директивы. Это действительно полезно для «Rolling Updates».

-
  name: Deploy a web application
  hosts: web_servers
  serial:
	- 2
	- 3
	- 5

Мы также можем использовать процентны. Директива serial имеет несколько расширенных вариантов использования. Например, как я уже сказал, мы можем указать, чтобы он запустил плейбук на 25% от общего количества серверов за раз. Вот так:

-
  name: Deploy a web application
  hosts: web_servers
  serial: "25%"

Поэтому если вы скажете 25% вместо трех, то он будет выполняться на 25% от всех серверов за раз.

Итак, со сколькими серверами Ansible может общаться одновременно? Что, если бы у нас было сто серверов, и мы хотели бы запустить этот плейбук на всех из них одновременно? Будет ли Ansible запускать плейбук на всех сотнях серверов одновременно?

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

По умолчанию Ansible может одновременно создавать пять форков, и это определено в файле конфигурации Ansible.cfg.

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

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

Поскольку мы затронули тему с условной сотней серверов, давайте поговорим про lookup’ы.

Представим, что до сих пор мы хранили учетные данные для наших целевых серверов в файле инвентари. Что делать, если серверов слишком много или эта информация уже доступна в другом месте?

Допустим, у нас есть учетные данные серверов, хранящиеся в файле CSV в формате имени хоста и пароля. Первый столбец — это имя хоста, а второй — пароль.

Credentials.csv

Hostname, password
Target1, password
Target2, password

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

Вот как мы используем плагин поиска.

{{ lookup('csvfile', 'target1  file=credentials.csv delimiter=,') }}

Например, в этом случае первый аргумент, который мы передаем плагину поиска, — это тип файла, который в данном случае является CSV-файлом.

Затем идет значение для поиска. Например, здесь мы хотели бы найти информацию о целевом сервере, а затем о файле, который мы просматриваем.

И, наконец, разделитель, который в данном случае является запятой, потому что это файл CSV. Если у нас есть файл, в котором используется другой разделитель, например, точка с запятой или что-то в этом роде, мы могли бы указать это здесь. Таким образом, этот же плагин поиска для файла CSV также поддерживает несколько других форматов файлов, разделенных символами.

В этом случае вся эта функция или плагин будет возвращать пароль из CSV-файла. Есть несколько других доступных плагинов поиска, таких как ini, DNS, MongoDB и так далее.

Давайте представим ситуацию: мы переместили учетные данные для хостов из файла инвентари в отдельный CSV-файл и вместо того, чтобы все это хардкодить, мы будем использовать лукап, вот так:

-
  name: Check if host is available
  hosts: web_server
  vars:
	ansible_ssh_pass: "{{ lookup('csvfile', 'web_server file=credentials.csv delimiter=,') }}"

При другом варианте, когда наши пароли от хостов хранятся в ini-файле, мы пропишем лукап вот так:

-
  name: Check if host is available
  hosts: web_server
  vars:
	ansible_ssh_pass: "{{ lookup('ini', 'password section=web_server file=credentials.ini') }}"

Вот так оно и делается :)


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

Комментарии (5)


  1. selivanov_pavel
    15.08.2023 17:31
    +2

    with_items давно deprecated в пользу loop


  1. JI0C0Cb
    15.08.2023 17:31
    -4

    Ansible давно deprecated в пользу jet https://habr.com/ru/companies/flant/news/751402/.


    1. psi
      15.08.2023 17:31
      +3

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


  1. Hamletghost
    15.08.2023 17:31

    Мой вариант: вообще ничего не надо провиженить через ssh.

    VM деплоятся из готового неизменяемого образа используя terraform

    образы собираются с помощью packer

    Обновление - пересоздание vm из нового образа

    Подход называется immutable infrastructure

    профит - гарантия полной идентичности хостов и отсутствие ошибок операторов (тик никто ничего не «админит»)

    кроме того старт vm из готового образа значительно быстрее чем прогон плейбука (если это настоящий плейбук а не школьный пример)


  1. Tamerlan666
    15.08.2023 17:31
    +2

    Приведенный пример плейбука - просто жесть. Такое нагромождение антипаттернов еще поискать нужно. Там и строчный синтаксис в плейбуке вместо нормального yaml, и ненужные в модулях apt и pip циклы with_items, и совершенно дикие и несогласованные отступы в структуре модулей...