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

от автора

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

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

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

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

В этой части мы рассмотрим, как запускать задачу локально в рамках 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 }}" 

Процессы на удаленных машинах.

Процессы на локальной машине.

Mы видим, что исполнение команды перенаправляется для локальной машины.

Таким образом, вы можете запустить любой модуль 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 

В примере выше мы запустили команду на сервере /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.

Задача запускается один раз, но 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.

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

ссылка на оригинал статьи http://habrahabr.ru/post/252461/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *