Примеры работы с Ansible

Thank you for reading this post, don't forget to subscribe!

Инструк­ция пред­став­ля­ет из себя шпар­гал­ку по рабо­те с Ansible

Получение информации

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

1. Сбор инфор­ма­ции о системе.

Что­бы собрать инфор­ма­цию о систе­ме, на кото­рой выпол­ня­ет­ся сце­на­рий ansible, нуж­но исполь­зо­вать модуль setup, напри­мер:


Или мож­но при­ме­нить фильтр, что­бы сбор выпол­нял­ся быстрее:

 

О setup: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html.

2. Пока­зать инфор­ма­цию об уда­лен­ной систе­ме, на кото­рой запус­ка­ет­ся ansible.

Выпол­ня­ет­ся с помо­щью моду­ля debug, кото­рый дол­жен пока­зать содер­жи­мое ansible_facts:


* ansible_facts содер­жит мас­сив дан­ных с инфор­ма­ци­ей о систе­ме. Одна­ко, функ­ция сбор­ки инфор­ма­ции может быть отклю­че­на (так как на ее рабо­ту тра­тит­ся, отно­си­тель­но, мно­го вре­ме­ни) в настрой­ках плей­бу­ка с помо­щью опции gather_facts: false — в этом слу­чае, зна­че­ние нуж­но изме­нить на true.

Так­же мы можем обра­тить­ся к кон­крет­но­му эле­мен­ту мас­си­ва ansible_facts, полу­чив инфор­ма­цию о кон­крет­ной настрой­ке или опции:


* напри­мер, имя компьютера.

Ссыл­ка на доку­мен­та­цию: https://docs.ansible.com/ansible/latest/user_guide/playbooks_vars_facts.html.

3. Отоб­ра­же­ние на экран переменной.

Выше мы уже исполь­зо­ва­ли debug для отоб­ра­же­ния пере­мен­ной — прин­цип тот же:


* при выпол­не­нии зада­чи на экране мы уви­дим зна­че­ние пере­мен­ной variable. Обра­ти­те вни­ма­ние, что запись ansible.builtin.debug и debug — это одно и то же, то есть, ansible.builtin мож­но не писать.

О debug: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/debug_module.html.

4. Полу­чить спи­сок сервисов.

Для это­го суще­ству­ет service_facts:


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

О service_facts: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/service_facts_module.html.

5. Сохра­не­ние резуль­та­та при выпол­не­нии задания.

Создать и исполь­зо­вать пере­мен­ную мож­но в про­цес­се выпол­не­ния рабо­ты с помо­щью дирек­ти­вы register:

Так­же нам может пона­до­бить­ся сохра­нить в пере­мен­ную резуль­тат обра­бот­ки переменной:


* в дан­ном слу­чае мы взя­ли пере­мен­ную, кото­рую полу­чи­ли в при­ме­ре выше и раз­би­ли стро­ку по зна­ку ":". Таким обра­зом, мы полу­чим мас­сив дан­ных, кото­рый будет сохра­нен в пере­мен­ную my_hosts.

Работа с переменными

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

1. Пере­мен­ные в ролях.

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

Неза­ви­си­мо от ката­ло­га, в кото­ром идет опи­са­ние пере­мен­ной, син­так­сис один:


* в дан­ном при­ме­ре опре­де­ле­на пере­мен­ная hostname со зна­че­ни­ем mail.test.local.

2. Пере­мен­ные в фай­ле inventory.

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

* в нашем при­ме­ре будут опре­де­ле­ны 3 переменные:

  • tower_user_name для всех машин.
  • human_name для 192.168.0.10.
  • human_name для 192.168.0.11.

3. Пере­мен­ные в плейбуке.

Зада­ют­ся в фай­ле плейбука:


* в дан­ном при­ме­ре это пере­мен­ная vm_name.

4. При запус­ке плейбука.

Когда мы запус­ка­ем на выпол­не­ние наш плей­бук, мы можем пере­дать пере­мен­ные с помо­щью опции extra-vars:


* пере­да­ем пере­мен­ную address.

Проверки, условия и действия на основе этих проверок

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

1. Про­вер­ка на пустую папку.

Зада­чи сво­дит­ся к двум операциям:

  • полу­че­нии спис­ка фай­лов в целе­вом ката­ло­ге (напри­мер, с помо­щью коман­ды ls) и реги­стра­ции полу­чен­но­го зна­че­ния в пере­мен­ную с помо­щью register.
  • про­вер­ка содер­жи­мо­го пере­мен­ной, кото­рую мы полу­чи­ли на шаге 1 с помо­щью when.

При­мер будет таким:


* в дан­ном при­ме­ре мы в пер­вой зада­че выво­дим содер­жи­мое ката­ло­га /var/lib/postgresql/11/main и поме­ща­ем его в пере­мен­ную pg_contents. Во вто­рой зада­че мы уже про­ве­ря­ем с помо­щью when коли­че­ство строк — если их 0, то тогда выпол­ня­ем коман­ду initdb. На прак­ти­ке, это важ­но, так как ини­ци­а­ли­за­ция базы PostgreSQL при непу­стом ката­ло­ге выво­дит сооб­ще­ние об ошибке.

О when: https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html.

2. Про­ве­рить, опре­де­ле­на ли переменная.

Для это­го исполь­зу­ет­ся опция is defined (опре­де­ле­на) или is not defined (не определена):


* в дан­ном при­ме­ре мы про­ве­рим нали­чие пере­мен­ной pgpro. На прак­ти­ке такая про­вер­ка име­ет зна­че­ние, так как если мы попро­бу­ем выпол­нить дей­ствия с несу­ще­ству­ю­щей пере­мен­ной, Ansible нам вер­нет ошибку.

В офи­ци­аль­ной доку­мен­та­ции про это ска­за­но в ста­тье о when (ссыл­ка выше).

3. Выпол­не­ние коман­ды, если сер­вис в рабо­чем состоянии.

Нам необ­хо­ди­мо полу­чить инфор­ма­цию о служ­бах с помо­щью service_facts, после чего мож­но уже делать про­вер­ку с помо­щью when:


* в дан­ном при­ме­ре мы про­ве­рим, есть ли служ­ба apache2 и запу­ще­на ли она. Если это так, то мы ее останавливаем.

Подроб­нее о service_facts мож­но про­чи­тать в доку­мен­та­ции (ссыл­ка выше в раз­де­ле 4. Полу­чить спи­сок сер­ви­сов).

4. Суще­ству­ет ли файл.

Про­вер­ка может быть выпол­не­на с помо­щью stat:


* в дан­ном при­ме­ре будет выпол­не­но чте­ние фай­ла /etc/nginx/nginx.conf, если он существует.

О stat: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/stat_module.html.

5. Опе­ра­ци­он­ная система.

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

а) Про­вер­ка по семейству:


* в дан­ном при­ме­ре мы запу­стим зада­чу task1 в систе­мах на осно­ве Debian и task2 — на осно­ве RedHat.

б) Про­вер­ка по вер­сии системы:


* в дан­ном при­ме­ре мы запу­стим зада­чу task1 в систе­мах на осно­ве Debian вер­сии 9 и task2 — на осно­ве RedHat вер­сии 8.

6. Выдать ошиб­ку и оста­но­вить выполнение.

Для это­го преду­смот­рен модуль fail или опция зада­ния failed_when. Рас­смот­рим при­ме­ры исполь­зо­ва­ния обоих.

а) Модуль fail. Явля­ет­ся отдель­ным зада­ни­ем. Его мож­но выпол­нить про­сто так, но без усло­вия его при­ме­не­ние нело­гич­но. Пример:


* в нашем при­ме­ре, если пере­мен­ная srv_ip ока­жет­ся пустой или неопре­де­лен­ной, мы полу­чим ошиб­ку с сооб­ще­ни­ем, опре­де­лен­ным в опции msg.

О fail: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/fail_module.html.

б) Опция failed_when. Зада­ет усло­вие, при соблю­де­нии кото­ро­го зада­ние выпол­ня­ет­ся с ошиб­кой, например:


* дан­ный при­мер с офи­ци­аль­но­го сай­та ansible (ссыл­ка ниже). Зада­ние выдаст ошиб­ку, если в резуль­та­те выпол­не­ния скрип­та /usr/bin/example-command будет текст FAILED.

О failed_when: https://docs.ansible.com/ansible/latest/user_guide/playbooks_error_handling.html.

7. Ожи­да­ния с помо­щью wait_for.

Мы можем создать зада­ние, кото­рое оста­но­вит выпол­не­ние плей­бу­ка, пока не будет выпол­не­но опре­де­лен­ное условие:

  • Про­сто ждать опре­де­лен­ное время.
  • Удач­ное соеди­не­ние по сети с узлом.
  • Опре­де­лен­ное содер­жи­мое в файле.
  • Пока не появит­ся или уда­лит­ся файл или папка.

Напри­мер:


* в дан­ном при­ме­ре выпол­не­ние плей­бу­ка оста­но­вит­ся, пока по пути /tmp/build/my_package.rpm не появит­ся файл.

О wait_for https://docs.ansible.com/ansible/2.3/wait_for_module.html.

Установки пакетов, модулей и расширений

В дан­ном раз­де­ле мы кос­нем­ся все­го, что при­во­дит к уста­нов­ке чего бы то ни было. А именно:

  • Уста­нов­ки паке­тов в систему.
  • Загруз­ки исходников.
  • Уста­нов­ке допол­ни­тель­ных модулей.
  • Рас­па­ков­ке архивов.

Рас­смот­рим это подробнее.

1. Установка/удаление паке­тов в систему.

Выпол­ня­ет­ся с помо­щью уни­вер­саль­но­го моду­ля package или с помо­щью спе­ци­а­ли­зи­ро­ван­ных yum и apt. Послед­ние име­ют боль­ше полез­ных опций.

а) package:


* по дан­ной инструк­ции в нашей систе­ме дол­жен быть уста­нов­лен пакет chrony послед­ней версии.

Для уста­нов­ки паке­та кон­крет­ной вер­сии, ее нуж­но ука­зать в назва­нии пакета:


 

О package: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/package_module.html.

б) yum:



О yum: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/yum_module.html.

в) apt:


Если нам нуж­но уста­но­вить пакет из фай­ла deb, син­так­сис будет следующий:

* в дан­ном при­ме­ре мы уста­нав­ли­ва­ем пакет libllvm7 из фай­ла deb, кото­рый досту­пен по url http://ftp.ru.debian.org/debian/pool/main/l/llvm-toolchain-7/libllvm7_7.0.1-8+deb10u2_amd64.deb.

Об apt: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html.

С помо­щью опции state мы можем управ­лять пове­де­ни­ем установки:

  • present — пакет дол­жен быть установлен.
  • latest — долж­на быть уста­нов­ле­на послед­няя версия.
  • absent — пакет дол­жен быть удален.

2. Рабо­та с репо­зи­то­ри­я­ми RPM.

а) Добав­ле­ние репозитория:


О yum_repository: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/yum_repository_module.html.

б) Для уда­ле­ния репо­зи­то­рия исполь­зу­ем модуль yum:


* в нашем при­ме­ре будет уда­лен репо­зи­то­рий с назва­ни­ем pgsql95.

О yum: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/yum_module.html.

3. Рабо­та с репо­зи­то­ри­я­ми DEB.

а) Добав­ле­ние репозитория:


О apt_repository: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_repository_module.html.

б) Для уда­ле­ния репо­зи­то­рия нуж­но вос­поль­зо­вать­ся дирек­ти­вой state со зна­че­ни­ем absent, напри­мер:


* в дан­ном при­ме­ре будет уда­лен репо­зи­то­рий, кото­рый идет по умол­ча­нию для Debian Stretch.

в) Импорт ключа.

Рас­смот­рим при­ме­ры с добав­ле­ни­ем gpg-клю­ча по URL и с сер­ве­ра ключей:


* импорт клю­ча из фай­ла https://www.postgresql.org/media/keys/ACCC4CF8.asc.

* импорт клю­ча с иден­ти­фи­ка­то­ром 648ACFD622F3D138 из сер­ве­ра клю­чей keyserver.ubuntu.com.

О apt_key: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_key_module.html.

4. Уста­нов­ка моду­ля в nodejs.

Уста­нов­ка моду­лей в nodejs выпол­ня­ет­ся с помо­щью npm. Для него в ansible есть отдель­ная функция:


* в дан­ном при­ме­ре будет выпол­не­на уста­нов­ка newman, кото­рая будет доступ­на всем про­ек­там (опция global).

О nodejs npm: https://docs.ansible.com/ansible/latest/collections/community/general/npm_module.html.

5. Уста­нов­ка рас­ши­ре­ний для python.

Исполь­зу­ем модуль pip. Рас­смот­рим несколь­ко примеров.

а) уста­нов­ка паке­та python:


* в дан­ном при­ме­ре будет уста­нов­лен psycopg2.

б) обнов­ле­ние пакетов:


 

* в нашем при­ме­ре будут обнов­ле­ны паке­ты pip и wheel.

в) исполь­зо­вать опре­де­лен­ную вер­сию pip:


 

О pip: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/pip_module.html.

6. Рас­па­ков­ка архива.

Выпол­ня­ет­ся с помо­щью unarchive:


* в дан­ном при­ме­ре мы рас­па­ку­ем исход­ник для nginx в ката­лог /tmp. Обра­ти­те вни­ма­ние на две вещи:
  • Мы исполь­зу­ем пере­мен­ную nginx_ver. Дан­ная пере­мен­ная долж­на быть опре­де­ле­на при запус­ке плей­бу­ка, или в инвен­тар­ном фай­ле, или в var, или в default. Подроб­нее в соот­вет­ству­ю­щем раз­де­ле выше.
  • Опция creates поз­во­лит не выпол­нять опе­ра­цию, если суще­ству­ет файл /tmp/nginx-{{ nginx_ver }}.tar.gz.

Об unarchive: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/unarchive_module.html.

Настройка системы

В дан­ном раз­де­ле мы рас­смот­рим про­цес­сы, кото­рые боль­ше под­хо­дят для кате­го­рии настрой­ки системы.

1. Доба­вить зада­ние в cron.

Выпол­ня­ет­ся с помо­щью моду­ля cron:


 

* в дан­ном при­ме­ре мы созда­дим зада­ние для запус­ка коман­ды /scripts/command.sh каж­дый день, каж­дые 6 часов.

О cron: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/cron_module.html.

2. Доба­вить пуб­лич­ный ключ хоста в known_hosts.

Дела­ет­ся с помо­щью known_hosts. При­мер из офи­ци­аль­ной документации:


* в дан­ном при­ме­ре мы доба­вим ключ из фай­ла pubkeys/foo.com.invalid в /etc/ssh/ssh_known_hosts.

О known_hosts: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/known_hosts_module.html.

3. Созда­ние новых SSH-клю­чей для сервера.

Созда­ние клю­чей реа­ли­зу­ет­ся с помо­щью моду­ля openssh_keypair:


* в дан­ном при­ме­ре мы созда­дим 4 клю­ча раз­ных типов: dsa, ecdsa, ed25519, rsa. Так как у каж­до­го из них свои тре­бо­ва­ния к раз­ме­ру, пере­чень пред­став­лен в виде дву­мер­но­го мас­си­ва. Клю­чи будут созда­ны в ката­ло­ге /etc/ssh/.

О openssh_keypair: https://docs.ansible.com/ansible/latest/collections/community/crypto/openssh_keypair_module.html.

4. Рабо­та с SSH authorized_key.

Дан­ный файл содер­жит пуб­лич­ный ключ для под­клю­че­ния по SSH. Рабо­та с ним через Ansible выпол­ня­ет­ся с помо­щью моду­ля authorized_key.

При­мер добав­ле­ния ключа:


* в дан­ном при­ме­ре мы берем содер­жи­мое фай­ла files/key.pub и уста­нав­ли­ва­ем его для поль­зо­ва­те­ля root.

Об authorized_key: https://docs.ansible.com/ansible/2.4/authorized_key_module.html.

5. Созда­ние учет­ной записи.

Для это­го есть модуль user. У него мно­го опций, рас­смот­рим неко­то­рые из них.

а) Про­стая учет­ная запись:


* в дан­ном при­ме­ре мы созда­дим поль­зо­ва­те­ля user1 с домаш­ней дирек­то­ри­ей. Так­же мы ука­за­ли для исполь­зо­ва­ния команд­ную обо­лоч­ку /bin/bash.

б) Для созда­ния систем­ной учет­ной запи­си нам достаточно:

* в дан­ном при­ме­ре будет созда­на учет­ная запись consul.

О user: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/user_module.html.

6. Рабо­та с systemd.

Для дан­ной настрой­ки есть одно­имен­ный модуль systemd. Рас­смот­рим вари­ан­ты его использования.

а) пере­чи­тать кон­фи­гу­ра­цию (необ­хо­ди­мо делать каж­дый раз, когда мы меня­ем настрой­ки юнита):


б) раз­ре­шить сер­вис (авто­за­пуск):

* для сер­ви­са mysql.

в) пере­за­пу­стить сервис:


г) оста­но­вить сервис:

О systemd: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/systemd_module.html.

7. Настрой­ка брандмауэра.

Выпол­ня­ет­ся раз­ны­ми моду­ля­ми в зави­си­мо­сти от исполь­зу­е­мой систе­мы управ­ле­ния netfilter:

  • firewalld
  • iptables
  • ufw

Рас­смот­рим неболь­шие примеры.

а) firewalld:


Подроб­нее: https://docs.ansible.com/ansible/latest/collections/ansible/posix/firewalld_module.html.

б) iptables:


Подроб­нее: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/iptables_module.html.

в) UFW.

Доба­вить 80 порт:


Доба­вить пор­ты с циклом:

Подроб­нее: https://docs.ansible.com/ansible/latest/collections/community/general/ufw_module.html.

Работа с папками и файлами

Рас­смот­рим зада­чи, кото­рые помо­гут нам созда­вать, копи­ро­вать и рабо­тать с файлами.

1. Созда­ние ката­ло­гов и файлов.

Созда­ние фай­лов и ката­ло­гов выпол­ня­ет­ся с помо­щью моду­ля file.

а) для ката­ло­га в каче­стве state ука­зы­ва­ем directory:


* в дан­ном при­ме­ре мы созда­дим 2 ката­ло­га: site1 и site2 в ката­ло­ге /var/www.

б) для созда­ния фай­ла уби­ра­ем опцию state (или даем ей зна­че­ние touch):


* в дан­ном при­ме­ре мы созда­ним файл index.php в ката­ло­ге /var/www/site1.

в) для созда­ния сим­лин­ка исполь­зу­ем state со зна­че­ни­ем link:


О file: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/file_module.html.

2. Задать права.

Это мож­но выпол­нить с помо­щью моду­ля созда­ния фай­ла или каталога:


* обра­ти­те вни­ма­ние, это при­мер из преды­ду­ще­но раз­де­ла. Для создан­но­го фай­ла мы про­сто немно­го изме­ни­ли права.

3. Копи­ро­ва­ние фай­лов из каталога.

Для копи­ро­ва­ния дан­ных мы исполь­зу­ем модуль copy:


* в дан­ном при­ме­ре мы про­чи­та­ем все содер­жи­мое ката­ло­га files на ком­пью­те­ре с ansible, и ско­пи­ру­ем его в ката­лог /etc/ssl/test на целе­вом компьютере.

О copy: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/copy_module.html.

4. Исполь­зу­ем шаблон.

Копи­ро­ва­ние из шаб­ло­на отли­ча­ет­ся от копи­ро­ва­ния из фай­ла тем, что в шаб­лоне могут исполь­зо­вать­ся пере­мен­ные, кото­рые будет заме­нять­ся их зна­че­ни­я­ми в момент копи­ро­ва­ния. Для само­го про­цес­са копи­ро­ва­ния из шаб­ло­на исполь­зу­ет­ся модуль template:


* в дан­ном при­ме­ре мы возь­мом шаб­лон templates/consul/config.json.j2 на ком­пью­те­ре ansible и раз­ме­стим его в по пути /etc/consul.d/config.json на целе­вом компьютере.

Мы можем выве­сти в кон­соль резуль­тат обра­бот­ки шаб­ло­на сле­ду­ю­щим образом:


О template: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html.

5. Архи­ви­ро­ва­ние.

Создать архив из фай­ла или ката­ло­га мож­но с помо­щью моду­ля archive:


* в дан­ном при­ме­ре мы созда­дим архив из ката­ло­га /etc/raddb и сохра­ним его в файл /tmp/raddb.gz.

О archive: https://docs.ansible.com/ansible/latest/collections/community/general/archive_module.html.

Для рас­па­ков­ки архи­вов исполь­зу­ет­ся модуль unarchive, о кото­ром мы гово­ри­ли выше.

6. Поиск фай­лов и папок.

Выпол­ня­ет­ся с помо­щью моду­ля find. Осо­бый инте­рес пред­став­ля­ет в кон­тек­сте поис­ка фай­лов и выпол­не­ние над ними опре­де­лен­ных дей­ствий. Раз­бе­рем несколь­ко примеров.

а) Уда­лить послед­ние 30 фай­лов. Зада­ча реша­ет­ся в два этапа:

  • ищем содер­жи­мое целе­во­го каталога.
  • сотри­ту­ем спи­сок най­ден­ных по вре­ме­ни изме­не­ния фай­лов и уда­ля­ем все, что идут после опре­де­лен­но­го чис­ла объектов.

