15 вещей, которые вы должны знать об Ansible

Перевод опубликованной на codeheaven.io статьи 15 Things You Should Know About Ansible за авторством Marlon Bernardes

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

1. Передача параметров для ролей

Хорошей практикой является создание ролей для организации ваших плейбуков. Скажем, мы хотим создать роль для установки Jenkins. Структура каталогов для этой роли будет выглядеть как то так:

jenkins/
   files/
   templates/
   tasks/
   handlers/
   defaults/

Каталог defaults используется для хранения переменных по-умолчанию для роли. Внутри него может находиться файл main.yml:

jenkins_port: 8080
jenkins_context_path: /jenkins
jenkins_home: /jenkins

Вы можете переопределить переменные по-умолчанию, передачей различных параметров для роли, вот так:

roles:
   - { role: jenkins, jenkins_port: 8181, jenkins_home: '/jenkins1' }
   - { role: jenkins, jenkins_port: 8080, jenkins_home: '/jenkins2' }

2. Как сделать модуль command идемпотентным

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

- command: /usr/bin/create-database.sh

Для достижения идемпотентности вы можете использовать атрибут creates. Когда он присутствует, Ansible выполнит задачу command, только если заданный в файле шаблон не существует. В качестве альтернативы можно использовать removes, который будет выполнять задание, только если указанный файл существует.

- command: /usr/bin/create-database.sh creates=/path/to/database

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

3. Использование Ansible модуля setup для сбора информации о ваших хостах

Вероятно, вы видели, что первое, что Ansible делает, когда выполняет плейбук - это что-то типа этого:

TASK [setup] *******************************************************************
ok: [servername]

Это происходит потому что Ansible вызывает специальный модуль setup перед выполнением первой задачи. Модуль setup коннектится на хост и собирает факты всех видов: IP адрес, дисковое пространство, архитектура процессора, доступная память и многое другое. Может быть полезным вызов этого модуля вручную, как быстрый способ сбора информации о ваших хостах. Чтобы сделать это - просто выполните команду:

$ ansible localhost -m setup
localhost | SUCCESS => {
  "ansible_facts": {
    "ansible_all_ipv4_addresses": [
        "10.27.12.77",
        "192.168.33.1"
    ],
    (множество других фактов)
  }

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

Хотите вспомнить, что делает плейбук? Выполните ansible-playbook используя флаг –list-tasks и Ansible отобразит все имещиеся задачи:

$ ansible-playbook install-jenkins.yml --list-tasks
PLAY: #1
  tasks:
    TASK: meta
    TASK: open-jdk : Install open jdk 1.8
    TASK: mount-partition : Creating the filesystem for the device {{ device }}  (if needed)
    TASK: mount-partition : Mounting the device {{ device }} on path {{ path }}
    TASK: jenkins : Ensure Jenkins repo is installed.
    TASK: jenkins : Add Jenkins repo GPG key.
    TASK: jenkins : Ensure Jenkins is present.
    TASK: jenkins : Ensures that the home directory exists
    TASK: jenkins : include
    TASK: jenkins : Ensure Jenkins is started and runs on startup.
    TASK: jenkins : Wait for Jenkins to start up before proceeding.
    TASK: jenkins : Get the jenkins-cli jarfile from the Jenkins server.

5. Используйте ansible-vault, если вы хотите хранить конфиденциальную информацию

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

Ansible поставляется с утилитой командной строки ansible-vault, которая позволяет создавать и управлять шифрованными файлами. Таким образом, вы можете «коммитить» зашифрованный файл в вашу систему управления версиями и только пользователи с паролем дешифрования смогут прочесть его.

# Зашифровать существующий файл. Вам необходимо создать пароль шифрования.
ansible-vault encrypt secrets.yml

# Создать новый зашифрованный файл. Вам необходимо создать пароль шифрования.
ansible-vault create secrets.yml

# Расшифровать файл. Вам необходимо ввести пароль, используемый для шифрования.
# Используйте с осторожностью! Не оставляйте ваши файлы не зашифрованными.
ansible-vault decrypt secrets.yml

# Редактировать зашифрованный файл
# (по-умолчанию используется vim, переопределяется переменной окружения $EDITOR)
ansible-vault edit secrets.yml

# Отобразить содержимое зашифрованного файла
ansible-vault edit secrets.yml

Если вы импортируете файл secrets.yml в ваш плейбук, Ansible «зафейлится», так как не будет знать как прочесть зашифрованный файл. Вам необходимо указать аргумент командной строки –ask-vault-pass, который заставит Ansible предложить ввести пароль для зашифрованного файла.

ansible-playbook playbook.yml -i hosts --ask-vault-password

Другой способ заключается в хранении пароля в файле (он не должен быть «закоммичен») и указать путь к файлу, используя аргумент –vault-password-file. Если этот файл исполняемый, Ansible будет запускать его и использовать вывод в качестве пароля.

Узнать больше о ansible-vault можно тут.

6. Использование with_items может быть хорошей идеей

При использовании with_items, Ansible создаст переменную {{item}}, содержащую значение текущей итерации. Некоторые модули обрабатывают коллекции очень хорошо, и намного быстрее, чем при запуске той же самой задачи несколько раз с различными параметрами.

# Установка всех пакетов за раз (быстрее)
  - name: install required packages using the apt module
    apt: package={{ item }}  update_cache=yes
    sudo: True
    with_items:
      - git
      - memcached
      - nginx

  # Установка пакетов по отдельности (медленнее)
  - name: install git
    apt: package=git update_cache=yes
    sudo: True

  - name: install memcached
    apt: package=memcached update_cache=yes
    sudo: True

  - name: install nginx
    apt: package=nginx update_cache=yes
    sudo: True

7. Как работают Local Actions

Иногда вам может понадобиться выполнить задачу на вашем локальном хосте, вместо запуска её на удаленном хосте. Это может быть полезно, если мы хотим подождать, пока сервер загрузится (если он только что запущен), или когда мы хотим добавить некоторые узлы в пул балансировщика (или удалить их):

tasks:
 - name: take out of load balancer pool
   local_action: >
      command /usr/bin/take_out_of_pool {{ inventory_hostname }}

 - name: update application
   yum: name=acme-web-stack state=latest

 - name: add back to load balancer pool
   local_action: >
      command /usr/bin/take_out_of_pool {{ inventory_hostname }}

Пример ниже показывает как запустить EC2 инстанс и дождаться, когда он станет доступен:

- name: Launching EC2 Instance
    # instance options here
  register: ec2


- name: Waiting for ec2 instances to listen on port 22
  wait_for:
    state=started
    host={{ item.public_dns_name }}
    port=22
  with_items: ec2.instances

8. Вы можете указать Ansible выполнять задачу только один раз

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

Что бы добиться этого, вы можете использовать параметр run_once, указывающий Ansible выполнять команду только один раз:

- name: run the database migrations
  command: bundle exec rake db:migrate
  run_once: true

9. Обработчики (handlers) это специальные типы задач

Обработчики (handlers) это задачи с уникальными именами, которые будут выполняться только в случае уведомления от другой задачи. Они очень удобны для перезапуска сервисов или перезагрузки системы.

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

Вот пример того, как перезапустить 2 сервиса, когда содержимое файла изменяется, но только в том случае, если файл изменяется (пример взят из Ansible docs).

- name: template configuration file
  template: src=template.j2 dest=/etc/foo.conf
  notify:
     - restart memcached
     - restart apache

Обработчики должны быть объявлены где-нибудь в вашем плейбуке:

handlers:
    - name: restart memcached
      # Используется модуль service, но вы можете использовать любой другой модуль
      service: name=memcached state=restarted
    - name: restart apache
      service: name=apache state=restarted

10. Ускорение с помощью конвейеризации (pipelining)

Несколько советов, о том как заставить Ansible выполняться еще быстрее:

  • Включение конвейеризации

Включение конвейерной обработки (pipelining) сокращает количество операций SSH, требуемых для выполнения модуля на удаленном сервере, передавая сценарии по каналу (pipe) в SSH сессию вместо того, чтобы копировать их. В результате это может привести к значительному повышению производительности.

Однако, вы должны быть осторожны. Конвейеризация будет работать только если опция requiretty отключена на всех удаленных хостах в файле sudoers (/etc/sudoers).

[ssh_connection]
pipelining = True
  • Отключение сбора фактов или включение кэширования фактов

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

- hosts: servername
  gather_facts: False
  tasks:
    - name: ...
    # ...

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

11. У Ansible есть несколько модулей для уведомлений

Используете Ansible для автоматизации ваших blue-green деплоев (blue-green deployment)? Запускаете плейбуки для «провиженинга» новых инстансов AWS? Дайте знать об этом вашей команде, с помощью одного из модулей для уведомлений. В качестве примера, задача ниже отправит уведомление в Slack:

- hosts: servername
  tasks:
    - name: Send notification message via Slack
      local_action:
        module: slack
        # To retrieve your slack token, open your team settings and look for the
        # Incoming Webhooks plugin
        token: <your>/<token>/<goes here>
        msg: "Hello team! I just finished updating our production environment."
        channel: "#general"
        username: "ansible-bot"

Также доступны модули для уведомления в irc, twillio, hipchat, jabber и много других.

12. EC2 инстансы автоматически группируются по их тегам

При использовании Amazon Web Services и Ansible EC2 скрипта динамической инвентаризации, все инстансы будут сгруппированы основываясь на их характеристиках, таких как тип, кейпары и теги. EC2 теги это просто ключ=значение ассоциированное с вашими инстансами, которые вы можете использовать как вам угодно. Некоторые используют метки, чтобы сгруппировать production/staging сервера, для обозначения web-серверов или «активных» серверов во время blue-green деплоев.

EC2 Dynamic Inventory скрипт использует следующий шаблон (без скобок) при группировке хостов по тегу:

tag_[TAG_NAME]_[TAG_VALUE]

Так что, если вы хотите выполнить задачу на всех узлах с тегом env=staging, просто добавьте это в ваш плейбук:

  hosts: tag_env_staging
  tasks:
    - name: This task will be run on all servers with env == staging
    # ...

Чтобы сделать это еще более интересным, вы можете использовать шаблоны Ansible, конкретизировав, какие хосты должны быть затронуты. Например, если вы хотите выполнить определенную задачу на ваших production db серверах (при условии, что они обозначены должным образом), вы можете использовать перекрестный шаблон ( :& ), как здесь:

  hosts: tag_env_production&:tag_type_db
  tasks:
    - name: This task will be run on all servers with tags 'env=production' and 'type=db'
    # ...

13. Вы можете выполнять задачи в режиме «Dry Run»

Ansible поддерживает запуск плейбука в режиме dry run (также называемый Check Mode). В этом режиме Ansible не будет вносить никаких изменений на вашем хосте, а просто сообщит, какие изменения будут сделаны, если бы плейбук был запущен без этого флага.

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

14. Задачи могут выполняться шаг за шагом

Иногда, вы не хотите выполнять все задачи вашего плейбука.
Это распространенное решение, когда вы пишете новый плейбук и хотите проверить его.
Ansible предоставляет способ, который позволит вам решить, какие задачи вы хотите запустить, используя флаг –step.
Это позволит вам выбирать, хотите ли вы выполнить задачу ( y ), пропустить ее ( n ) или ( c ) продолжить не спрашивая.

15. Можно выполнять задачи основываясь на их тегах

Вы можете добавить один или несколько тегов к задаче или плейбуку.
Чтобы сделать это, просто отметьте, что вы хотите «тегировать» с помощью атрибута tags:

Позже вы можете решить какие теги выполнить или пропустить используя флаг –tags tagname (или просто -t) и –skip-tags tagnames:

# выполнить только задачу с тегом 'dependencies'
$ ansible-playbook --tags=dependencies playbook.yml

# выполнит все задачи, кроме тех, что содержат тег 'optional'
$ ansible-playbook --skip-tags=optional playbook.yml

Можно указать более одного тега, разделяя их запятыми.

Рекомендации

Ansible Docs Ansible Up & Running Book, by Lorin Hochstein