Знакомство с Ansible. Часть 5: роли, условия и циклы

Nov 16, 2015 20:36 · 930 words · 5 minute read ansible

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

В завершающей части знакомства с Ansible давайте разберемся с условиями и циклами, а также научимся организовывать playbook в роли!

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

Рассмотрим простой playbook, который поможет установить несколько пакетов на удаленный сервер:

---
- hosts: test
  tasks:
 
  - name: Install packages
    apt: name={{ item }} state=latest
    with_items:
    - htop
    - mytop
    - wget
    sudo: yes

В результате выполнения данного набора инструкций на удаленном хосте будут установлены пакеты, перечисленные в with_items:. Playbook будет запущен один раз, но модуль apt будет вызываться для каждого указанного пакета:

ansible-playbook install_packages.yml 

PLAY [test] ******************************************************************* 

GATHERING FACTS *************************************************************** 
ok: [test-1]

TASK: [Install packages] ****************************************************** 
changed: [test-1] => (item=htop,mytop,wget)

PLAY RECAP ******************************************************************** 
test-1                 : ok=2    changed=1    unreachable=0    failed=0   

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

---
- hosts: test
  tasks:
   - name: Add test users
     user: name={{ item.name }} state=present groups={{ item.groups }}
     with_items:
      - { name: 'user1', groups: 'adm'  }
      - { name: 'user2', groups: 'lpadmin' }
     sudo: yes

Результат выполнения данного playbook будет выглядеть так:

ansible-playbook add_users.yml 

PLAY [test] ******************************************************************* 

GATHERING FACTS *************************************************************** 
ok: [test-1]

TASK: [Add test users] ******************************************************** 
changed: [test-1] => (item={'name': 'user1', 'groups': 'adm'})
changed: [test-1] => (item={'name': 'user2', 'groups': 'lpadmin'})

PLAY RECAP ******************************************************************** 
test-1                 : ok=2    changed=1    unreachable=0    failed=0   

Примечание. Больше примеров с использованием циклов можно найти в официальной документации по Ansible.

Ansible выполняет задачи в порядке их следования в наборе инструкций, но бывают случаи, когда требуется выполнить только часть задач. В третьей части нашего цикла мы уже пробовали правильно устанавливать web-сервер Apache на разные дистрибутивы с помощью переменных. Чтобы задачи выполнялись только в определенном случае, можно (и нужно) указывать условия с помощью when:.

В условиях в Ansible для сравнения используются == (равно), != (не равно), > (больше), < (меньше), >= (больше равно), <= (меньше равно). Можно также указать несколько условий с помощью операторов and (и) и or (или). Для проверки вхождения символа или подстроки в строку используются операторы in и not.

Для проверки работы некоторых условий в Ansible создадим playbook следующего содержания:

---
- hosts: test
  tasks:
 
  - name: Check OS family
    debug: msg="This is my OS"
    when: ansible_os_family == "Debian"
 
  - name: Check if Apache2 is installed
    command: dpkg-query -W apache2
    register: apache2_check
 
  - name: Print message if apache installed
    debug: msg="Apache2 is installed on remote host"
    when: "'apache2' in apache2_check.stdout"
 
  - name: Check if admin logged
    command: who
    register: who_check
 
  - name: Print message if user admin not logged
    debug: msg="User admin is not logged on remote host"
    when: not 'admin' in who_check.stdout

Результат выполнения данного набора инструкций будет таким:

ansible-playbook debug.yml 

PLAY [test] ******************************************************************* 

GATHERING FACTS *************************************************************** 
ok: [test-1]

TASK: [Check OS family] ******************************************************* 
ok: [test-1] => {
    "msg": "This is my OS"
}

TASK: [Check if Apache2 is installed] ***************************************** 
changed: [test-1]

TASK: [Print message if apache installed] ************************************* 
ok: [test-1] => {
    "msg": "Apache2 is installed on remote host"
}

TASK: [Check if admin logged] ************************************************* 
changed: [test-1]

TASK: [Print message if user admin not logged] ******************************** 
ok: [test-1] => {
    "msg": "User admin is not logged on remote host"
}

PLAY RECAP ******************************************************************** 
test-1                 : ok=6    changed=2    unreachable=0    failed=0   

Напоследок у нас остается немного “магии”. В архитектуре проекта обычно сервера разделяются по своему предназначению и выполняют определенную роль - web-сервер, сервер баз данных, почтовый сервер и т. д. Каждому из серверов требуется отличающийся набор пакетов и настроек для правильного выполнения своей роли. С ростом количества серверов будет расти количество наборов инструкций (playbook), которые будут использоваться повторно.

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

---
- hosts: servers
  roles:
     - web
     – db
     - post

Тогда файловая структура ролей может выглядеть таким образом:

site.yml
servers.yml
roles/
   web/
     files/
     templates/
     tasks/
     handlers/
     vars/
     defaults/
     meta/
   db/
     files/
     templates/
     tasks/
     handlers/
     vars/
     defaults/
     meta/
   post/
     files/
     templates/
     tasks/
     handlers/
     vars/
     defaults/
     meta/

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

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

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

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

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

На этом все, знакомство с Ansible завершено, но это только вершина айсберга - намного больше предстоит узнать активно используя Ansible при настройке IT-инфраструктуры.

tweet Share