Поиск выпол­ня­ем с помо­щью моду­ля find, уда­ле­ние — file:


 

* в дан­ном при­ме­ре мы ищем фай­лы в ката­ло­ге /backup, после чего сор­ти­ру­ем най­ден­ное и уда­ля­ем по спис­ку все фай­лы, кото­рые идут после 30-го.

б) Уда­ле­ние архи­вов для логов. Так­же выпол­ня­ем в два эта­па — более слож­ный поиск и уда­ле­ние с помо­щью file:


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

О find: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/find_module.html.

7. Ска­чать файл с помо­щью curl.

Для это­го исполь­зу­ет­ся модуль uri. Про­стой пример:


* в дан­ном при­ме­ре мы загру­зим файл https://www.test.ru/files/winsetupfromusb.zip в ката­лог /tmp.

При­мер посложнее:


* в дан­ном при­ме­ре мы ска­ча­ем файл с ресур­са, где тре­бу­ет­ся аутен­ти­фи­ка­ция по токе­ну, кото­рый пере­да­ет­ся в заго­лов­ке. Заго­лов­ки мы пере­да­ем с помо­щью пара­мет­ра headers. Так­же мы зада­ем пра­ва на загру­жен­ный файл и дела­ем в каче­стве вла­дель­ца поль­зо­ва­те­ля и груп­пу test.

Об uri: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/uri_module.html.

8. Созда­ние вре­мен­ных фай­лов и папок.

Ино­гда, для рабо­ты нуж­но вре­мен­ное хра­ни­ли­ще фай­лов, кото­рое после рабо­ты мож­но будет уда­лить. Для рабо­ты с дан­ным хра­ни­ли­шем в ansible мож­но исполь­зо­вать модуль tempfile.

При­мер созда­ния каталога:


 

Путь до создан­но­го ката­ло­га будет запи­сан в пере­мен­ную tmp.path.

После не забы­ва­ем уда­лить создан­ную папку:


О tempfile: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/tempfile_module.html.

9. Рабо­та с GIT.

О Git: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/git_module.html.

а) Кло­ни­ро­ва­ние про­ек­та из гита.

Выпол­ня­ет­ся с помо­щью моду­ля git.


 

* в дан­ном при­ме­ре мы сде­ла­ем клон репо­зи­то­рия в ката­лог /tmp/docker-compose.

б) Изме­не­ние содер­жи­мо­го для файла.

Модуль ansible не под­дер­жи­ва­ет отпав­ку изме­не­ний в git, но мож­но вос­поль­зо­вать­ся API. Напри­мер, для gitlab обно­вить кон­тент фай­ла мож­но с помо­щью моду­ля uri:


* где:
  • url — пол­ный путь до фай­ла. Обра­ти­те вни­ма­ние на: 
    • gitlab.test.ru — адрес наше­го сер­ве­ра gitlab.
    • 666 — иден­ти­фи­ка­тор про­ек­та. Его мож­но посмот­реть в настрой­ках само­го проекта.
    • folder%2Ffilename — путь до фай­ла. В нор­маль­ном фор­ма­те, это folder/filename.
  • body — содер­жит дан­ные, кото­рые будут отправ­ле­ны на сер­вер для сме­ны контента.
  • headers PRIVATE-TOKEN токен досту­па к API. Его мож­но создать в настрой­ках про­фи­ля учет­ной запи­си Gitlab.

Подроб­нее о рабо­те API в гибла­бе: https://docs.gitlab.com/ee/api/repository_files.html.

Содержимое файла

С помо­щью Ansible мы можем менять содер­жи­мое строк как в фай­лах — встав­лять, уда­лять и редак­ти­ро­вать, так и полу­чен­ных резуль­та­тах. Рас­смот­рим несколь­ко примеров.

1. Про­смотр содер­жи­мо­го файла.

Содер­жи­мое фай­лов на уда­лен­ном сер­ве­ре и на сто­роне ansible про­смат­ри­ва­ет­ся по-раз­но­му. Рас­смот­рим оба варианта.

а) На ansible.

Что­бы уви­деть содер­жи­мое фай­ла, кото­рый лежит на сто­роне ansible, исполь­зу­ем пла­гин lookup:


* в дан­ном при­ме­ре мы про­сто пока­жем содер­жи­мое фай­ла /etc/ntp.conf.

О lookup: https://docs.ansible.com/ansible/latest/plugins/lookup.html.

б) На уда­лен­ном сервере.

Выпол­ня­ет­ся в два эта­па — про­смотр содер­жи­мо­го с сохра­не­ни­ем резуль­та­та в пере­мен­ную, и вывод содер­жи­мо­го переменной:


 

2. Заме­ны стро­ки в файлах.

Заме­ны выпол­ня­ют­ся с помо­щью моду­ля replace. Рас­смот­рим несколь­ко примеров.

а) Про­стой поиск и заме­на строки:


* в дан­ном при­ме­ре мы добав­ля­ем ком­мен­та­рий к стро­ке server.address=127.0.0.1.

б) Заме­на с под­ста­нов­кой най­ден­но­го содержимого:


 

* в дан­ном при­ме­ре мы нахо­дим стро­ку server.address с любым зна­че­ни­ем и меня­ем ее на стро­ку, в кото­рой будет про­пи­са­но это значение.

О replace: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/replace_module.html.

3. Добав­ле­ние и уда­ле­ние строк в файле.

Мы можем менять содер­жи­мое фай­ла с помо­щью моду­ля lineinfile. Рас­смот­рим неко­то­рые при­ме­ры рабо­ты с ним.

а) Уда­лить строку:


 

* в дан­ном при­ме­ре мы уда­лим все стро­ки из фай­ла /etc/chrony.conf, кото­рые начи­на­ют­ся на server.

б) Доба­вить строку:


* в дан­ном при­ме­ре мы доба­вим стро­ку server ntp.server.local в файл /etc/chrony.conf. Если дан­ная запись уже есть в фай­ле, ansible ниче­го не ста­нет менять.

в) Доба­вить стро­ку с исполь­зо­ва­ни­ем регу­ляр­но­го выражения:


* при­мер взят с офи­ци­аль­но­го сай­та. В дан­ном слу­чае мы гаран­ти­ру­ем нали­чие стро­ки SELINUX=enforcing — либо для дирек­ти­вы SELINUX будет зада­но опре­де­лен­ное зна­че­ние, либо стро­ка будет пол­но­стью встав­ле­на в кон­фи­гу­ра­ци­он­ный файл /etc/selinux/config.

О lineinfile: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/lineinfile_module.html.

4. Раз­бить строку.

Выпол­ня­ет­ся с помо­щью мето­да split. Рас­смот­рим при­мер, когда мы реги­стри­ру­ем резуль­тат при выпол­не­нии коман­ды, после чего мы разо­бьем его по предложениям:


* в дан­ном при­ме­ре мы полу­чим мас­сив дан­ных из предложений.

5. Доба­вить блок текста.

Мож­но сде­лать с помо­щью моду­ля blockinfile:


 

О blockinfile: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/blockinfile_module.html.

6. Сохра­нить содер­жи­мое пере­мен­ной в файл.

Выше мы рас­смат­ри­ва­ли модуль copy для копи­ро­ва­ния фай­лов. Мы так­же можем его исполь­зо­вать для поме­ще­ния содер­жи­мо­го пере­мен­ной в файл:


 

* в дан­ном при­ме­ре содер­жи­мое пере­мен­ной my_variable будет сохра­не­но в фай­ле /tmp/data.txt.

Виртуализация VMware

Рабо­та с вир­ту­аль­ны­ми маши­на­ми на плат­фор­ме VMware выпол­ня­ет­ся с помо­щью боль­шо­го коли­че­ства раз­лич­ных моду­лей vmware. С пол­ным их спис­ком мож­но озна­ко­мить­ся на стра­ни­це https://docs.ansible.com/ansible/latest/collections/community/vmware/index.html.

Мы рас­смот­рим несколь­ко при­ме­ров. В них мы будем исполь­зо­вать сле­ду­ю­щие переменные:

  1. vcenter_hostname — имя сер­ве­ра vcenter или его IP-адрес.
  2. vcenter_username — логин для под­клю­че­ния к vcenter.
  3. vcenter_password — пароль для под­клю­че­ния. Луч­ше хра­нить с исполь­зо­ва­ни­ем vailt.
  4. vcenter_datacenter — имя дата цен­тра, к кото­ро­му нуж­но подключаться.

Дан­ные пере­мен­ные необ­хо­ди­мо опре­де­лить зара­нее. Это мож­но сде­лать при вызо­ве плей­бу­ка, опи­сать в самом сце­на­рии или роли. Подроб­нее о рабо­те с пере­мен­ны­ми смот­ри­те раз­дел выше.

1. Модуль vmware_guest

С его помо­щью мы можем вза­и­мо­дей­ство­вать с госте­вой опе­ра­ци­он­ной систе­мой вир­ту­аль­ной маши­ны. Необ­хо­ди­мо поза­бо­тить­ся, что­бы на послед­ней были уста­нов­ле­ны VMware Tools.

Подроб­нее о vmware_guest: https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_guest_module.html.

а) Базо­вое подключение.

Для выпол­не­ния дей­ствий над вир­ту­аль­ны­ми маши­на­ми мы долж­ны под­клю­чить­ся к хосту VMware. Для это­го исполь­зу­ем дан­ные строки:


 

* пара­метр validate_certs, выстав­лен­ный в no, поз­во­лит избе­жать ошиб­ки, если у нас на хосте исполь­зу­ет­ся само­под­пи­сан­ный сер­ти­фи­кат (как пра­ви­ло, так и есть).

б) Пере­име­но­вать вир­ту­аль­ную машину.

Для выпол­не­ния дей­ствия нам нуж­но знать иден­ти­фи­ка­тор вир­ту­аль­ной машины:


 

* где uuid — иден­ти­фи­ка­тор вир­ту­аль­ной маши­ны; name — новое имя вир­ту­аль­ной машины.

в) Кон­вер­ти­ро­вать вир­ту­аль­ную маши­ну в шаблон.

Для это­го нуж­но про­сто задать при­знак is_template:


2. Модуль vmware_vm_info

Поз­во­ля­ет соби­рать инфор­ма­цию о вир­ту­аль­ных маши­нах на хосте VMware.

О моду­ле vmware_vm_info: https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_vm_info_module.html.

а) Полу­че­ние инфор­ма­ции по всем вир­ту­аль­ным маши­нам на хосте:


* всю инфор­ма­цию мы запи­шем в пере­мен­ную vminfo и выве­дем ее на экран.

б) Что­бы полу­чить инфор­ма­цию не о всех маши­нах, а о кон­крет­ной, то добав­ля­ем опцию vm_name:


 

3. Модуль vmware_guest_info

Поз­во­ля­ет полу­чить подроб­ную инфор­ма­цию о вир­ту­аль­ной машине.

О моду­ле vmware_guest_info: https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_guest_info_module.html.

а) Полу­че­ние инфор­ма­ции о кон­крет­ной вир­ту­аль­ной машине:


 

* инфор­ма­ция будет собра­на для вир­ту­аль­ной маши­ны с име­нем, кото­рое опре­де­ле­но в пере­мен­ной vm_name.

4. Модуль vmware_guest_powerstate

Поз­во­ля­ет вклю­чать, выклю­чать и пере­за­гру­жать вир­ту­аль­ные машины.

О моду­ле vmware_guest_powerstate: https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_guest_powerstate_module.html.

а) Вклю­че­ние вир­ту­аль­ной машины:


 

б) Выклю­че­ние вир­ту­аль­ной машины:


 

Или гру­бо:


 

в) Пере­за­груз­ка:


Или грубо:

 

Виртуализация Proxmox

Рас­смот­рим немно­го при­ме­ров по рабо­те с систе­мой вир­ту­а­ли­за­ции proxmox.

Для рабо­ты с дан­ной систе­мой вир­ту­а­ли­за­ции есть в Ansible спе­ци­а­ли­зи­ро­ван­ные моду­ли, но, пока, их мало. Поэто­му часть задач реша­ет­ся с помо­щью API.

1. Сбор информации

В дан­ном раз­де­ле мы рас­смот­рим при­ме­ры для полу­че­ния инфор­ма­ции с Proxmox.

а) Спи­сок вир­ту­аль­ных машин на хосте. Для полу­че­ния инфор­ма­ции о вир­ту­аль­ных маши­нах, кото­рые нахо­дят­ся на хосте вир­ту­а­ли­за­ции нуж­но исполь­зо­вать API Proxmox. Для это­го в ansible мы будем при­ме­нять модуль URI:


 

* где:

  • pve.test.local — адрес веб-интер­фей­са сер­ве­ра виртуализации.
  • 8006 — порт для под­клю­че­ния в веб-интерфейсу.
  • ansible@pve — имя учет­ной запи­си, для кото­рой создан токен досту­па. Он созда­ет­ся в кон­со­ле управ­ле­ния Proxmox (Дата­центр - Раз­ре­ше­ния - API Tokens).
  • Ansible=e94d5627-1f8d-36a7-37e2-7ad6fad65ab7 — имя токе­на и сам токен.

В резуль­та­те мы сохра­ним спи­сок вир­ту­аль­ных машин в пере­мен­ной vm_list.

б) Подроб­ная инфор­ма­ция о вир­ту­аль­ной машине или хосте вир­ту­а­ли­за­ции. Если мы хотим собрать поболь­ше инфор­ма­ции, нам так­же пона­до­бит­ся API:


Для полу­че­ния раз­лич­ной инфор­ма­ции о вир­ту­аль­ной машине или сер­ве­ре мы исполь­зу­ем раз­лич­ные запро­сы (отме­че­но как <запрос>). С пол­ным спис­ком того, что мы можем полу­чить пред­ла­гаю озна­ко­мить­ся на стра­ни­це pve.proxmox.com/pve-docs/api-viewer.

Напри­мер, для полу­че­ния кон­фи­гу­ра­ции вир­ту­аль­ной маши­ны, исполь­зу­ем запрос:


Что­бы узнать hostname:

И так далее

Работа с Docker

В дан­ном раз­де­ле мы рас­смот­рим рабо­ту с кон­тей­не­ра­ми Docker.

1. Рабо­та с контейнером.

С помо­щью моду­ля docker_container мы можем рабо­тать с кон­тей­не­ра­ми. Созда­вать их, уда­лять, пере­за­пус­кать и так далее. Рас­смот­рим несколь­ко примеров.

