Автоматизируем и ускоряем процесс настройки облачных серверов с Ansible. Часть 5: local_action, условия, циклы и роли

в 10:49, , рубрики: automation, infoboxcloud, it-infrastructure, system administration, автоматизация, Блог компании Infobox, ит-инфраструктура, Настройка Linux, Серверное администрирование, системное администрирование, управление, хостинг

В первой части мы начали изучение Ansible, популярного инструмента для автоматизации настройки и развертывания ИТ-инфраструктуры. Ansible был успешно установлен в InfoboxCloud, описаны принципы работы, базовая настройка. В завершении статьи мы показали как быстро установить nginx на несколько серверов.

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

В третьей части мы узнали как написать единый Ansible playbook для разных ОС (например с rpm и deb), как обслуживать сотни хостов и не писать их все в inventory и как сгруппировать сервера по регионам InfoboxCloud. Было изучено использование переменных Ansible и файла inventory.

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

Автоматизируем и ускоряем процесс настройки облачных серверов с Ansible. Часть 5: local_action, условия, циклы и роли - 1

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

Запускаем задачи локально с помощью local_action

Иногда задачи надо запускать на локальной машине в рамках исполнения playbook для удаленных серверов. Например, можно на Ansible–сервере прописать ключи доступа по API к облаку и отдавать команды утилите командной строки для создания новых серверов облака. Часто может требоваться отправлять запросы в REST API через модуль uri Ansible. Возможность что-то делать прямо на Ansible–сервере для отдельной задачи в playbook, где в качестве hosts прописаны удаленные сервера, есть.

Допустим, вы хотите запустить shell–модуль на сервере, откуда вы запускаете Ansible. Для этого пригодится опция local_action, которая запустит модуль локально.

---
- hosts: experiments
  remote_user: root
  tasks:

  - name: check running processes on remote system
    shell: ps
    register: remote_processes

  - name: remote running processes
    debug: msg="{{ remote_processes.stdout }}"

  - name: check running processes on local system
    local_action: shell ps
    register: local_processes

  - name: local running processes
    debug: msg="{{ local_processes.stdout }}"

Процессы на удаленных машинах.
Автоматизируем и ускоряем процесс настройки облачных серверов с Ansible. Часть 5: local_action, условия, циклы и роли - 2

Процессы на локальной машине.
Автоматизируем и ускоряем процесс настройки облачных серверов с Ansible. Часть 5: local_action, условия, циклы и роли - 3

Mы видим, что исполнение команды перенаправляется для локальной машины.
Автоматизируем и ускоряем процесс настройки облачных серверов с Ansible. Часть 5: local_action, условия, циклы и роли - 4

Таким образом, вы можете запустить любой модуль Ansible с local_action.

Работаем с условиями

Ansible исполняет все задачи последовательно. Тем не менее, для сложного playbook с десятками задач, вам может потребоваться в зависимости от ситуации запускать только часть задач. Ранее мы уже рассматривали ситуацию, когда с помощью переменных мы корректно устанавливали Apache на rpm и deb дистрибутивы. Подобным образом можно указывать условия для выполнения задач с помощью when:

---
- hosts: experiments
  remote_user: root
  tasks:
  - name: Install httpd package
    yum: name=httpd state=latest
    sudo: yes
    when: ansible_os_family == "RedHat"

  - name: Install apache2 package
    apt: name=apache2 state=latest
    sudo: yes
    when: ansible_os_family == "Debian"

Если ОС семейства RedHat – будет установлен пакет httpd через yum, а если семейства Debian – apache2 через apt. ansible_os_family – переменная Ansible, получаемая на стадии gather_facts.

В playbook выше мы использовали sudo: yes, подразумевая, что у пользователя есть права sudo. Давайте проверим, так ли это:

---
- hosts: experiments
  remote_user: root
  tasks:

  - name: Testing user sudo privilege
    command: /usr/bin/sudo -v
    register: sudo_response
    ignore_errors: yes

  - name: Stop if Users doesn`t have sudo privilege
    fail: msg="User doesn`t have sudo privilege"
    when: sudo_response.rc == 1

Автоматизируем и ускоряем процесс настройки облачных серверов с Ansible. Часть 5: local_action, условия, циклы и роли - 5

В примере выше мы запустили команду на сервере /usr/bin/sudo -v и сохранили ее вывод в переменную через register. В переменной был захвачен вывод stdout и stderr (rc, return code). Во второй задаче мы проверили содержание return code переменной и если ошибка произошла — должны завершить исполнение playbook с выводом сообщения.