а) Создать новый кон­тей­нер мож­но с помо­щью сценария:


 

* в дан­ном при­ме­ре будет создан кон­тей­нер с име­нем new_container из обра­за nginx.

Созда­ние кон­тей­не­ра с боль­шим коли­чист­вом опций:


 

* где:

  • auto_remove — уда­лить кон­тей­нер после завер­ше­ния его работы.
  • volumes — созда­ем volume. В дан­ном слу­чае, про­брос ката­ло­га /tmp внутрь кон­тей­не­ра тоже в /tmp.
  • ports — на каком пор­ту долж­на слу­шать хосто­вая систе­ма и на какой порт про­бра­сы­вать запро­сы внутрь контейнера.

б) Для уда­ле­ния используем:


 

* важ­но ука­зать имя кон­тей­не­ра и state со зна­че­ни­ем absent.

О docker_container: https://docs.ansible.com/ansible/2.9/modules/docker_container_module.html.

2. Запуск коман­ды внут­ри контейнера.

Дан­ное дей­ствие экви­ва­лент­но коман­де docker exec. В ansible мож­но выпол­нить с помо­щью моду­ля docker_container_exec или ранее рас­смот­рен­но­го docker_container. Рас­смот­рим оба варианта.

а) docker_container_exec.

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


 

При­мер использования:


 

* в нашем при­ме­ре мы ска­ча­ем ути­ли­ту wp-cli в кон­тей­не­ре wordpress. Коман­да будет запу­ще­на в дирек­то­рии chdir.

О docker_container_exec: https://docs.ansible.com/ansible/latest/collections/community/docker/docker_container_exec_module.html.

б) docker_container.

Дан­ный спо­соб подой­дет в слу­чае, когда нам нуж­но создать кон­тей­нер, запу­стить его и выпол­нить коман­ду. Например:


 

Работа с базами данных

В дан­ном бло­ке рас­смот­рим при­ме­ры рабо­ты с раз­лич­ны­ми база­ми данных.

MySQL

Рабо­та с базой дан­ных воз­мож­на с помо­щью кол­лек­ции mysql.mysql_db. Она не идет в ком­плек­те к ansible и нам необ­хо­ди­мо ее уста­но­вить командой:


 

О mysql_db: https://docs.ansible.com/ansible/latest/collections/community/mysql/mysql_db_module.html.

Резерв­ное копи­ро­ва­ние базы дан­ных MySQL/MariaDB:


 

* в дан­ном при­ме­ре мы созда­дим 2 дам­па из баз db1 и db2 и сохра­ним резуль­тат в файл /tmp/dump.sql.

PostgreSQL

Рас­смот­рим раз­ные при­ме­ры рабо­ты с СУБД PostgreSQL. Мы будем исполь­зо­вать сле­ду­ю­щие модули:

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


 

Подроб­нее мож­но най­ти инструк­цию по ссыл­ке выше.

1. Резерв­ное копирование:


 

2. Вос­ста­нов­ле­ние из дампа:

Сце­на­рий напо­ми­на­ет резерв­ное копи­ро­ва­ние, за исклю­че­ни­ем опции state:


 

3. Выпол­не­ние запроса:

Запрос в базу мож­но сде­лать с помо­щью моду­ля postgresql_query:


 

Рас­смот­рим кон­крет­ный при­мер с сохра­не­ни­ем резуль­та­та в переменную:


 

* что мы сделали:

  • под­клю­чи­лись к сер­ве­ру баз дан­ных на локаль­ном сер­ве­ре под поль­зо­ва­те­лем dbuser с паро­лем dbpassword.
  • сде­ла­ли запрос к базе clients. Запрос дол­жен полу­чить спи­сок всех запи­сей из таб­ли­цы users, где в поле name есть зна­че­ние Andrey или в поле family — Ivanov. Обра­ти­те вни­ма­ние, что мы исполь­зо­ва­ли пат­тер­ны с при­ме­не­ни­ем positional_args.
  • резуль­тат рабо­ты зада­чи мы зафик­си­ро­ва­ли в пере­мен­ной myclients.
  • созда­ли пере­мен­ную my_clients, куда занес­ли резуль­ты­ты выборки.

Запуск плейбука

Рас­смот­рим отдель­но неко­то­рые воз­мож­но­сти при запус­ке плейбука.

1. Начи­нать выпол­не­ние с опре­де­лен­ной задачи.

При выпол­не­нии отлад­ки, полез­но запу­стить плей­бук, но начать выпол­не­ние с опре­де­лен­ной зада­чи. Осталь­ные пропустить.

Это мож­но сде­лать с помо­щью опции --start-at-task:


 

* в дан­ном при­ме­ре плей­бук нач­нет выпол­нять зада­ния с зада­чи Start Job.

2. Пере­да­ча переменных.

Мы можем опре­де­лить пере­мен­ные при запус­ке плей­бу­ка с помо­щью опции --extra-vars. Воз­мож­на пере­да­ча одной или несколь­ких переменных:


 

3. Исполь­зо­ва­ние тегов.

Теги поз­во­ля­ют про­пус­кать или выпол­нять опре­де­лен­ные зада­ния. Пред­по­ло­жим, у нас есть задание:


 

* в дан­ном при­ме­ре мы наве­си­ли тег init postfix на зада­ние Install postfix.

Теперь мы можем запу­стить плейбук:


 

И зада­ние будет выполнено.

Или так:


 

И зада­ние будет проигнорировано.

О тегах: https://docs.ansible.com/ansible/latest/user_guide/playbooks_tags.html.

Обработка ошибок

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

1. Игно­ри­ро­вать ошибки.

Если ansible столк­нет­ся с ошиб­кой при выпол­не­нии зада­чи, рабо­та плей­бу­ка будет завер­ше­на. Ино­гда, нуж­но про­пу­стить ошиб­ку при выпол­не­нии опре­де­лен­ной зада­чи, что­бы выпол­не­ние было про­дол­же­но. Для это­го суще­ству­ет опция ignore.

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


 

б) что­бы игно­ри­ро­вать ошиб­ки при под­клю­че­нии к хосту:


 

2. Выбра­сы­вать ошиб­ку по сво­е­му условию.

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

а) выки­нуть ошиб­ку, если пере­мен­ная не рав­на опре­де­лен­но­му значению:


 

* в дан­ном при­ме­ре если пере­мен­ная my_dataload_status не рав­на complete, системв вер­нет ошибку.

б) ошиб­ка, если пере­мен­ная содер­жит запре­щен­ный символ:


 

О fail: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/fail_module.html.

3. Дей­ствия в слу­чае ошибки.

Рас­смот­рим 2 варианта:

  1. Мы, в любом слу­чае, хотим выпол­нить чать задачь, даже если сце­на­рий пре­кра­тит рабо­ту из-за ошибки.
  2. Мы хотим выпол­нить опре­де­лен­ное дей­ствие, если сце­на­рий пре­кра­тит рабо­ту из-за ошибки.

Это воз­мож­но реа­ли­зо­вать с помо­щью block + rescue/always.

О blocks: https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_blocks.html.

Рас­смот­рим пример.

а) Выпол­нять зада­чу после rescue, если основ­ной сце­на­рий выпол­нил­ся с ошибкой:


 

б) Выпол­нять зада­чу после always неза­ви­си­мо от резуль­та­та выпол­не­ния основ­но­го сценария:


 

в) И то и другое:


 

* в дан­ном при­ме­ре мы отоб­ра­зим текст Show error толь­ко при нали­чии ошиб­ки, а так­же выпол­ним коман­ду rm -f /tmp/test неза­ви­си­мо от исхо­да рабо­ты сценария.

Работа с выводом и строками

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

Конвертация типов данных

От типа дан­ных зави­сят опе­ра­ции и спо­со­бы рабо­ты с ними. При­ве­ду при­мер типов дан­ных, с кото­ры­ми мы можем столкнуться:

  • Стро­ка — набор сим­во­лов. Заклю­ча­ют­ся в кавычки.
  • Чис­ло — набор цифр. Запи­сы­ва­ют­ся без кавычек.
  • Спи­сок (list) — мас­сив дан­ных. В каче­стве клю­ча исполь­зу­ет­ся про­ну­ме­ро­ван­ный индекс. Запи­сы­ва­ют­ся в квад­рат­ных скобках.
  • Сло­варь (dict) — мас­сив дан­ных. В каче­стве клю­ча исполь­зу­ет­ся сим­вол­ный индекс. Запи­сы­ва­ют­ся в фигур­ных скобках.
  • JSON стро­ка, име­ю­щая фор­мат запи­си в виде сло­ва­ря. Счи­та­ет­ся удоб­ным для пере­да­чи дан­ных меж­ду системами.
  • YAML стро­ка, в кото­рой дан­ные раз­де­ля­ют­ся пере­но­са­ми, а вло­жен­ность опре­де­ля­ет­ся отсту­па­ми. Фор­мат стро­го зави­сит от послед­них — лиш­ний про­бел нару­ша­ет его обра­бот­ку. Удоб­но исполь­зо­вать для нагляд­но­го выво­да информации.

Рас­смот­рим при­ме­ры пре­об­ра­зо­ва­ний дан­ных из одно­го типа в другой.

1. Стро­ку в чис­ло. Пре­об­ра­зу­ем с помо­щью int:

"100" | int

2. Спи­сок в json. Мож­но выпол­нить с помо­щью to_json:

list_var | to_json

или в json, кото­рый будет удоб­но читать:

list_var | to_nice_json

Преобразование строк

Полу­чен­ные стро­ки могут тре­бо­вать даль­ней­ше­го раз­бо­ра, напри­мер, пар­син­га. Рас­смот­рим несколь­ко примеров.

1. Полу­че­ние мас­си­ва с помо­щью split.

Метод split поз­во­ля­ет раз­бить стро­ку на части по опре­де­лен­но­му сим­во­лу. Полу­чен­ные части ста­нут зна­че­ни­я­ми массива:


 

* в дан­ном при­ме­ре мы разо­бьем стро­ку из пере­мен­ной results на части по двое­то­чию и сохра­ним все это в пере­мен­ную newvar.

2. Заме­ны с помо­щью replace.

С помо­щью дан­ной функ­ции мож­но в нашем выво­де заме­нить одни сим­во­лы другими:


 

* тут в пере­мен­ную newvar будет запи­са­на стро­ка results, из кото­рой мы убе­рем все пробелы.

Работа с массивами

В дан­ном под­раз­де­ле будем рабо­тать со спис­ка­ми и словарями.

1. Объ­еди­не­ние.

Пред­по­ло­жим, что у нас есть два спис­ка из словарей:


 

Мы можем их объ­еди­нить по одно­му из полей, напри­мер, name:


 

* для lists_mergeby обя­за­тель­но пол­ное напи­са­ние — community.general.lists_mergeby.

В резуль­та­те, мы полу­чим такой массив:


 

* как видим, два мас­си­ва сли­лись в один. Для клю­ча name1 так­же добав­ле­но поле value2.

О lists_mergeby: https://docs.ansible.com/ansible/devel/collections/community/general/lists_mergeby_filter.html.

2. Пре­об­ра­зо­ва­ние эле­мен­та спис­ка в словарь.

Пред­по­ло­жим, у нас есть:


 

Мы можем его пре­об­ра­зо­вать в сло­варь по име­ю­щим­ся клю­чам (key).

а) Если имя клю­ча для клю­ча key, а имя клю­ча для зна­че­ния value:


 

Мы долж­ны уви­деть что-то на подобие:


 

б) Если имя клю­ча для клю­ча НЕ key, а имя клю­ча для зна­че­ния НЕ value:


 

* в дан­ном при­ме­ре пред­по­ла­га­ет­ся, что у нас есть мас­сив с клю­ча­ми name и model. Мы хотим, что­бы в каче­стве клю­ча для созда­ва­е­мо­го сло­ва­ря исполь­зо­вал­ся пер­вый, а для зна­че­ния второй.

О items2dict: https://docs.ansible.com/ansible/devel/collections/ansible/builtin/items2dict_filter.html.

Формат JSON

Посмот­рим, что мы можем сде­лать с json.

1. Фильтр дан­ных. С помо­щью json_query мы можем выби­рать толь­ко те дан­ные, кото­рые соот­вет­ству­ют кри­те­рию поиска.

а) один критерий:


 

* в дан­ном при­ме­ре мы сде­ла­ем выбор­ку всех дан­ных, где зна­че­ние поля state рав­но running.

б) два критерия:


 

* в дан­ном при­ме­ре мы доба­ви­ли кри­те­рий code==`200`. Обра­ти­те вни­ма­ние, что в нем мы исполь­зу­ем дру­гой тип кавы­чек. Это сде­ла­но не про­сто так — для строк исполь­зу­ют­ся кавыч­ки " или ', для цифр — `.

в) фильтр с выбо­ром кон­крет­ных полей:


 

* в этои при­ме­ре мы взя­ли ранее исполь­зо­вав­шей­ся фильр и пере­чис­ли­ли кон­крет­ные поля, кото­рые мы хотим пока­зать при выво­де информации.

Шаблоны Jinja2

Поми­мо сце­на­рия с зада­ни­я­ми, при авто­ма­ти­за­ции силь­но помо­га­ет шаб­ло­ни­за­тор Jinja2. С его помо­щью мож­но на лету фор­ми­ро­вать файл с любым содер­жи­мым, кото­рое долж­но быть сфор­ми­ро­ва­но дина­ми­че­ски. Так­же шаб­ло­ны jinja мож­но при­ме­нять в самих зада­ни­ях. Рас­смот­рим это на несколь­ких примерах.

1. Пере­бор массива.

Пред­по­ло­жим, нам нуж­но пере­брать все эле­мен­ты мас­си­ва в шаб­лоне. Это мож­но сде­лать конструкцией:


 

* в дан­ном при­ме­ре мы сде­ла­ем пере­бор по пере­мен­ной my_hosts. Для каж­до­го эле­мен­та мас­си­ва будет созда­на стро­ка со зна­че­ни­ем server <зна­че­ние пере­мен­ной>.

2. Зада­ем зна­че­ние пере­мен­ной с IF.

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