Для сравнения в условиях в Ansible можно использовать == (равно), != (не равно), > (больше), < (меньше), >= (больше равно), <= (меньше равно).

Если вам нужно проверить, есть ли в переменной символ или строка, используйте операторы in и not.

- name: Querying rpm list for httpd package
  shell: rpm -qa | grep httpd
  register: httpd_rpm

- name: Check if httpd rpm is installed on the remote host
  debug: msg="httpd is installed on the remote host"
  when: "'httpd-2.2.27–1.2.x86_64' in httpd_rpm.stdout"

– name: Check if httpd rpm is not installed on the remote host
  debug: msg="httpd is not installed on the remote host"
  when: not 'httpd-2.2.27.1.2.x86_64' in httpd_rpm.stdout

Можно задавать несколько условий, используя операторы and (и) и or (или).

– name: Check if httpd rpm is installed on the remote host
  debug: msg="httpd is installed on the remote host"
  when: "'httpd-2.2.27–1.2.x86_64' in httpd_rpm.stdout and 'httpd-tools-2.2.27–1.2.x86–64' in httpd_rpm.stdout"

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

– name: Rsync 
  shell: /usr/bin/rsync -ra /home /backup/{{ inventory_hostname}}
  sudo: yes
  when: backup

Ansible позволяет в условии использовать информацию о том, была ли уже определена переменная. Для этого используйте when: var is not define (где var — имя переменной, is not define – еще не была определена, is defined – уже была определена).

Работаем с циклами

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

Стандартные циклы

Используя стандартные циклы вы можете передать список пакетов для установки и Ansible запустит задачу для всех указанных пакетов.

---
- hosts: experiments
  remote_user: root
  tasks: 
  – name: Install nginx package
    yum: name={{ item }} state=latest
    with_items:
    – nginx
    – htop
    sudo: yes

В примере выше мы использовали конструкцию «with_items:» для задания переменных и использовали переменную по умолчанию item. На каждой итерации item принимает следующее значение, указанное в with_items.

Автоматизируем и ускоряем процесс настройки облачных серверов с Ansible. Часть 5: local_action, условия, циклы и роли - 6

Задача запускается один раз, но apt вызывается для всех указанных пакетов. Можно так же использовать with_items как словарь вместо строк:

with_items:
– {name: 'httpd', state: 'latest'}
– {name: 'htop', state: 'absent'}

Вложенные циклы

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

–––
– hosts: experiments
  remote_user: root
  tasks:
  – name: give users access to multiple databases
  mysql_user: name={{ item[0] }} priv={{ item[1]}}.*:ALL append_privs=yes password=pass login_user=root login_password=root
  with_nested:
  – ['alexey', 'alexander']
  – ['clientdb', 'providerdb']

В приведенном примере мы используем модуль mysql_user для установки прав на базы данных и используем вложенные циклы с двумя списками: список пользователей и список баз данных. Ansible запустит модуль mysql_user для пользователя alexey, даст права на все указанные во втором списке базы данных, затем запустит для пользователя alexander и так же даст права.

Циклы по подэлементам

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

---
- hosts: experiments
  remote_user: root
  vars:
    users:
    – name: alexey
      database:
      – clientdb
      – providerdb
    – name: alexander
      database:
      – providerdb
  tasks:
  – name: give users access to multiple databases
    mysql_user: name={{ item.0.name }} priv={{ item.1 }}.*:ALL append_privs=yes password=pass login_user=root login_password=root
    with_subelements:
    – users
    - database

Мы создали словари, которые состоят из имен пользователей и имен баз данных. Вместо добавления данных пользователей в playbook можно вынести их в отдельный файл переменных и включить в playbook. Ansible пройдется по словарю используя переменную item. Ansible назначает численные значения ключам, представленным конструкцией with_subelements, начиная с 0. В словаре 0 имя — пара «ключ-значения», поэтому для обращения по имени пользователя мы используем item.0.name. Dictionary — простой список, поэтому для обращения используем item.1.

Работаем с ролями

При проектировании архитектуры обычно оперируют ролями серверов: веб-сервер, сервер баз данных, балансировщик нагрузки и так далее. Каждая роль включает в себя определенный набор софта для установки и настройки. С ростом вашей системы постепенно будут выделяться компоненты, которые можно повторно использовать. Роли в Ansible предоставляют удобный способ организации ваших playbook. На основе предопределенной файловой структуры будут загружаться компоненты роли. Фактически роли — просто магия вокруг include (импортов), облегчающая подготовку playbook.

Типичная структура playbook с ролями:

---
- hosts: webservers
  roles:
     - common
     - web
     – db

Файловая структура ролей будет выглядеть так:

site.yml
webservers.yml
roles/
   common/
     files/
     templates/
     tasks/
     handlers/
     vars/
     defaults/
     meta/
   web/
     files/
     templates/
     tasks/
     handlers/
     vars/
     defaults/
     meta/
   db/
     files/
     templates/
     tasks/
     handlers/
     vars/
     defaults/
     meta/

Если какой-то директории в роли нет — она будет проигнорирована и playbook будет исполняться. Совсем не обязательно у вас должны быть все элементы и директории playbook.

Правила, используемые для каждой роли:

  • Если roles/x/tasks/main.yml существует, задачи будут добавлены в процесс исполнения playbook.
  • Если roles/x/handlers/main.yml существует, обработчики событий будут добавлены в процесс исполнения playbook.
  • Если roles/x/vars/main.yml существует, переменные будут добавлены в процесс исполнения playbook.
  • Если roles/x/meta/mail.yml существует, любые роли-зависимости будут добавлены в список ролей. (В meta можно указывать список ролей, которые должны быть применены до конкретной роли, чтобы она применилась корректно).
  • Любая задача копирования может ссылаться на файл в roles/x/files без указания абсолютного или относительного пути.
  • Любая скриптовая задача может ссылаться на скрипты в roles/x/files без указания абсолютного или относительного пути.
  • Любая задача шаблонизации может ссылаться на roles/x/templates без указания абсолютного или относительного пути.
  • Любые импортируемые задачи могут ссылаться на файлы задач в директории roles/x/tasks без указания абсолютного или относительного пути.

В конфигурационном файле Аnsible можно задать roles_path (директорию с ролями). Это может пригодиться, если у вас playbook лежат в одном репозитории, а сами роли в другом. Можно задавать сразу несколько путей к ролям через двоеточие:

roles_path = /opt/mysite/roles:/opt/othersite/roles

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

---
- hosts: experiments
  roles:
   – common
   – {role: web, dir: '/var/www', port: 80}
   – {role: repository, when: "ansible_os_family =='RedHat'"}

Ранее в статьях мы не рассматривали тэги. С их помощью можно запускать помеченную часть playbook.
С задачами использование тэгов выглядит так:

tasks:
    - apt: name={{ item }} state=installed
      with_items:
         - httpd
         - htop
      tags:
         - packages

    - template: src=templates/src.j2 dest=/var/www/.htaccess
      tags:
         - configuration

Можно запустить часть playbook так: ansible-playbook example.yml --tags «configuration,packages» или пропустить исполнение части так: ansible-playbook example.yml --skip-tags «notification».

Так вот тэги также можно использовать и при указании ролей:

---
- hosts: experiments
  roles:
    - { role: web, tags: ["apache", "simple"] }

Можно указать, какие задачи должны выполниться до роли и после:

---
- hosts: experiments
  pre_tasks:
    - shell: echo 'hello, habr'
  roles:
    - { role: web }
  tasks:
    - shell: echo 'still busy'
  post_tasks:
    - shell: echo 'goodbye, habr'

Зависимости ролей

Зависимости ролей позволяют автоматически исполнить зависимые роли при запуске конкретных ролей, у которых зависимости есть. Зависимости хранятся в roles/x/meta/main.yml. Вместе с зависимыми ролями могут быть переданы параметры. Путь к ролям может быть указан как в сокращенном виде, так и в полном. Также может быть использован репозиторий системы управления версиями.

---
dependencies:
  - { role: common, some_parameter: 3 }
  - { role: '/path/to/common/roles/foo', x: 1 }
  - { role: 'git+http://git.example.com/repos/role-foo,v1.1,foo' }

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

Ansible Galaxy

Ansible Galaxy — репозиторий ролей Ansible. С этого ресурса можно использовать уже готовые роли Ansible или добавлять свои.

Заключение

В написании статьи очень помогла книга "Learning Ansible" и конечно официальная документация.

Все эксперименты с Ansible удобно проводить в InfoboxCloud, так как имеется возможность для каждого виртуального сервера установить именно то количество ресурсов, которое необходимо для задачи (CPU/Ram/диск независимо друг от друга) или использовать автомасштабирование, а не выбирать VM из готовых шаблонов. Когда эксперименты не проводятся — можно просто выключить VM и оплачивать только стоимость диска.

Если вы обнаружили ошибку в статье, автор ее с удовольствием исправит. Пожалуйста напишите в ЛС или на почту о ней. Туда же можно задавать вопросы по Ansible для освещения в последующих статьях. Если вы не можете писать комментарии на Хабре, можно оставить их в Сообществе InfoboxCloud.

Успешной работы!

Автор: infobox

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js