* мы рас­смот­ре­ли сле­ду­ю­щие варианты:
  • var1 — если есть пере­мен­ная hostname, то добав­ля­ем к test еще и -value. В ито­ге, полу­чит­ся test-value.
  • var2 — если есть пере­мен­ная hostname, зна­че­ние будет value1, в про­тив­ном слу­чае — value2.
  • var3 — если пере­мен­ная hostname рав­на myhost, зада­ем для var3 зна­че­ние value1, в про­тив­ном слу­чае — value2.
  • var4 — дру­гой фор­мат запи­си. Если есть пере­мен­ная hostname, зна­че­ние будет value1, в про­тив­ном слу­чае — value2.

Разное

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

1. Шиф­ро­ва­ние строки.

С помо­щью ansible-vault мы можем шиф­ро­вать фай­лы и пап­ки. Это поз­во­лит нам хра­нить сек­ре­ты не в откры­том виде. Дан­ные рас­шиф­ро­вы­ва­ют­ся в момент выпол­не­ния задач.

Дан­ной коман­дой мы полу­ча­ем шиф­ро­ван­ную строку:


Систе­ма запро­сит вве­сти два­жды пароль и пред­ло­жит вве­сти стро­ку, кото­рую нуж­но зашиф­ро­вать. После мы долж­ны нажать 2 раза Ctrl + D — мы полу­чим стро­ку, кото­рая начи­на­ет­ся с !Vault и раз­лич­ные символы.

Для того, что­бы в момент выпол­не­ния зада­чи ansible рас­шиф­ро­вал дан­ные, при запус­ке плей­бу­ка мы долж­ны ука­зать ключ --ask-vault-pass:


Об ansible-vault: https://docs.ansible.com/ansible/latest/user_guide/vault.html.

2. Завер­шить выпол­не­ние плей­бу­ка после опре­де­лен­ной задачи.

С помо­щью дан­ной конструкции:


 

Мы можем пол­но­стью оста­но­вить выпол­не­ние задач для хоста:


 

О meta: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/meta_module.html.

3. Зави­си­мые роли.

С помо­щью фай­ла meta/main.yml в роли мы можем опре­де­лить пред-роль, от кото­рой зави­сит выпол­не­ние теку­щей роли. Для это­го настра­и­ва­ет­ся опция dependencies:


 

4. Встав­ка роли и ее задач.

Поз­во­ля­ет в про­цес­се выпол­не­ния зада­чи под­клю­чить роль. Дела­ет­ся при помо­щи include_role:


 

А это при­мер, как под­клю­чить роль и сде­лать так, что­бы все ее зада­чи выпол­ни­лись на опре­де­лен­ном хосте:


 

Еще один спо­соб вклю­чить роль, но выпол­нить зада­ния из дру­го­го файла:


 

* в дан­ном при­ме­ре будут исполь­зо­вать файл не main.yml, а new_roles.yml.

5. Повто­ры и цик­лы при выпол­не­нии задач.

Мы можем управ­лять цик­лич­но­стью выпол­не­ния задач с помо­щью раз­лич­ных моду­лей ansible. Рас­смот­рим их на примерах.

а) Повтор­ный запуск задачи.

Выпол­ня­ет­ся с помо­щью retries (коли­чист­во повто­ров) и delay (задерж­ка в секун­дах). Напри­мер, мож­но еще раз запу­стить зада­чу при воз­ник­но­ве­нии ошибки:


 

* в дан­ном при­ме­ре мы будем выпол­нять коман­ду /foo/bar/cmd пока ее выпол­не­ние не закон­чит­ся без оши­бок. Коли­че­ство повто­ров будет равен 3 с интер­ва­лом в 60 секунд.

Неболь­шой при­мер на стра­ни­це https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#retrying-a-task-until-a-condition-is-met.

б) Исполь­зо­ва­ние циклов.

Выше в при­ме­рах мы часто упо­ми­на­ли loop для цик­лич­но­го запус­ка задач в раз­ны­ми вари­ан­та­ми выпол­не­ния, например:


 

* в дан­ном при­ме­ре зада­ча будет выпол­не­на два раза — для созда­ния ката­ло­гов /var/log/prometheus и /var/log/grafana.

в) Цик­лы из списков:


 

* под­ра­зу­ме­ва­ет­ся, что у нас есть пере­мен­ная nginxcfg в виде спис­ка (мас­си­ва). В дан­ном при­ме­ре ansible про­бе­жит по каж­дой запи­си дан­но­го спис­ка и сде­ла­ет соот­вет­ству­ю­щую заме­ну в кон­фи­гу­ра­ци­он­ном фай­ле nginx.conf.

г) Повтор зада­чи несколь­ко раз.

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


* в дан­ном при­ме­ре мы созда­ем цикл от 1 до 5 (зна­че­ние пере­мен­ной var_count). Что­бы ключ для цик­ла item не кон­флик­то­вал с клю­ча­ми дру­гих цик­лов, кото­рые могут исполь­зо­вать­ся внут­ри зада­чи, меня­ем имя item (по умол­ча­нию) на var_count_item с помо­щью дирек­ти­вы loop_var.

О loop: https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html.

6. Объ­еди­не­ние задач в блоки.

Это поз­во­лит уста­но­вить общие свой­ства и усло­вие для несколь­ких задач. Такая фор­ма запи­си умень­шит коли­чист­во строк и упро­стит восприятие.

Син­так­сис записи:


 

* в дан­ном при­ме­ре будет выпол­не­ны 3 зада­чи, если выпол­нит­ся одно усло­вие, кото­рое опи­сы­ва­ет­ся не для задач, а для блока.

О block: https://docs.ansible.com/ansible/latest/user_guide/playbooks_blocks.html.

7. Обра­ще­ния к DNS.

При помо­щи моду­ля dig мы можем отправ­лять запро­сы на сер­ве­ры имен, полу­чая све­де­ния о домен­ных запи­сях. Сце­на­рии исполь­зо­ва­ния могут быть различные.

а) про­стой запрос на полу­че­ние IP-адре­са для А-записи:


 

* в дан­ном при­ме­ре мы полу­чим адрес для узла www.test.ru.

б) запрос на полу­че­ние IP-адре­са для А-запи­си через опре­де­лен­ный DNS-сервер:


 

О dig: https://docs.ansible.com/ansible/latest/collections/community/general/dig_lookup.html.

8. Отправ­ка POST запроса.

Ранее мы рас­смат­ри­ва­ли модуль uri для ска­чи­ва­ния фай­ла. Одна­ко, дан­ный модуль может быть более полез­ным. Напри­мер, мы можем отправ­лять запро­сы к API:


* в дан­ном при­ме­ре будет отправ­лен POST-запрос на адрес https://api.test.ru/api/v2/send_request. В POST-дан­ных мы отпра­вим idname и action. Так­же допол­ни­тель­но мы отпра­ви­ли заго­ло­вок Authorization.

Об uri: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/uri_module.html.

9. Дата и время.

Мы можем исполь­зо­вать встро­ен­ную пере­мен­ную ansible_date_time для отоб­ра­же­ния даты и вре­ме­ни в раз­ных фор­ма­тах. Что­бы пере­мен­ная не была пустой, ansible дол­жен собрать фак­ты о хосте. Для это­го исполь­зу­ем инструк­цию gather_facts: yes.

Что­бы уви­деть все фор­ма­ты, напи­шем такой сценарий:


 

Мы долж­ны уви­деть что-то на подобие:


 

Что­бы полу­чить толь­ко дату, используем: