Ansible руководство на русском

управление конфигурацией оркестра

Это практическое пособие познакомит вас c Ansible. Вам понадобится виртуальная или реальная машина, которая будет выступать в роли узла для Ansible. Окружение для Vagrant идет в комплекте с этим пособием.

Ansible — это программное решение для удаленного управления конфигурациями. Оно позволяет настраивать удаленные машины. Главное его отличие от других подобных систем в том, что Ansible использует существующую инфраструктуру SSH, в то время как другие (chef, puppet, и пр.) требуют установки специального PKI-окружения.

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

  1. Установка Ansible и Vagrant
  2. Файл инвенторизации
  3. Модули shell, copy, сбор фактов, переменные
  4. Запуск на группу хостов
  5. Плейбуки
  6. Пример: поднимаем кластер, устанавливаем и настраиваем Apache и балансировщик нагрузок HAproxy
  7. Обработка ошибок, откат
  8. Шаблоны конфигурации
  9. Роли

Ansible использует так называемый push mode: конфигурация «проталкивается» (push) с главной машины. Другие CM-системы обычно поступают наоборот – узлы «тянут» (pull) конфигурацию с главной машины.

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

Что нужно для Ansible

Необходимы следующие Python-модули

  • python-yaml
  • python-jinja2

На Debian/Ubuntu запустите:

sudo apt-get install python-yaml python-jinja2 python-paramiko python-crypto

У вас также должна быть пара ключей в ~/.ssh.

Установка Ansible

Из исходников

Ветка devel всегда стабильна, так что используем ее. Возможно, вам нужно будет установить git (sudo apt-get install git на Debian/Ubuntu).

git clone git://github.com/ansible/ansible.git
cd ./ansible

Теперь можно загрузить окружение Ansible.

source ./hacking/env-setup

Из deb пакета

sudo apt-get install make fakeroot cdbs python-support
git clone git://github.com/ansible/ansible.git
cd ./ansible
make deb
sudo dpkg -i ../ansible_1.1_all.deb (version may vary)

В этом пособии предполагается, что вы использовали именно этот способ.

Установка Vagrant

Vagrant позволяет с легкостью создавать виртуальные машины и запускать их на VirtualBox. Vagrantfile идет в комплекте с пособием.

Чтобы запустить Vagrant вам нужно установить:

  • VirtualBox
  • Ruby (скорее всего уже установлено на вашей системе)
  • Vagrant 1.1+ (см.
    http://docs.vagrantup.com/v2/installation/index.html).

Теперь инициализируйте виртуальную машину с помощью следующей команды. Имейте ввиду, что вам не нужно скачивать какой-либо «box» вручную. Это пособие уже содержит готовый Vagrantfile, он содержит все, что нужно для работы.

vagrant up

и налейте себе кофе (если вы используете vagrant-hostmaster, то вам нужно будет ввести root-пароль). Если что-то пошло не так, загляните в туториал по Vagrant’у.

Добавление SSH-ключей на виртуальной машине

Чтобы продолжить, вам нужно добавить свои ключи в authorized_keys root’а на виртуальной машине. Это не обязательно (Ansible может использовать sudo и авторизацию по паролю), но так будет намного проще.

Ansible идеально подходит для этой задачи, поэтому используем его. Однако, я не буду пока ничего объяснять. Просто доверьтесь мне.

ansible-playbook -c paramiko -i step-00/hosts step-00/setup.yml --ask-pass --sudo

В качестве пароля введите vagrant. Если возникнут ошибки «Connections refused», то проверьте настройки фаервола.

Теперь добавьте свои ключи в ssh-agent (ssh-add).

Inventory

Теперь нам нужно подготовить файл inventory. Место по умолчанию это /etc/ansible/hosts.
Но вы можете настроить Ansible так, чтобы использовался другой путь. Для этого используется переменная окружения (ANSIBLE_HOSTS) или флаг -i.

Мы создали такой файл inventory:

host0.example.org ansible_ssh_host=192.168.33.10 ansible_ssh_user=root
host1.example.org ansible_ssh_host=192.168.33.11 ansible_ssh_user=root
host2.example.org ansible_ssh_host=192.168.33.12 ansible_ssh_user=root

ansible_ssh_host это специальная переменная, которая содержит IP-адрес узла, к которому будет создаваться соединение. В данном случае она не обязательна, если вы используете gem vagrant-hostmaster. Также, вам нужно будет менять IP-адреса если вы устанавливали и настраивали свою виртуальную машину с другими адресами.

ansible_ssh_user это еще одна специальная переменная которая говорит Ansible’у подключаться под указанным аккаунтом (юзером). По умолчанию Ansible использует ваш текущий аккаунт, или другое значение по умолчанию, указанное в ~/.ansible.cfg (remote_user).

Проверка

Теперь когда Ansible установлен, давайте проверим, что все работает:

ansible -m ping all -i step-01/hosts

Здесь Ansible попытается запустить модуль ping (подробнее о модулях позже) на каждом хосте. Вывод должен быть примерно таким:

host0.example.org | success >> {
    "changed": false,
    "ping": "pong"
}

host1.example.org | success >> {
    "changed": false,
    "ping": "pong"
}

host2.example.org | success >> {
    "changed": false,
    "ping": "pong"
}

Отлично! Все три хоста живы и здоровы, и Ansible может общаться с ними.

Общение с узлами

Теперь мы готовы. Давайте поиграем с уже знакомой нам командой из прошлого раздела: ansible. Эта команда – одна из трех команд, которую Ansible использует для взаимодействия с узлами.

Сделаем что-нибудь полезное

В прошлой команде -m ping означал «используй модуль ping». Это один из множества модулей, доступных в Ansible. Модуль ping очень прост, он не требует никаких аргументов. Модули, требующие аргументов, могут получить их через -a. Давайте взглянем на несколько модулей.

Модуль shell

Этот модуль позволяет запускать shell-команды на удаленном узле:

ansible -i step-02/hosts -m shell -a 'uname -a' host0.example.org

Вывод должен быть вроде:

host0.example.org | success | rc=0 >>
Linux host0.example.org 3.2.0-23-generic-pae #36-Ubuntu SMP Tue Apr 10 22:19:09 UTC 2012 i686 i686 i386 GNU/Linux

Легко!

Модуль copy

Модуль copy позволяет копировать файл из управляющей машины на удаленный узел. Представим, что нам нужно скопировать наш /etc/motd в /tmp узла:

ansible -i step-02/hosts -m copy -a 'src=/etc/motd dest=/tmp/' host0.example.org

Вывод:

host0.example.org | success >> {
    "changed": true,
    "dest": "/tmp/motd",
    "group": "root",
    "md5sum": "d41d8cd98f00b204e9800998ecf8427e",
    "mode": "0644",
    "owner": "root",
    "size": 0,
    "src": "/root/.ansible/tmp/ansible-1362910475.9-246937081757218/motd",
    "state": "file"
}

Ansible (точнее, модуль copy, запущенный на узле) ответил кучей полезной информации в формате JSON. Позже мы увидим, как это можно использовать.

У Ansible есть огромный
список модулей, который покрывает практически все, что можно делать в системе. Если вы не нашли подходящего модуля, то написание своего модуля – довольно простая задача (и не обязательно писать его на Python, главное, чтобы он понимал JSON).

Много хостов, одна команда

Все что было выше – замечательно, но нам нужно управлять множеством хостов. Давайте попробуем. Допустим, мы хотим собрать факты про узел и, например, хотим узнать какая версия Ubuntu установлена на узлах. Это довольно легко:

ansible -i step-02/hosts -m shell -a 'grep DISTRIB_RELEASE /etc/lsb-release' all

all означает «все хосты в файле inventory». Вывод будет примерно таким:

host1.example.org | success | rc=0 >>
DISTRIB_RELEASE=12.04

host2.example.org | success | rc=0 >>
DISTRIB_RELEASE=12.04

host0.example.org | success | rc=0 >>
DISTRIB_RELEASE=12.04

Больше фактов

Легко и просто. Однако, если нам нужно больше информации (IP-адреса, размеры ОЗУ, и пр.), такой подход может быстро оказаться неудобным. Решение – использовать модуль setup. Он специализируется на сборе фактов с узлов.

Попробуйте:

ansible -i step-02/hosts -m setup host0.example.org

ответ:

"ansible_facts": {
    "ansible_all_ipv4_addresses": [
        "192.168.0.60"
    ],
    "ansible_all_ipv6_addresses": [],
    "ansible_architecture": "x86_64",
    "ansible_bios_date": "01/01/2007",
    "ansible_bios_version": "Bochs"
    },
    ---snip---
    "ansible_virtualization_role": "guest",
    "ansible_virtualization_type": "kvm"
},
"changed": false,
"verbose_override": true

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

Например, вам нужно узнать, сколько памяти доступно на всех хостах. Это легко: запустите ansible -i step-02/hosts -m setup -a 'filter=ansible_memtotal_mb' all:

host2.example.org | success >> {
    "ansible_facts": {
        "ansible_memtotal_mb": 187
    },
    "changed": false,
    "verbose_override": true
}

host1.example.org | success >> {
    "ansible_facts": {
        "ansible_memtotal_mb": 187
    },
    "changed": false,
    "verbose_override": true
}

host0.example.org | success >> {
    "ansible_facts": {
        "ansible_memtotal_mb": 187
    },
    "changed": false,
    "verbose_override": true
}

Заметьте, что узлы ответили не в том порядке, в котором они отвечали выше. Ansible общается с хостами параллельно!

Кстати, при использовании модуля setup можно указывать * в выражении filter=. Как в shell.

Выбор хостов

Мы видели, что all означает «все хосты», но в Ansible есть
куча иных способов выбирать хосты:

  • host0.example.org:host1.example.org будет запущен на host0.example.org и на
    host1.example.org
  • host*.example.org будет запущен на всех хостах, названия которых начинается с ‘host’ и заканчивается на ‘.example.org’ (тоже как в shell)

Группировка хостов

Хосты в inventory можно группировать. Например, можно создать группу debian, группу web-servers, группу production и так далее.

[debian]
host0.example.org
host1.example.org
host2.example.org

Можно даже сократить:

[debian]
host[0-2].example.org

Если хотите задавать дочерние группы, используйте [groupname:children] и добавьте дочерние группы в него. Например, у нас есть разные дистрибутивы Линукса, их можно организовать следующим образом:

[ubuntu]
host0.example.org

[debian]
host[1-2].example.org

[linux:children]
ubuntu
debian

Установка переменных

Вы можете добавлять переменные для хостов в нескольких местах: в файле inventory, файлах переменных хостов, файлах переменных групп и др.

Обычно я задаю все переменные в файлах переменных групп/хостов (подробнее об этом позже). Однако, зачастую я использую переменные напрямую в файле inventory, например, ansible_ssh_host, которая задает IP-адрес хоста. По умолчанию Ansible резолвит имена хостов при соединении по SSH. Но когда вы инициализируете хост, он, возможно, еще не имеет IP-адреса. ansible_ssh_host будет полезен в таком случае.

При использовании команды ansible-playbook (а не обычной команды ansible), переменные можно задавать с помощью флага --extra-vars (или -e). О команде ansible-playbook мы поговорим в следующем шаге.

ansible_ssh_port, как вы могли догадаться, используется, чтобы задать порт соединения по SSH.

[ubuntu]
host0.example.org ansible_ssh_host=192.168.0.12 ansible_ssh_port=2222

Ansible ищет дополнительные переменные в файлах переменных групп и хостов. Он будет искать эти файлы в директориях group_vars и host_vars, внутри директории, где расположен главный файл inventory.

Ansible будет искать файлы по имени. Например, при использовании упомянутого ранее файле inventory, Ansible будет искать переменные host0.example.org в файлах:

  • group_vars/linux
  • group_vars/ubuntu
  • host_vars/host0.example.org

Если этих файлов не существует – ничего не произойдет, но если они существуют – они будут использованы.

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

Плейбуки Ansible

Концепция плейбуков очень проста: это просто набор команд Ansible (задач, tasks), похожих на те, что мы выполняли с утилитой ansible. Эти задачи направлены на конкретные наборы узлов/групп.

Пример с Apache (a.k.a. «Hello World!» в Ansible)

Продолжаем с допущением, что ваш файл inventory выглядит так (назовем его hosts):

[web]
host1.example.org

и все хосты — это системы на основе Debian.

Заметка: помните, что вы можете (и в нашем упражнении мы делаем это) использовать ansible_ssh_host чтобы задать реальный IP-адрес хоста. Вы также можете изменять inventory и использовать реальный hostname. В любом случае, используйте машину, с которой безопасно экспериментировать. На реальных хостах мы также добавляем ansible_ssh_user=root чтобы избежать потенциальных проблем с разными конфигурациями по умолчанию.

Давайте соберем плейбук, который установит Apache на машины группы web.

- hosts: web
  tasks:
    - name: Installs apache web server
      apt: pkg=apache2 state=installed update_cache=true

Нам всего лишь нужно сказать, что мы хотим сделать, используя правильные модули Ansible. Здесь мы используем модуль apt, который может устанавливать пакеты Debian. Мы также просим этот модуль обновить кэш.

Нам нужно имя для этой задачи. Это не обязательно, но желательно для вашего же удобства.

Ну, в целом было довольно легко! Теперь можно запустить плейбук (назовем его apache.yml):

ansible-playbook -i step-04/hosts -l host1.example.org step-04/apache.yml

Здесь step-04/hosts это файл inventory, -l ограничивает запуск хостом host1.example.org,
а apache.yml это наш плейбук.

При запуске команды будет вывод подобный этому:

PLAY [web] *********************

GATHERING FACTS *********************
ok: [host1.example.org]

TASK: [Installs apache web server] *********************
changed: [host1.example.org]

PLAY RECAP *********************
host1.example.org              : ok=2    changed=1    unreachable=0    failed=0    

Примечание: возможно, вы заметите проходящую мимо корову, если у вас установлен cowsay :-) Если она вам не нравится, можно отключить ее так: export ANSIBLE_NOCOWS="1".

Давайте проанализируем вывод строчка за строчкой.

PLAY [web] *********************

Ansible говорит нам, что play выполняется в группе web. Play — это набор инструкций Ansible, связанных с хостом. Если бы у нас был другой -host: blah в плейбуке, он бы тоже вывелся (но после того, как первый play завершен).

GATHERING FACTS *********************
ok: [host1.example.org]

Помните, когда мы использовали модуль setup? Перед каждым воспроизведением Ansible запускает его на каждом хосте и собирает факты. Если это не требуется (скажем, потому что вам не нужна никакая информация о хосте) можно добавить gather_facts: no под строкой хоста (на том же уровне, где находится tasks:).

TASK: [Installs apache web server] *********************
changed: [host1.example.org]

Теперь самое главное: наша первая и единственная задача запущена, и, так как там сказано changed, мы знаем, что она изменила что-то на хосте host1.example.org.

PLAY RECAP *********************
host1.example.org              : ok=2    changed=1    unreachable=0    failed=0

Наконец, Ansible выводит выжимку того, что произошло: две задачи были выполнены, и одна из них изменила что-то на хосте (это была наша задача apache; модуль setup ничего не меняяет).

Давайте запустим это еще раз и посмотрим, что произойдет:

$ ansible-playbook -i step-04/hosts -l host1.example.org step-04/apache.yml

PLAY [web] *********************

GATHERING FACTS *********************
ok: [host1.example.org]

TASK: [Installs apache web server] *********************
ok: [host1.example.org]

PLAY RECAP *********************
host1.example.org              : ok=2    changed=0    unreachable=0    failed=0    

Теперь changed равен ‘0’. Это совершенно нормально и является одной из главных особенностей Ansible: плейбук будет делать что-то, только если есть что делать. Это называется идемпотентностью. Это значит что можно запускать плейбук сколько угодно раз, но в итоге мы будем иметь машину в одном и том же состоянии (ну, только если вы не будете безумствовать с модулем shell, но тут Ansible уже не сможет ничего поделать).

Улучшаем набор apache

Мы установили apache, давайте теперь настроим virtualhost.

Улучшение плейбука

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

Давайте создадим директиорию под названием files и добавим нашу конфигурацию для host1.example.org, назовем ее awesome-app:

<VirtualHost *:80>
  DocumentRoot /var/www/awesome-app

  Options -Indexes

  ErrorLog /var/log/apache2/error.log
  TransferLog /var/log/apache2/access.log
</VirtualHost>

Теперь небольшое обнуление плейбука и все готово:

- hosts: web
  tasks:
    - name: Installs apache web server
      apt: pkg=apache2 state=installed update_cache=true

    - name: Push default virtual host configuration
      copy: src=files/awesome-app dest=/etc/apache2/sites-available/ mode=0640

    - name: Deactivates the default virtualhost
      command: a2dissite default

    - name: Deactivates the default ssl virtualhost
      command: a2dissite default-ssl

    - name: Activates our virtualhost
      command: a2ensite awesome-app
      notify:
        - restart apache

  handlers:
    - name: restart apache
      service: name=apache2 state=restarted

Поехали:

$ ansible-playbook -i step-05/hosts -l host1.example.org step-05/apache.yml

PLAY [web] *********************

GATHERING FACTS *********************
ok: [host1.example.org]

TASK: [Installs apache web server] *********************
ok: [host1.example.org]

TASK: [Push default virtual host configuration] *********************
changed: [host1.example.org]

TASK: [Deactivates the default virtualhost] *********************
changed: [host1.example.org]

TASK: [Deactivates the default ssl virtualhost] *********************
changed: [host1.example.org]

TASK: [Activates our virtualhost] *********************
changed: [host1.example.org]

NOTIFIED: [restart apache] *********************
changed: [host1.example.org]

PLAY RECAP *********************
host1.example.org              : ok=7    changed=5    unreachable=0    failed=0    

Круто! Ну, если задуматься, мы немного опережаем события. Не нужно ли проверить корректность конфигурации перед тем, как перезапускать apache? Чтобы не нарушать работоспособность сервиса в случае если конфигурация содержит ошибку.

Перезапуск в случае ошибки конфигурации

Мы установили apache, изменили virtualhost и перезапустили сервер. Но что, если мы хотим перезапускать сервер только когда конфигурация корректна?

Откатываемся, если есть проблемы

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

Давайте изменим файл конфигурации виртуального хоста awesome-app и сломаем его:

  <VirtualHost *:80>
    RocumentDoot /var/www/awesome-app

    Options -Indexes

    ErrorLog /var/log/apache2/error.log
    TransferLog /var/log/apache2/access.log
  </VirtualHost>

Как я сказал, если задача не может исполниться, обработка останавливается. Так что нужно удостовериться в валидности конфигурации перед перезапуском сервера. Мы также начнем с добавления виртуального хоста до удаления дефолтного виртуального хоста, так что последующий перезапуск (возможно, сделанный напрямую на сервере) не сломает apache.

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

- hosts: web
  tasks:
    - name: Installs apache web server
      apt: pkg=apache2 state=installed update_cache=true

    - name: Push future default virtual host configuration
      copy: src=files/awesome-app dest=/etc/apache2/sites-available/ mode=0640

    - name: Activates our virtualhost
      command: a2ensite awesome-app

    - name: Check that our config is valid
      command: apache2ctl configtest

    - name: Deactivates the default virtualhost
      command: a2dissite default

    - name: Deactivates the default ssl virtualhost
      command: a2dissite default-ssl

    notify:
        - restart apache

  handlers:
    - name: restart apache
      service: name=apache2 state=restarted

Поехали:

$ ansible-playbook -i step-06/hosts -l host1.example.org step-06/apache.yml

PLAY [web] *********************

GATHERING FACTS *********************
ok: [host1.example.org]

TASK: [Installs apache web server] *********************
ok: [host1.example.org]

TASK: [Push future default virtual host configuration] *********************
changed: [host1.example.org]

TASK: [Activates our virtualhost] *********************
changed: [host1.example.org]

TASK: [Check that our config is valid] *********************
failed: [host1.example.org] => {"changed": true, "cmd": ["apache2ctl", "configtest"], "delta": "0:00:00.045046", "end": "2013-03-08 16:09:32.002063", "rc": 1, "start": "2013-03-08 16:09:31.957017"}
stderr: Syntax error on line 2 of /etc/apache2/sites-enabled/awesome-app:
Invalid command 'RocumentDoot', perhaps misspelled or defined by a module not included in the server configuration
stdout: Action 'configtest' failed.
The Apache error log may have more information.

FATAL: all hosts have already failed -- aborting

PLAY RECAP *********************
host1.example.org              : ok=4    changed=2    unreachable=0    failed=1    

Как вы заметили, apache2ctl возвращает код ошибки 1. Ansible видит это и останавливает работу. Отлично!

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

(прим. переводичка: хабраюзер @clickfreak в комментариях советует взлянуть на специальную фичу Ansible 2.x).

Использование условий

Мы установили Apache, добавили виртуальный хост и перезапустили сервер. Но мы хотим вернуться к рабочему состоянию, если что-то пошло не так.

Возврат при проблемах

Здесь нет никакой магии. Прошлая ошибка – не вина Ansible. Это не система резервного копирования, и она не умеет отказывать все к прошлым состояниям. Безопасность плейбуков – ваша ответственность. Ansible просто не знает, как отменить эффект a2ensite awesome-app.

Как было сказано ранее, если задача не может исполниться – обработка останавливается… но мы можем принять ошибку (и нам нужно это делать). Так мы и поступим: продолжим обработку в случае ошибки, но только чтобы вернуть все к рабочему состоянию.

- hosts: web
  tasks:
    - name: Installs apache web server
      apt: pkg=apache2 state=installed update_cache=true

    - name: Push future default virtual host configuration
      copy: src=files/awesome-app dest=/etc/apache2/sites-available/ mode=0640

    - name: Activates our virtualhost
      command: a2ensite awesome-app

    - name: Check that our config is valid
      command: apache2ctl configtest
      register: result
      ignore_errors: True

    - name: Rolling back - Restoring old default virtualhost
      command: a2ensite default
      when: result|failed

    - name: Rolling back - Removing our virtualhost
      command: a2dissite awesome-app
      when: result|failed

    - name: Rolling back - Ending playbook
      fail: msg="Configuration file is not valid. Please check that before re-running the playbook."
      when: result|failed

    - name: Deactivates the default virtualhost
      command: a2dissite default

    - name: Deactivates the default ssl virtualhost
      command: a2dissite default-ssl

    notify:
        - restart apache

  handlers:
    - name: restart apache
      service: name=apache2 state=restarted

Ключевое слово register записывает вывод команды apache2ctl configtest (exit
status, stdout, stderr, …), и when: result|failed проверяет, содержит ли переменная
(result) статус failed.

Поехали:

$ ansible-playbook -i step-07/hosts -l host1.example.org step-07/apache.yml

PLAY [web] *********************

GATHERING FACTS *********************
ok: [host1.example.org]

TASK: [Installs apache web server] *********************
ok: [host1.example.org]

TASK: [Push future default virtual host configuration] *********************
ok: [host1.example.org]

TASK: [Activates our virtualhost] *********************
changed: [host1.example.org]

TASK: [Check that our config is valid] *********************
failed: [host1.example.org] => {"changed": true, "cmd": ["apache2ctl", "configtest"], "delta": "0:00:00.051874", "end": "2013-03-10 10:50:17.714105", "rc": 1, "start": "2013-03-10 10:50:17.662231"}
stderr: Syntax error on line 2 of /etc/apache2/sites-enabled/awesome-app:
Invalid command 'RocumentDoot', perhaps misspelled or defined by a module not included in the server configuration
stdout: Action 'configtest' failed.
The Apache error log may have more information.
...ignoring

TASK: [Rolling back - Restoring old default virtualhost] *********************
changed: [host1.example.org]

TASK: [Rolling back - Removing our virtualhost] *********************
changed: [host1.example.org]

TASK: [Rolling back - Ending playbook] *********************
failed: [host1.example.org] => {"failed": true}
msg: Configuration file is not valid. Please check that before re-running the playbook.

FATAL: all hosts have already failed -- aborting

PLAY RECAP *********************
host1.example.org              : ok=7    changed=4    unreachable=0    failed=1    

Кажется, все работает как нужно. Давайте попробуем перезапустить apache:

$ ansible -i step-07/hosts -m service -a 'name=apache2 state=restarted' host1.example.org
host1.example.org | success >> {
    "changed": true,
    "name": "apache2",
    "state": "started"
}

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

Деплоим сайт с помощью Git

Мы установили Apache, добавили виртуальный хост и безопасно перезапустили сервер. Теперь давайте используем модуль git чтобы сделать деплой приложения.

Модуль git

Ну, честно говоря, тут все будет просто, ничего нового. Модуль git это просто еще один модуль. Но давайте попробуем что-нибудь интересное. А позже это пригодится, когда мы будем работать с ansible-pull.

Виртуальный хост задан, но нам нужно внести пару изменений чтобы закончить деплой. Мы деплоим приложение на PHP, так что нужно установить пакет libapache2-mod-php5. Также нужно установить сам git, так как, очевидно, модуль git требует его наличия.

Можно сделать так:

    ...
    - name: Installs apache web server
      apt: pkg=apache2 state=installed update_cache=true

    - name: Installs php5 module
      apt: pkg=libapache2-mod-php5 state=installed

    - name: Installs git
      apt: pkg=git state=installed
    ...

но в Ansible есть способ лучше. Он может проходить по набору элементов и использовать каждый в определенном действии, вот так:

- hosts: web
  tasks:
    - name: Updates apt cache
      apt: update_cache=true

    - name: Installs necessary packages
      apt: pkg={{ item }} state=latest
      with_items:
        - apache2
        - libapache2-mod-php5
        - git

    - name: Push future default virtual host configuration
      copy: src=files/awesome-app dest=/etc/apache2/sites-available/ mode=0640

    - name: Activates our virtualhost
      command: a2ensite awesome-app

    - name: Check that our config is valid
      command: apache2ctl configtest
      register: result
      ignore_errors: True

    - name: Rolling back - Restoring old default virtualhost
      command: a2ensite default
      when: result|failed

    - name: Rolling back - Removing out virtualhost
      command: a2dissite awesome-app
      when: result|failed

    - name: Rolling back - Ending playbook
      fail: msg="Configuration file is not valid. Please check that before re-running the playbook."
      when: result|failed

    - name: Deploy our awesome application
      git: repo=https://github.com/leucos/ansible-tuto-demosite.git dest=/var/www/awesome-app
      tags: deploy

    - name: Deactivates the default virtualhost
      command: a2dissite default

    - name: Deactivates the default ssl virtualhost
      command: a2dissite default-ssl
      notify:
        - restart apache

  handlers:
    - name: restart apache
      service: name=apache2 state=restarted

Поехали:

$ ansible-playbook -i step-08/hosts -l host1.example.org step-08/apache.yml

PLAY [web] *********************

GATHERING FACTS *********************
ok: [host1.example.org]

TASK: [Updates apt cache] *********************
ok: [host1.example.org]

TASK: [Installs necessary packages] *********************
changed: [host1.example.org] => (item=apache2,libapache2-mod-php5,git)

TASK: [Push future default virtual host configuration] *********************
changed: [host1.example.org]

TASK: [Activates our virtualhost] *********************
changed: [host1.example.org]

TASK: [Check that our config is valid] *********************
changed: [host1.example.org]

TASK: [Rolling back - Restoring old default virtualhost] *********************
skipping: [host1.example.org]

TASK: [Rolling back - Removing out virtualhost] *********************
skipping: [host1.example.org]

TASK: [Rolling back - Ending playbook] *********************
skipping: [host1.example.org]

TASK: [Deploy our awesome application] *********************
changed: [host1.example.org]

TASK: [Deactivates the default virtualhost] *********************
changed: [host1.example.org]

TASK: [Deactivates the default ssl virtualhost] *********************
changed: [host1.example.org]

NOTIFIED: [restart apache] *********************
changed: [host1.example.org]

PLAY RECAP *********************
host1.example.org              : ok=10   changed=8    unreachable=0    failed=0    

Теперь можно перейти на http://192.168.33.11 и увидеть котенка и имя сервера.

Строка tags: deploy позволяет запустить определнную порцию плейбука. Допустим, вы запушили новую версию сайта. Вы хотите ускорить процесс и запустить только ту часть, которая ответственна за деплой. Это можно сделать с помощью тегов. Естественно, «deploy» — это просто строка, можно задавать любую. Давайте посмотрим, как это можно использовать:

$ ansible-playbook -i step-08/hosts -l host1.example.org step-08/apache.yml -t deploy
X11 forwarding request failed on channel 0

PLAY [web] *****

GATHERING FACTS *****
ok: [host1.example.org]

TASK: [Deploy our awesome application] *****
changed: [host1.example.org]

PLAY RECAP *****
host1.example.org: ok=2 changed=1 unreachable=0 failed=0

Добавляем еще один веб-сервер

У нас есть один веб-сервер. Мы хотим два.

Обновление inventory

Мы ожидаем наплыва трафика, так что давайте добавим еще один веб-сервер и балансировщик, который мы настроим в следующем шаге. Давайте закончим с inventory:

[web]
host1.example.org ansible_ssh_host=192.168.33.11 ansible_ssh_user=root
host2.example.org ansible_ssh_host=192.168.33.12 ansible_ssh_user=root

[haproxy]
host0.example.org ansible_ssh_host=192.168.33.10 ansible_ssh_user=root

Помните, здесь мы указываем ansible_ssh_host потому что хост имеет не тот IP, что ожидается. Можно добавить эти хосты к себе в /etc/hosts или использовать реальные имена (что вы и будете делать в обычной ситуации).

Сборка второго веб-сервера

Мы не зря напрягались перед этим. Деплой второго сервера очень прост:

$ ansible-playbook -i step-09/hosts step-09/apache.yml

PLAY [web] *********************

GATHERING FACTS *********************
ok: [host2.example.org]
ok: [host1.example.org]

TASK: [Updates apt cache] *********************
ok: [host1.example.org]
ok: [host2.example.org]

TASK: [Installs necessary packages] *********************
ok: [host1.example.org] => (item=apache2,libapache2-mod-php5,git)
changed: [host2.example.org] => (item=apache2,libapache2-mod-php5,git)

TASK: [Push future default virtual host configuration] *********************
ok: [host1.example.org]
changed: [host2.example.org]

TASK: [Activates our virtualhost] *********************
changed: [host2.example.org]
changed: [host1.example.org]

TASK: [Check that our config is valid] *********************
changed: [host2.example.org]
changed: [host1.example.org]

TASK: [Rolling back - Restoring old default virtualhost] *********************
skipping: [host1.example.org]
skipping: [host2.example.org]

TASK: [Rolling back - Removing out virtualhost] *********************
skipping: [host1.example.org]
skipping: [host2.example.org]

TASK: [Rolling back - Ending playbook] *********************
skipping: [host1.example.org]
skipping: [host2.example.org]

TASK: [Deploy our awesome application] *********************
ok: [host1.example.org]
changed: [host2.example.org]

TASK: [Deactivates the default virtualhost] *********************
changed: [host1.example.org]
changed: [host2.example.org]

TASK: [Deactivates the default ssl virtualhost] *********************
changed: [host2.example.org]
changed: [host1.example.org]

NOTIFIED: [restart apache] *********************
changed: [host1.example.org]
changed: [host2.example.org]

PLAY RECAP *********************
host1.example.org              : ok=10   changed=5    unreachable=0    failed=0    
host2.example.org              : ok=10   changed=8    unreachable=0    failed=0    

Все, что нужно, это удалить -l host1.example.org из командной строки. Помните, -l позволяет ограничить хосты для запуска. Теперь ограничения не требуется, и запуск произойдет на всех машинах группы web.

Если бы в группе web были другие машины, и нам нужно было бы запустить плейбук только на некоторых из них, можно было бы использовать, например, такое: -l firsthost:secondhost:....

Теперь у нас есть чудесная ферма веб-серверов, давайте превратим ее в кластер с помощью балансировщика нагрузок.

Шаблоны

Мы будем использовать haproxy в качестве балансировщика. Установка такая же, как с apache. Но конфигурация немного сложнее, потому что нам нужно указать список всех веб-серверов в конфигурации haproxy. Как это сделать?

Шаблон конфигурации HAProxy

Ansible использует Jinja2, систему шаблонов для Python. Внутри Jinja2-шаблона можно использовать любую переменную, которая определена Ansible’ом.

Например, если нужно вывести на экран inventory_name хоста, для которого собран шаблон, то можно просто написать {{ inventory_hostname }} в Jinja2-шаблоне. Или, если нужно вывести IP-адрес первого ethernet-интерфейса (о котором Ansible знает благодаря модулю setup), то можно написать {{ ansible_eth1['ipv4']['address'] }}.

Jinja2 также поддерживает условия, циклы и прочее.

Давайте создадим директорию templates/ с Jinja-шаблоном внутри. Назовем его haproxy.cfg.j2. Расширение .j2 даем прото для удобства, оно не обязательно.

global
    daemon
    maxconn 256

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

listen cluster
    bind {{ ansible_eth1['ipv4']['address'] }}:80
    mode http
    stats enable
    balance roundrobin
{% for backend in groups['web'] %}
    server {{ hostvars[backend]['ansible_hostname'] }} {{ hostvars[backend]['ansible_eth1']['ipv4']['address'] }} check port 80
{% endfor %}
    option httpchk HEAD /index.php HTTP/1.0

Тут есть несколько новых для нас деталей.

Во-первых, {{ ansible_eth1['ipv4']['address'] }} заменится на IP балансировщика нагрузки на eth1.

Дальше у нас есть цикл. Он используется для генерации списка бэкенд-серверов. Каждый шаг цикла соответствует одному хосту из группы [web], и каждый такой хост будет записан в переменную backend. С помощью фактов хоста для каждого из хостов будет сгенерирована строка. Факты всех хостов доступны через переменную hostvars, поэтому достать переменные (например, имя хоста или IP, как в нашем случае) из других хостов очень легко.

Можно было написать список хостов вручную, у нас их всего два. Но мы надеемся, что популярность заставит нас заводить сотни серверов. Так что, при добавлении или изменении серверов нам нужно лишь обновить группу [web].

HAProxy playbook

Самое сложное позади. Написать плейбук для устновки и конфигурации HAproxy очень легко:

- hosts: haproxy
  tasks:
    - name: Installs haproxy load balancer
      apt: pkg=haproxy state=installed update_cache=yes

    - name: Pushes configuration
      template: src=templates/haproxy.cfg.j2 dest=/etc/haproxy/haproxy.cfg mode=0640 owner=root group=root
      notify:
        - restart haproxy

    - name: Sets default starting flag to 1
      lineinfile: dest=/etc/default/haproxy regexp="^ENABLED" line="ENABLED=1"
      notify:
        - restart haproxy

  handlers:
    - name: restart haproxy
      service: name=haproxy state=restarted

Выглядит знакомо, правда? Новый модуль тут только один: template. У него такие же аргументы, как у copy. А еще мы ограничили этот плейбук группой haproxy.

А теперь… попробуем. В нашем inventory содержатся только необходимые для кластера хосты, поэтому нам не нужно делать дополнительных ограничений и можно даже запустить оба плейбука. Ну, на самом деле, нам нужно запускать их одновременно, так как haproxy-плейбуку нужны факты из двух веб-серверов. Чуть позже мы узнаем, как избежать этого.

$ ansible-playbook -i step-10/hosts step-10/apache.yml step-10/haproxy.yml

PLAY [web] *********************

GATHERING FACTS *********************
ok: [host1.example.org]
ok: [host2.example.org]

TASK: [Updates apt cache] *********************
ok: [host1.example.org]
ok: [host2.example.org]

TASK: [Installs necessary packages] *********************
ok: [host1.example.org] => (item=apache2,libapache2-mod-php5,git)
ok: [host2.example.org] => (item=apache2,libapache2-mod-php5,git)

TASK: [Push future default virtual host configuration] *********************
ok: [host2.example.org]
ok: [host1.example.org]

TASK: [Activates our virtualhost] *********************
changed: [host1.example.org]
changed: [host2.example.org]

TASK: [Check that our config is valid] *********************
changed: [host1.example.org]
changed: [host2.example.org]

TASK: [Rolling back - Restoring old default virtualhost] *********************
skipping: [host1.example.org]
skipping: [host2.example.org]

TASK: [Rolling back - Removing out virtualhost] *********************
skipping: [host1.example.org]
skipping: [host2.example.org]

TASK: [Rolling back - Ending playbook] *********************
skipping: [host1.example.org]
skipping: [host2.example.org]

TASK: [Deploy our awesome application] *********************
ok: [host2.example.org]
ok: [host1.example.org]

TASK: [Deactivates the default virtualhost] *********************
changed: [host1.example.org]
changed: [host2.example.org]

TASK: [Deactivates the default ssl virtualhost] *********************
changed: [host2.example.org]
changed: [host1.example.org]

NOTIFIED: [restart apache] *********************
changed: [host2.example.org]
changed: [host1.example.org]

PLAY RECAP *********************
host1.example.org              : ok=10   changed=5    unreachable=0    failed=0    
host2.example.org              : ok=10   changed=5    unreachable=0    failed=0    

PLAY [haproxy] *********************

GATHERING FACTS *********************
ok: [host0.example.org]

TASK: [Installs haproxy load balancer] *********************
changed: [host0.example.org]

TASK: [Pushes configuration] *********************
changed: [host0.example.org]

TASK: [Sets default starting flag to 1] *********************
changed: [host0.example.org]

NOTIFIED: [restart haproxy] *********************
changed: [host0.example.org]

PLAY RECAP *********************
host0.example.org              : ok=5    changed=4    unreachable=0    failed=0    

Вроде все хорошо. Зайдите на http://192.168.33.10/ и оцените результат. Кластер задеплоен! Можно даже посмотреть на статистику HAproxy: http://192.168.33.10/haproxy?stats.

Снова переменные

Итак, мы установили балансировщик нагрузки, и он работает нормально. Мы берем переменные из фактов и используем их для генерации конфигурации.

Ansible также поддерживает другие виды переменных. Мы уже видели ansible_ssh_host в файле inventory, но теперь используем переменные, которые заданы в файлах host_vars и group_vars.

Тонкая настройка конфигурации HAProxy

Обычно HAProxy проверяет, живы ли бэкенды. Если бэкенд не откликается, то он удаляется из пула, и HAProxy больше не шлет ему запросы.

У бэкэндов может быть указан вес (от 0 до 256). Чем выше вес, тем больше запросов сервер получит по сравнению с другими серверами. Это полезно, когда узлы отличаются по мощности и нужно направить трафик в соответствии с этим.

Мы используем переменные для настройки этих параметров.

Group-переменные

Интервал проверки haproxy будет задан в файле group_vars. Таким образом, все экземпляры haproxy унаследуют это.

Нужно создать файл group_vars/haproxy внутри директории inventory. Название файла должно совпадать с названием группы, для которой задаются переменные. Если бы мы задавали переменные для группы web, то назвали бы файл group_vars/web.

haproxy_check_interval: 3000
haproxy_stats_socket: /tmp/sock

Название переменной может быть любым. Естественно, рекомендуется давать осмысленные названия, но каких-то специальных правил нет. Можно делать даже комплексные переменные (то есть Python dict) вот так:

haproxy:
    check_interval: 3000
    stats_socket: /tmp/sock

Это дело вкуса. Такой подход позволяет делать логическую группировку. Мы пока будем использовать простые переменные.

Переменные хоста

С переменными хоста такая же история, но файлы живут в директори host_vars. Давайте зададим вес бэкенда в host_vars/host1.example.com:

haproxy_backend_weight: 100

и для host_vars/host2.example.com:

haproxy_backend_weight: 150

Если бы мы задали haproxy_backend_weight в group_vars/web, то он бы использовался по-умолчанию:
переменные из файла host_vars имеют приоритет перед переменными из group_vars.

Обновляем шаблон

Теперь необходимо обновить шаблон, чтобы он использовал эти переменные.

global
    daemon
    maxconn 256
{% if haproxy_stats_socket %}
    stats socket {{ haproxy_stats_socket }}
{% endif %}

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

listen cluster
    bind {{ ansible_eth1['ipv4']['address'] }}:80
    mode http
    stats enable
    balance roundrobin
{% for backend in groups['web'] %}
    server {{ hostvars[backend]['ansible_hostname'] }} {{ hostvars[backend]['ansible_eth1']['ipv4']['address'] }} check inter {{ haproxy_check_interval }} weight {{ hostvars[backend]['haproxy_backend_weight'] }} port 80
{% endfor %}
    option httpchk HEAD /index.php HTTP/1.0

Заметили блок {% if ...? Этот блок будет отработан, если условие верно. Так что, если мы где-нибудь зададим haproxy_stats_socket для балансировщика нагрузки (можно даже добавить --extra-vars="haproxy_stats_sockets=/tmp/sock" при вызове из командной строки), то блок будет добавлен в сгенерированный конфигурационный файл.

Имейте ввиду, что такой метод очень плох с точки зрения безопасности!

Поехали:

ansible-playbook -i step-11/hosts step-11/haproxy.yml

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

- hosts: web
- hosts: haproxy
  tasks:
    - name: Installs haproxy load balancer
      apt: pkg=haproxy state=installed update_cache=yes

    - name: Pushes configuration
      template: src=templates/haproxy.cfg.j2 dest=/etc/haproxy/haproxy.cfg mode=0640 owner=root group=root
      notify:
        - restart haproxy

    - name: Sets default starting flag to 1
      lineinfile: dest=/etc/default/haproxy regexp="^ENABLED" line="ENABLED=1"
      notify:
        - restart haproxy

  handlers:
    - name: restart haproxy
      service: name=haproxy state=restarted

Видите? Мы добавили пустой блок для веб-хостов в самом начале. В нем ничего не происходит. Но факт его наличия заставит Ansible собрать факты для группы web. Это необходимо, потому что плейбук haproxy использует факты из этой группы. Если не сделать этого, то Ansible будет ругаться, что ключа ansible_eth1 не существует.

Мигрируем к ролям!

Теперь, когда все плейбуки готовы, давайте все отрефакторим! Мы переделаем все через роли. Роли — это просто еще один способ организации файлов, но у них есть несколько интересных возможностей. Не будем вдаваться в детали, все они описаны в
документации Ansible. Моя любимая фича — это зависимости ролей: роль B может зависеть от другой роли A. Поэтому при применении роли B, автоматически будет применена роль A.

Структура ролей

Роли добавляют немного «магии» в Ansible: они предполагают особую организацию файлов. Роли полагается структурировать определенным образом, хотя вы можете делать это как угодно вам. Тем не менее, если придерживаться соглашений, вам будет гораздо легче создавать модульные плейбуки. Содержать код в порядке будет гораздо легче. Рубисты называют это «convention over configuration».

Структура файлов для ролей такая:

roles
  |
  |_some_role
       |
       |_files
       |   |
       |   |_file1
       |   |_...
       |
       |_templates
       |   |
       |   |_template1.j2
       |   |_...
       |
       |_tasks
       |   |
       |   |_main.yml
       |   |_some_other_file.yml
       |   |_ ...
       |
       |_handlers
       |    |
       |    |_main.yml
       |    |_some_other_file.yml
       |    |_ ...
       |
       |_vars
       |    |
       |    |_main.yml
       |    |_some_other_file.yml
       |    |_ ...
       |
       |_meta
            |
            |_main.yml
            |_some_other_file.yml
            |_ ...

Довольно просто.

Файлы main.yml не обязательны. Но если они присутствуют, то роли добавят их к отработке автоматически. Эти файлы можно использовать для добавления других тасков и хэндлеров.

Обратите внимание на директории vars и meta. vars нужна для случаев, когда есть куча переменных, связанных с ролью. Но мне лично не нравится задавать переменные в ролях и сценариях напрямую. Я считаю, что переменные должны быть частью конфигурации, а сценарии — это структура. Иными словами, я считаю сценарии фабриками, а данные — параметрами для фабрик. Поэтому я предпочитаю видеть «данные» (например, переменные) вне ролей и сценариев. Тогда мне легче шарить роли и не раскрывать слишком много информации о внутренностях серверов. Но это дело личных предпочтений. Ansible предоставляет вам выбор.

В директории meta находятся зависимости, но об этом поговорим в следующий раз. Сценарии лежат в директории roles.

Создаем роль Apache

Теперь у нас достаточно знаний, чтобы создать роль для apache на основе нашего плейбука.

Несколько простых шагов:

  • создать директорию ролей и структуру роли apache
  • вынести хэндлер apache в файл roles/apache/handlers/main.yml
  • перенести конфигурационный файл apache awesome-app в roles/apache/files/
  • создать плейбук для роли

Задаем структуру

Все просто:

mkdir -p step-12/roles/apache/{tasks,handlers,files}

Теперь копируем таски из apache.yml в main.yml. Файл выглядит так:

- name: Updates apt cache
  apt: update_cache=true

- name: Installs necessary packages
  apt: pkg={{ item }} state=latest
  with_items:
    - apache2
    - libapache2-mod-php5
    - git

...

- name: Deactivates the default ssl virtualhost
  command: a2dissite default-ssl
  notify:
    - restart apache

Это не полный текст файла, а просто иллюстрация. Файл в точности повторяет содержание
apache.yml между tasks: и handlers:.

Мы также убрали обращения к директориям files/ и templates/ в тасках. Так как используется стандартная структура ролей, Ansible сам знает, в какие директории смотреть.

Выносим хэндлер

Нужно создать файл step-12/roles/apache/handlers/main.yml:

- name: restart apache
  service: name=apache2 state=restarted

Переносим файл конфигурации

Еще проще:

cp step-11/files/awesome-app step-12/roles/apache/files/

Роль apache работает. Но нам нужен способ запустить ее.

Создаем плейбук роли

Давайте создадим плейбук верхнего уровня для связывания хостов и групп хостов с ролями. Назовем файл site.yml, так как нам нужна общая конфигурация сайта. Заодно добавим туда haproxy:

- hosts: web
  roles:
    - { role: apache }

- hosts: haproxy
  roles:
    - { role: haproxy }

Совсем не сложно. Теперь давайте создадим роль haproxy:

mkdir -p step-12/roles/haproxy/{tasks,handlers,templates}
cp step-11/templates/haproxy.cfg.j2 step-12/roles/haproxy/templates/

потом извлечем хэндлер и удалим упоминание templates/.

Попробуем?:

ansible-playbook -i step-12/hosts step-12/site.yml

Если все хорошо, то мы увидим «PLAY RECAP»:

host0.example.org          : ok=5    changed=2    unreachable=0 failed=0
host1.example.org          : ok=10   changed=5    unreachable=0 failed=0
host2.example.org          : ok=10   changed=5    unreachable=0 failed=0

Вы наверное заметили, что запуск всех ролей в site.yml занимает много времени. Что если нужно сделать изменения только для веб-серверов? Легко! Используем limit-флаг:

ansible-playbook -i step-12/hosts -l web step-12/site.yml

На этом миграция на роли закончена.

(От переводчика: в оригинальном пособии в будущем появится еще как минимум одна глава. Я добавлю ее в эту публикацию или создам новую).

Пособие по Ansible

Это пособие познакомит вас Ansible пошагово. Вам понадобится (виртуальная или реальная) машина, которая будет выступать в роли ansible node. Окружение для Vagrant идет в комплекте с этим пособием.

Ansible это программное решение для удаленного управления конфигурациями. Оно позволяет настраивать удаленные узлы. Главное его отличие от других подобных систем – Ansible использует (потенциально) существующую инфраструктуру SSH, в то время как другие (chef, puppet, …) требует установки специального PKI-окружения.

Ansible использует т.н. push mode: конфигурация «проталкивается» (push) с главной машины. Другие CM-системы обычно поступают наоборот – узлы «тянут» (pull) конфигурацию с главной машины.

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

Что нужно для Ansible

Необходимы следующие Python-модули

  • python-yaml
  • python-jinja2

На Debian/Ubuntu запустите:

sudo apt-get install python-yaml python-jinja2 python-paramiko python-crypto

У вас также должна быть комбинация ключей в ~/.ssh.

Установка Ansible

Из исходников

Ветка devel всегда стабильна, так что используем ее. Возможно, вам нужно будет установить git (sudo apt-get install git на Debian/Ubuntu).

git clone git://github.com/ansible/ansible.git
cd ./ansible

Теперь можно загрузить окружение Ansible.

source ./hacking/env-setup

Из deb пакета

sudo apt-get install make fakeroot cdbs python-support
git clone git://github.com/ansible/ansible.git
cd ./ansible
make deb
sudo dpkg -i ../ansible_1.1_all.deb (version may vary)

В этом пособии мы допускаем, что вы использовали именно этот способ.

Клонирование пособия

git clone https://github.com/freetonik/ansible-tuto-rus.git
cd ansible-tuto-rus

Использование Vagrant в пособии

Настоятельно рекомендуем использовать Vagrant при прохождении этого пособия. Если вы еще не установили его, следуйте простым инструкциям в step-00/README.md.

Если вы хотите продолжить без Vagnrant, переходите к шагу
step-01/README.md.

Содержание

  • 00. Установка Vagrant
  • 01. Basic inventory
  • 02. Первые модули и факты
  • 03. Группы и переменные
  • 04. Playbooks
  • 05. Плейбуки, отправка файлов на узлы
  • 06. Плейбуки и ошибки
  • 07. Плейбуки и условия
  • 08. Модуль Git
  • 09. Расширение до нескольких хостов
  • 10. Шаблоны
  • 11. Снова переменные
  • 12. Миграция к ролям

Contributing

Спасибо всем, кто участвовал в создании этого пособия:

  • Aladin Jaermann
  • Alexis Gallagher
  • Atilla Mas
  • Benny Wong
  • Chris Schmitz
  • dalton
  • Daniel Howard
  • David Golden
  • Eugene Kalinin
  • Hartmut Goebel
  • Justin Garrison
  • Karlo
  • Marchenko Alexandr
  • mxxcon
  • Patrick Pelletier
  • Pierre-Gilles Levallois
  • Ruud Kamphuis
  • Victor Boivie

Я использую Ansible почти с самого его появления, и я узнал очень много в процессе написания пособия. Если вы хотите поучаствовать – буду рад вашим дополнениям.

От переводчика

Это перевод туториала от Michel Blanc. Разделы, работа над которыми идет в данный момент, находятся в ветке writing в оригинальном репозитории.

Если вы хотите дополнить/исправить перевод, пожалуйста, откройте Pull Request.

Ansible – одна из систем управления конфигурациями (автоматизации настройки и развертывания серверов).

Особенность Ansible – наличие управляющего сервера, с которого отправляются команды или наборы инструкций (playbooks) на удаленные хосты посредством протокола SSH.

Ansible успешно решает следующие задачи:

  • правильная и быстрая настройка серверов до нужной конфигурации;
  • управление развертыванием облачных серверов (через API, с помощью Docker);
  • установка и обновление приложений;
  • координация звеньев инфраструктуры для выполнения развертываний;
  • централизованный сбор логов.

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

Чтобы «пощупать» Ansible потребуется один управляющий сервер и несколько хостов для настройки. На управляющем сервере у нас установлена операционная система Debian Wheezy, на удаленных хостах — Ubuntu 14.04.

Установка Ansible не должна вызывать трудностей:

  • перед установкой обновим имеющиеся в системе пакеты:
apt-get update && apt-get upgrade
  • непосредственно установка Ansible:
echo 'deb http://http.debian.net/debian wheezy-backports main' > /etc/apt/sources.list.d/backports.list
apt-get update
apt-get -t wheezy-backports install "ansible"

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

  • ./ansible.cfg – в текущем каталоге;
  • ~/.ansible.cfg — в домашнем каталоге;
  • /etc/ansible/ansible.cfg — в каталоге, созданном при установке через менеджер пакетов (наш случай).

Примечание. Некоторые параметры или даже всю конфигурацию также можно переопределить в playbook или переменных окружения.

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

  • hostfile: — путь к inventory file, содержит список ip-адресов (или имен) хостов для подключения;
  • library: — путь к модулям Ansible;
  • forks: — кол-во потоков, которые может создать Ansible;
  • sudo_user: — пользователь, от которого запускаются команды/инструкции на удаленных хостах;
  • remote_port: — порт для подключения по протоколу SSH;
  • host_key_checking: — включить/отключить проверку SSH–ключа на удаленном хосте;
  • timeout: — таймаут подключения по SSH;
  • log_path: — путь к файлу логов.

Перечислим имена удаленных хостов, с которыми будем проводить эксперименты в файле /etc/ansible/hosts. Для этого:

  • переходим в каталог /etc/ansible:
cd /etc/ansible
  • переименуем существующий файл hosts:
mv hosts hosts.orig
  • и создадим новый файл hosts:
touch hosts

Содержимое файла будет следующим:

[test]
test-1
test-2

Также нужно сгенерировать на управляющем сервере ключ для доступа к удаленным хостам по SSH, для этого используем утилиту ssh-keygen. Копируем публичный ключ на удаленные хосты утилитой ssh-copy-id.

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

  • пингуем удаленные хосты:
ansible test -m ping
test-1 | success >> {
    "changed": false, 
    "ping": "pong"
}

test-2 | success >> {
    "changed": false, 
    "ping": "pong"
}
  • просмотрим информацию об использовании оперативной памяти на удаленных хостах:
ansible test -a "free -h"
test-1 | success | rc=0 >>
             total       used       free     shared    buffers     cached
Mem:          7.6G       6.4G       1.2G       471M        64M       1.2G
-/+ buffers/cache:       5.2G       2.4G
Swap:         4.0G       616M       3.4G

test-2 | success | rc=0 >>
             total       used       free     shared    buffers     cached
Mem:          3.9G       3.3G       573M       333M       4.8M       442M
-/+ buffers/cache:       2.9G       1.0G
Swap:         4.0G       1.7G       2.3G

Работа с наборами инструкций (playbooks) — главная задача Ansible. Наборы инструкций содержат задачи и должны быть описаны в формате YAML. Задача использует часть кода-модуля, который может быть написан на любом языке программирования (но есть одно важное требование — сообщения от модулей должны быть в формате JSON).

Несколько слов о правилах написания YAML-файлов:

  • все YAML-файлы должны начинаться с ---. Эта часть формата YAML означает начало документа;
  • члены списка должны начинаться с пробела или - и иметь одинаковые отступы от начала строки;
  • комментарии начинаются с символа #;
  • словарь описывается в виде ключ: значение и может быть представлен в сокращенной форме.

В playbook может находиться список удаленных хостов, переменных пользователя, задач, обработчиков (как уже упоминалось ранее, здесь можно переопределять параметры конфигурации). Каждый набор инструкций (за исключением ролей) обязательно должен иметь поле hosts:. Кроме этого, набор инструкций содержит задачи — список действий, которые нужно выполнить на удаленных хостах.

Давайте разберемся с синтаксисом конкретного набора инструкций на примере установки web-сервера nginx:

  • создаем каталог, для хранения наших playbooks:
mkdir /etc/ansible/playbooks
  • в каталоге /etc/ansible/playbooks создаем файл install_nginx.yml:
touch /etc/ansible/playbooks/install_nginx.yml

Содержимое файла следующее:

---
- hosts: test
  tasks:
 
  - name: Install package nginx
    apt: name=nginx update_cache=yes
    sudo: yes
 
  - name: Starting service nginx
    service: name=nginx state=started
    sudo: yes

В данном примере поле hosts: содержит группу, на которой будет запущена задача (test).

Узнать, на каких хостах будет происходить работа, можно командой:

ansible-playbook <имя_набора_инструкций> --list-host

Поле tasks: содержит имена задач (Install package nginx и Starting service nginx), имена модулей, которые должны выполняться (apt и service) и аргументы, необходимые для выполнения модуля (для первого это name=nginx update_cache=yes, для второго — name=nginx state=started). Дополнительно указано, что для выполнения задач необходимы права суперпользователя (sudo: yes).

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

ansible-playbook install_nginx.yml 

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

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

TASK: [Install package nginx] ************************************************* 
changed: [test-1]
changed: [test-2]

TASK: [Starting service nginx] ************************************************ 
ok: [test-1]
ok: [test-2]

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

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

() translation by (you can also view the original English article)

Обзор

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

Что такое Ansible?

Ansible — это инструмент управления конфигурацией и оркестровки. Он работает в том же домене, что и Puppet, Chef и Saltstack. Это означает, что с помощью Ansible вы можете удаленно предоставлять целый парк удаленных серверов, устанавливать и развертывать на них программное обеспечение и отслеживать их удаленно.

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

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

Начало работы с Ansible

Ansible работает на управляющей машине и может управлять серверами под управлением любой операционной системы, но управляющая машина не может быть машиной Windows на данный момент. Я буду использовать Mac OS X в этом руководстве в качестве контрольной машины.

Установка

Ansible требует Python 2.6 или 2.7. Чтобы установить его, введите:

pip install ansible

В Mac OS X рекомендуется увеличить количество дескрипторов файлов:

sudo launchctl limit maxfiles 1024 unlimited

Если вы видите ошибку типа «Слишком много открытых файлов», вам, вероятно, нужно это сделать.

Чтобы проверить правильность установки Ansible, введите ansible --version. Вы увидите следующее:

1
ansible 2.0.0.2
2

3
  config file =
4

5
  configured module search path = Default w/o overrides
6

Конечно, номер версии может быть другим.

Файл конфигурации Ansible

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

  • ANSIBLE_CONFIG (переменная окружения)
  • ansible.cfg (в текущем каталоге)
  • .ansible.cfg (в домашнем каталоге)
  • /etc/ansible/ansible.cfg

Вы также можете переопределить определенные параметры, используя отдельные переменные среды, которые имеют приоритет над файлом конфигурации.

Проверьте документацию Ansible, чтобы узнать обо всех вариантах.

Настройте Кластер Vagrant

Чтобы по-настоящему понять всю мощь Ansible, вам нужно несколько серверов для управления. В этом руководстве я буду использовать кластер Vagrant из 3 виртуальных машин, но для Ansible это всего лишь несколько хостов, которыми он должен управлять. Чтобы узнать больше о Vagrant, ознакомьтесь с разделом Введение в Vagrant.

Сначала установите VirtualBox и Vagrant. Затем поместите следующее в файл с именем Vagrantfile в рабочем каталоге.

1
# -*- mode: ruby -*-

2

3
# vi: set ft=ruby :

4

5
hosts = {
6

7
  "larry" => "192.168.88.10",
8

9
  "curly" => "192.168.88.11",
10

11
  "moe" => "192.168.88.12"
12

13
}
14

15
Vagrant.configure("2") do |config|
16

17
  config.vm.box = "precise64"
18

19
  config.vm.box_url = "http://files.vagrantup.com/precise64.box"
20

21
 hosts.each do |name, ip|
22

23
    config.vm.define name do |machine|
24

25
      machine.vm.network :private_network, ip: ip
26

27
      machine.vm.provider "virtualbox" do |v|
28

29
        v.name = name
30

31
      end
32

33
    end
34

35
  end
36

37
end

Затем наберите vagrant up. Vagrant создаст для вас три виртуальные машины, доступные как larry, curly и moe. Чтобы проверить, введите vagrant status. Вы должны увидеть:

1
Current machine states:
2

3

4

5
larry                     running (virtualbox)
6

7
curly                     running (virtualbox)
8

9
moe                       running (virtualbox)
10

11

12

13
This environment represents multiple VMs. The VMs are all listed
14

15
above with their current state. For more information about a specific
16

17
VM, run `vagrant status NAME`.

Чтобы убедиться, что вы можете использовать SSH на хостах кластера, введите: vagrant ss >> ~ / .ssh / config.

Теперь вы можете использовать SSH на любом из ваших виртуальных серверов, используя их имя хоста. Например: ssh curly. Это позволит Ansible подключаться к хостам вашего кластера через SSH без каких-либо проблем с именами пользователей, паролями или ключами.

Инвентарь

Теперь, когда у нас есть кластер, нам нужно рассказать об этом Ansible. Это делается с помощью инвентарного файла. Файл инвентаризации — это список имен хостов, организованных в группы с использованием формата файла INI. Поместите следующее в файл с именем ‘hosts’ в вашей рабочей директории.

1
[funny]
2

3
 larry
4

5
 
6

7
 [funnier]
8

9
 curly
10

11
 moe

Я поместил «larry» в группу «funny», а остальные хосты в группу «funnier». Эта организация позволит нам выполнять действия в отношении этих групп. Вы также можете выполнять действия на отдельных хостах и на всех хостах.

Модули

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

Специальные команды

Пришло время взяться за дело. Простейший способ использования Ansible — запуск специальных команд. Специальные команды используют модули. Формат специальной команды:

ansible -i -m [-a , ... ]

Например, чтобы увидеть, все ли хосты в вашем инвентаре работают, вы можете использовать модуль ping (без аргументов):

1
ansible all -i hosts -m ping
2

3
curly | SUCCESS => {
4

5
    "changed": false,
6

7
    "ping": "pong"
8

9
}
10

11
larry | SUCCESS => {
12

13
    "changed": false,
14

15
    "ping": "pong"
16

17
}
18

19
moe | SUCCESS => {
20

21
    "changed": false,
22

23
    "ping": "pong"
24

25
}

Ansible имеет много модулей для всех распространенных задач системного администрирования, таких как управление файлами, управление пользователями и пакетами, а также множество необычных задач. Но если вы не можете найти то, что вам нужно, или просто чувствуете себя более комфортно с простыми командами оболочки, вы можете использовать модуль оболочки напрямую, включая каналы. Следующая команда извлекает внутренние и внешние IP-адреса всех хостов:

1
ansible all -i hosts -m shell -a '/sbin/ifconfig | grep inet.*Bcast'"

2


3


4


5
larry | SUCCESS | rc=0 >>

6


7
          inet addr:10.0.2.15  Bcast:10.0.2.255  Mask:255.255.255.0

8


9
          inet addr:192.168.88.10  Bcast:192.168.88.255  Mask:255.255.255.0

10


11


12


13
curly | SUCCESS | rc=0 >>

14


15
          inet addr:10.0.2.15  Bcast:10.0.2.255  Mask:255.255.255.0

16


17
          inet addr:192.168.88.11  Bcast:192.168.88.255  Mask:255.255.255.0

18


19


20


21
moe | SUCCESS | rc=0 >>

22


23
          inet addr:10.0.2.15  Bcast:10.0.2.255  Mask:255.255.255.0

24


25
          inet addr:192.168.88.12  Bcast:192.168.88.255  Mask:255.255.255.0

Playbooks

Специальные команды хороши, когда вы хотите быстро что-то сделать на нескольких хостах, но настоящая сила Ansible в его Playbooks. Playbooks — это файлы YAML, в которых вы определяете наборы задач для достижения таких целей, как подготовка, настройка, развертывание и управление вашей инфраструктурой.

Пример Playbook

Давайте посмотрим на то, как выглядит типичный playbook, прежде чем мы перейдем к деталям.

1
---
2

3
- hosts: funnier
4

5
  tasks:
6

7
   - name: Install Nginx
8

9
     apt: pkg=nginx state=installed update_cache=true
10

11
     notify: Start Nginx
12

13
   - name: Install Python 3
14

15
     apt: pkg=python3-minimal state=installed
16

17
  handlers:
18

19
    - name: Start Nginx
20

21
      service: name=nginx state=started

В playbook есть раздел hosts, в котором вы указываете хосты из файла инвентаризации. В этом случае название группы «funnier». Затем есть раздел задач с двумя задачами, которые устанавливают Nginx и Python 3. Наконец, есть раздел обработчиков, где Nginx запускается после его установки.

Запуск Playbooks

Вы запускаете playbook с помощью команды ansible-playbook. Вам все еще нужно предоставить файл инвентаря и книгу воспроизведения, которую вы хотите запустить. Сохраните playbook в файл с именем «playbook.yml» в вашем рабочем каталоге. Давайте попробуем:

1
ansible-playbook -i hosts playbook.yml
2

3

4

5
PLAY ***************************************************************************
6

7

8

9
TASK [setup] *******************************************************************
10

11
ok: [moe]
12

13
ok: [curly]
14

15

16

17
TASK [Install Nginx] ***********************************************************
18

19
fatal: [moe]: FAILED! => {"changed": false, "failed": true, "msg": "Failed to lock apt for exclusive operation"}
20

21
fatal: [curly]: FAILED! => {"changed": false, "failed": true, "msg": "Failed to lock apt for exclusive operation"}
22

23

24

25
PLAY RECAP *********************************************************************
26

27
curly                      : ok=1    changed=0    unreachable=0    failed=1
28

29
moe                        : ok=1    changed=0    unreachable=0    failed=1
30

О нет. Что случилось? Ansible выдает приличное сообщение об ошибке здесь: «Не удалось заблокировать apt для исключительной операции». Многие playbooks требуют привилегий sudo. Этот playbook не является исключением. Чтобы запустить playbook с правами sudo, просто добавьте флаг --sudo:

1
ansible-playbook -i hosts playbook.yml --sudo
2

3

4

5
PLAY ***************************************************************************
6

7

8

9
TASK [setup] *******************************************************************
10

11
ok: [curly]
12

13
ok: [moe]
14

15

16

17
TASK [Install Nginx] ***********************************************************
18

19
changed: [moe]
20

21
changed: [curly]
22

23

24

25
TASK [Install Python 3] ********************************************************
26

27
changed: [moe]
28

29
changed: [curly]
30

31

32

33
RUNNING HANDLER [Start Nginx] **************************************************
34

35
changed: [moe]
36

37
changed: [curly]
38

39

40

41
PLAY RECAP *********************************************************************
42

43
curly                      : ok=4    changed=3    unreachable=0    failed=0
44

45
moe                        : ok=4    changed=3    unreachable=0    failed=0
46

Ansible является идемпотентом, что означает, что если что-то уже находится в желаемом состоянии, то Ansible оставит это в покое. В выводе ansible-playbook вы можете увидеть, какие задачи были успешными или неудачными, а какие были изменены.

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

1
ansible-playbook -i hosts playbook.yml --sudo
2

3

4

5
PLAY ***************************************************************************
6

7

8

9
TASK [setup] *******************************************************************
10

11
ok: [moe]
12

13
ok: [curly]
14

15

16

17
TASK [Install Nginx] ***********************************************************
18

19
ok: [curly]
20

21
ok: [moe]
22

23

24

25
TASK [Install Python 3] ********************************************************
26

27
ok: [curly]
28

29
ok: [moe]
30

31

32

33
PLAY RECAP *********************************************************************
34

35
curly                      : ok=3    changed=0    unreachable=0    failed=0
36

37
moe                        : ok=3    changed=0    unreachable=0    failed=0

Стратегии запуска

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

В Ansible 2.0 добавлена концепция стратегий запуска. В настоящее время существует две стратегии: «линейная» стратегия, которую я описал выше, которая является стратегией по умолчанию, и «свободная» стратегия, при которой хосты могут выполнять задачи в сборнике playbook по-прежнему в порядке, но не в стопах с другими хостами.

Это может быть полезно, если сотни хостов должны загрузить несколько файлов с некоторых FTP-серверов. Первый хост может завершить загрузку первого файла и перейти к следующему, в то время как другие хосты все еще заняты загрузкой первого файла. К тому моменту, когда другие хосты загрузят следующий файл, первый хост уже будет готов, и будет меньше конфликтов.

Свободная стратегия кажется превосходной в большинстве ситуаций. Вы просто добавляете strategy: free пару ключ-значение в playbook.

1
- hosts: all
2

3
  strategy: free
4

5
  tasks:
6

7
  ...

Блоки

Еще одна новая особенность Ansible 2.0 — блоки. Блоки позволяют группировать задачи вместе. Это очень полезно, если у вас есть задачи, которые нужно выполнять только при определенных условиях. Раньше вам приходилось делать это для каждой задачи отдельно.

1
---
2

3
- hosts: all
4

5
  tasks:
6

7
    - debug: msg='Task 1 here'
8

9
      when: ansible_distribution == 'Ubuntu'
10

11

12

13
    - debug: msg='Task 2 here'
14

15
      when: ansible_distribution == 'Ubuntu'
16

17

18

19
    - debug: msg='Task 3 here'
20

21
      when: ansible_distribution == 'Ubuntu'

С блоками вы можете сгруппировать все эти задачи отладки и поставить условие « when» на уровне блока.

1
- hosts: all
2

3
  tasks:
4

5
    - block:
6

7
      - debug: msg='Task 1 here'
8

9
      - debug: msg='Task 2 here'
10

11
      - debug: msg='Task 3 here'
12

13
      when: ansible_distribution == 'Ubuntu'
14

Хранилище

Ansible связывается с удаленными компьютерами по SSH, но в плейбуках могут содержаться такие секреты, как имя пользователя, пароли и ключи API. Поскольку вы обычно храните playbooks в системах контроля версий, таких как git, эта информация будет видна всем, кто имеет доступ для чтения.

Ansible помогает с программой ansible-vault, которая позволяет создавать, редактировать и повторно шифровать зашифрованные файлы. Эти файлы могут быть расшифрованы на лету при запуске playbook, указав пароль. Если вы добавите флаг --vault-ask-pass в ansible-playbook, он запросит пароль хранилища.

Кроме того, вы можете добавить --vault-password-file <файл-пароля>, и Ansible прочитает пароль из вашего файла. Если вы используете файл паролей, не храните его в системе контроля версий!

Теперь вы можете безопасно хранить зашифрованные файлы в системе контроля версий и не беспокоиться о том, что кто-то найдет ваши секреты. Вам нужно тщательно управлять своим паролем хранилища. Если вы потеряете его, вы не сможете расшифровать файлы в хранилище.

Заключение

Ansible — отличный инструмент. Это легкий. Он может использоваться в интерактивном режиме со специальными командами и очень хорошо масштабируется для больших систем. У него также есть большой импульс и большое сообщество. Если вы управляете или даже просто работаете с удаленными серверами, вам определенно нужен Ansible.

Оставайтесь с нами для второй части.

Ansible – это простой ИТ-движок с открытым исходным кодом, который автоматизирует развертывание приложений, внутрисервисную оркестровку, облачную подготовку и многие другие ИТ-инструменты.

Ansible прост в развертывании, поскольку он не использует агентов или пользовательскую инфраструктуру безопасности.

Ansible использует playbook для описания заданий автоматизации, а playbook использует очень простой язык, то есть YAML (это читаемый человеком язык сериализации данных и обычно используется для файлов конфигурации, но может использоваться во многих приложениях, где хранятся данные), что очень легко для людей, чтобы понять, читать и писать. Следовательно, преимущество заключается в том, что даже ребята из службы поддержки ИТ-инфраструктуры могут читать и понимать игровую книгу и отлаживать ее при необходимости (YAML – она ​​в удобочитаемой форме).

Ansible предназначен для многоуровневого развертывания. Ansible не управляет одной системой одновременно, он моделирует ИТ-инфраструктуру, описывая все ваши системы взаимосвязанными. Ansible полностью без агента, что означает, что Ansible работает, подключая ваши узлы через ssh (по умолчанию). Но если вам нужен другой способ подключения, например Kerberos, Ansible предоставит вам такую ​​возможность.

После подключения к вашим узлам Ansible запускает небольшие программы, называемые «Ansible Modules». Ansible запускает эти модули на ваших узлах и удаляет их по завершении. Ansible управляет вашим инвентарем в простых текстовых файлах (это файл hosts). Ansible использует файл hosts, в котором можно сгруппировать хосты и управлять действиями над определенной группой в книгах воспроизведения.

@DevOPSitsec – бесплатное обучение DevOps в нашем канале.

Образец файла Hosts

Это содержимое файла hosts –

#File name: hosts
#Description: Inventory file for your application. Defines machine type abc
node to deploy specific artifacts
# Defines machine type def node to upload
metadata.

[abc-node]

#server1 ansible_host = <target machine for DU deployment> ansible_user = <Ansible user> ansible_connection = ssh server1 ansible_host = <your host name> ansible_user = <your unix user> ansible_connection = ssh

[def-node]

#server2 ansible_host = <target machine for artifact upload> ansible_user = <Ansible user> ansible_connection = ssh server2 ansible_host = <host> ansible_user = <user> ansible_connection = ssh

Что такое управление конфигурацией

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

Такая информация обычно включает точные версии и обновления, которые были применены к установленным программным пакетам, а также местоположения и сетевые адреса аппаратных устройств. Например, если вы хотите установить новую версию сервера WebLogic / WebSphere на всех машинах, имеющихся на вашем предприятии, вам не представляется возможным вручную перейти и обновить каждую машину.

Вы можете установить WebLogic / WebSphere за один раз на все свои машины с помощью самых простых книг и инвентаризации Ansible. Все, что вам нужно сделать, это перечислить IP-адреса ваших узлов в инвентаре и написать книгу для установки WebLogic / WebSphere. Запустите playbook со своего компьютера управления, и он будет установлен на всех ваших узлах.

Как работает Ansible?

На рисунке ниже показана работа Ansible.

Ansible работает , подключаясь к вашим узлам и выдвигая к ним небольшие программы, называемые « Ansible modules». Затем Ansible выполняет эти модули (по умолчанию через SSH) и удаляет их после завершения. Ваша библиотека модулей может находиться на любом компьютере, и для нее не требуются серверы, демоны или базы данных.

Ansible Works

Узел управления на приведенном выше изображении является управляющим узлом (управляющим узлом), который управляет всем исполнением книги воспроизведения. Это узел, с которого вы запускаете установку. Файл инвентаризации содержит список хостов, на которых необходимо запустить модули Ansible, а узел управления устанавливает соединение SSH, выполняет небольшие модули на хост-машине и устанавливает продукт / программное обеспечение.

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

Ansible – Настройка среды

В этой главе мы узнаем о настройке среды Ansible.

Процесс установки

В основном, есть два типа машин, когда мы говорим о развертывании –

  • Управление машиной – Машина, с которой мы можем управлять другими машинами.
  • Дистанционная машина – машины, которые управляются / контролируются управляющей машиной.

Управление машиной – Машина, с которой мы можем управлять другими машинами.

Дистанционная машина – машины, которые управляются / контролируются управляющей машиной.

Может быть несколько удаленных машин, которые обрабатываются одной управляющей машиной. Итак, для управления удаленными машинами мы должны установить Ansible на управляющей машине.

Требования к контрольной машине

Ansible может быть запущен с любой машины с установленным Python 2 (версии 2.6 или 2.7) или Python 3 (версии 3.5 и выше).

Примечание. Windows не поддерживает управление машиной.

По умолчанию Ansible использует ssh для управления удаленным компьютером.

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

Ansible может быть установлен на управляющей машине, которая имеет вышеупомянутые требования по-разному. Вы можете установить последнюю версию через Apt, yum, pkg, pip, OpenCSW, pacman и т. Д.

Установка через Apt на Ubuntu Machine

Для установки Ansible вы должны настроить PPA на вашем компьютере. Для этого вам нужно запустить следующую строку кода –

$ sudo apt-get update 
$ sudo apt-get install software-properties-common 
$ sudo apt-add-repository ppa:ansible/ansible $ sudo apt-get update 
$ sudo apt-get install ansible

Запустив приведенную выше строку кода, вы готовы управлять удаленными машинами через Ansible. Просто запустите Ansible – version, чтобы проверить версию и просто проверить, правильно ли был установлен Ansible.

Ansible – Основы YAML

Ansible использует синтаксис YAML для выражения Ansible playbooks. В этой главе представлен обзор YAML. Ansible использует YAML, потому что людям очень легко понимать, читать и писать по сравнению с другими форматами данных, такими как XML и JSON.

Каждый файл YAML может начинаться с «—» и заканчиваться на «…».

Понимание YAML

В этом разделе мы изучим различные способы представления данных YAML.

пара ключ-значение

YAML использует простую пару ключ-значение для представления данных. Словарь представлен в паре ключ: значение.

Примечание. Между: и значением должен быть пробел.

Пример: запись студента

--- #Optional YAML start syntax 
james: 
   name: james john 
   rollNo: 34 
   div: B 
   sex: male 
… #Optional YAML end syntax 

Сокращение

Вы также можете использовать сокращение для представления словарей.

пример

James: {name: james john, rollNo: 34, div: B, sex: male}

Представляющий список

Мы также можем представить список в YAML. Каждый элемент (член) списка должен быть записан в новой строке с таким же отступом, начинающимся с «-» (- и пробел).

пример

---
countries:  
   - America 
   - China 
   - Canada 
   - Iceland 
…

Сокращение

Вы также можете использовать сокращение для представления списков.

пример

Countries: [‘America’, ‘China’, ‘Canada’, ‘Iceland’] 

Список внутри словарей

Мы можем использовать список внутри словарей, т. Е. Значением ключа является список.

пример

---  
james: 
   name: james john 
   rollNo: 34 
   div: B 
   sex: male 
   likes: 
      - maths 
      - physics 
      - english 
… 

Список словарей

Мы также можем составить список словарей.

пример

---  
- james: 
   name: james john 
   rollNo: 34 
      div: B 
   sex: male 
   likes: 
      - maths 
      - physics 
      - english 

- robert: 
      name: robert richardson 
      rollNo: 53 
      div: B 
      sex: male 
   likes: 
      - biology 
      - chemistry 
…  

YAML использует «|» для включения новых строк при отображении нескольких строк и «>» для подавления новых строк при отображении нескольких строк. Благодаря этому мы можем читать и редактировать большие строки. В обоих случаях намерения будут игнорироваться.

Мы также можем представлять логические (True / false) значения в YAML. где логические значения могут быть без учета регистра.

пример

---  
- james: 
   name: james john 
   rollNo: 34 
   div: B 
   sex: male 
   likes: 
      - maths 
      - physics 
      - english 
   
   result: 
      maths: 87 
      chemistry: 45 
      biology: 56 
      physics: 70 
      english: 80 
   
   passed: TRUE 
   
   messageIncludeNewLines: | 
      Congratulation!! 
      You passed with 79% 
   
   messageExcludeNewLines: > 
      Congratulation!! 
      You passed with 79% 

Некоторые общие слова, связанные с Ansible.

Сервис / Сервер – процесс на компьютере, который предоставляет сервис.

Машина – физический сервер, виртуальная машина или контейнер.

Целевая машина – машина, которую мы собираемся настроить с помощью Ansible.

Задача – действие (запустить, удалить это) и т. Д., Управляемое Ansible.

Playbook – файл yml, в котором записываются команды Ansible и на компьютере выполняется yml.

Сервис / Сервер – процесс на компьютере, который предоставляет сервис.

Машина – физический сервер, виртуальная машина или контейнер.

Целевая машина – машина, которую мы собираемся настроить с помощью Ansible.

Задача – действие (запустить, удалить это) и т. Д., Управляемое Ansible.

Playbook – файл yml, в котором записываются команды Ansible и на компьютере выполняется yml.

Инвентори: ansible_host vs FQDN

В этой части мы хорошо разбираемся с тем, что такое inventory_hostname, что такое ansible_host, с понятием транспорта.

При том, что транспорт уже не совсем “инвентори”, к содержимому инвентори он относится наипрямейшим образом, потому что смена транспорта внутри play — это уже экстремальный спорт, на который не распространяется ваша медстраховка.

Что такое “транспорт”? Это результат использования “connection plugin” Ансибла, через который модуль копируется в целевую систему (или, в ряде случаев, не копируется, но получает доступ к целевой системе). Какой-то транспорт используется всегда. Самый популярный транспорт ssh (используется по-умолчанию), но их на самом деле много. Каждый плагин может использовать набор переменных, выделенных для подключения: ansible_hostansible_useransible_port и т. д. А может и не использовать. Например, если транспорт lxc (который выполняет код через lxc-execute), то зачем ему порт?

Если же ansible_host не задан, то используется inventory_hostname. Это — имя хоста в инвентори.

Вот пример:

---
somegroup:
  hosts:
     somehost:
         ansible_host: 254.12.11.10

Вот somehost тут — это inventory_hostname. Если нет ansible_host, то используется inventory_hostname. И всё было бы понятно, если бы не следующий уровень преобразований, который не имеет никакого отношения к Ансибл, но может попортить много нервов.

Внутри как inventory_hostname, так и ansible_host может быть либо адрес, либо имя. С адресом всё понятно, а вот с именем уже интереснее. Оно передаётся “как есть” в нижележащий исполнитель. Интерпретация имени оставляется на усмотрение транспорта. Например, lxc использует его для выбора контейнера. А вот ssh (самый распространённый транспорт, напоминаю) использует кое-что более сложное.

Во-первых, он смотрит в конфиг ~/.ssh/ssh_config (или другой, заданный через переменные окружения). Если кто пропустил, напоминаю, что конфиг ssh тьюринг-полный и может делать странное через комбинацию регэкспов и сниппетов для исполнения баша. Т.е. переданное имя становится (в общем случае) аргументом к частично-рекурсивной функции, которая (может быть) выдаёт реальные параметры соеднения на выходе. Может быть, соединение пойдёт через цепочку jump-хостов, редиректов портов и прочего ssh-цирка. А может быть, такого хоста не найдётся. Если же из ssh_config выползает другое имя (или искомого нет в ssh_config), то ssh делает gethostbyname(). Это вызов libc, который получает адрес по имени. Который, в свою очередь, руководствуется пачкой конфигурационных файлов (/etc/nsswitch.conf/etc/hosts) и ответами DNS-ресолвера (если конфигурационные файлы это разрешают). Который, в свою очередь, может дописывать к имени домен, смотреть на разные рекурсивные DNS-сервера, которые могут отвечать разное, а могут посмотреть на ресурсную запись CNAME пойти куда сказано… Просто волшебная простыня возможностей того, что может пойти не так.

Из этого вытекает моё, выстраданное, мнение: при работе с SSH, всегда (кроме спецслучаев) использовать ansible_host внутри которого IP-адрес.

Я пробовал другой путь, и он мне местами аукается до сих пор. Давайте разберём этот вопрос подробно.

Если вы используете любое вне-ансибловое, но host-local определение имени (ssh_config/etc/hosts), то ваши плейбуки перестают быть портабельными между машинами. Вы ссылаетесь на что-то, что существует только у вас в голове и с вами разговаривает только в конфигурации вашего компьютера. Вы не можете перетащить эти плейбуки на CI, на машину коллеги или даже на вторую вашу машину. Точнее, можете, но для этого нужно что-то (что?) прописать в конфигурацию, которой не видно в репозитории. Опечатки трудно отлаживать (у меня всё работает), изменения почти невозможно распространять. НЕ ДЕЛАЙТЕ ТАК.

Хотя, разумеется, есть исключения. Например, моя маленькая уютная оверлейная сеточка для домашних нужд живёт с именами из /etc/hosts и все плейбуки полагаются на эти имена. Но это моё осознанное решение, которое к индустриальному продакшену никакого отношения иметь не должно.

Если вы используете DNS, то вы получаете себе регэксп ещё одну проблему. Когда изменения в DNS дойдут до вашей машины? Негативное/позитивное кеширование, всё такое. А даже если оно дошло до вас, то когда оно дойдёт до резолвера, которым пользуется ваш динамический слейв CI? Слейв-то помер, а DNS-ресолвер — нет. Удачи в отладке. НЕ ДЕЛАЙТЕ ТАК.

Второй момент, куда более тонкий. Надо ли всегда указывать ansible_host или inventory_hostname достаточно?
В плейбуках рано или поздно возникает потребность указать “адрес соседа”. В самых трудных случаях этот процесс требует модуля setup и выполнения головоломного кода:

    - name: Ping neighbor
      command: ping -c 1 {{ neighbor_ip }} -w 1
      changed_when: false
      vars:
        neighbor_ip: '{{ (hostvars[item].ansible_all_ipv4_addresses|ipaddr(public_network))[0] }}'
      with_items: '{{ groups[target_group] }}'

(имея на руках public_network мы проверяем, что хосты могут общаться со всеми серверами в группе target_group).

Но, это трудный случай, поскольку у серверов несколько интерфейсов. В 99% случаев вам нужен просто “адрес соседа”. Если вы договорились, что у каждого хоста есть ansible_host и внутри там обязательно IP-адрес, то вот он. Никакого setup. Бери и используй. Прелесть ansible_host с IP-адресом трудно переоценить, потому что, помимо “какого-то IP соседа”, этот адрес ещё неявно (явно!) отвечает вам на вопрос, какой из IP-адресов сервера является его “access address” при наложении всяких файрвольных правил, конфигурации доступов и т.д. Делайте так. Это хорошо и удобно.

… Но тут может возникнуть вопрос: а если у нас сервера появляются на свет динамически, или у нас внешная система оркестрации (а-ля докер) у которой точно есть хороший DNS? Ну, тогда используйте их. А, заодно, страдайте, если вам понадобились IP. Разумеется, к любой общей рекомендации всегда можно найти частные исключения.

Инвентори: ansible_user

Следующая интереснейшая проблема: надо ли в инвентори хранить имя пользователя? Это важный вопрос, но у него нет однозначного ответа. Вот набор моментов, о которых надо подумать перед выбором.

  1. Есть ли доступ к этому хосту из-под “спецаккаунта” у других пользователей? Если есть, то ansible_user в инвентори разумно.
  2. Есть ли доступ к серверу под “своими” аккаунтами у других пользователей? Если есть, то ansible_user в инвентори создаёт проблемы.
  3. Если вы не указываете пользователя в инвентори, то опция -u у ansible-playbook позволяет пользователя задать, причём так, что его можно переопределить из любого места в инвентори или плейбуке для необычных видов коннектов. Это удобно. Каждый под своим пользователем, CI использует -u (или тоже под своим пользователем), все счастливы.
  4. Но тогда абстракция протекает. Например, ваш сосед может быть залогинен на своём ноутбуке под именем ‘me’. Это ж его ноутбук. А на сервере он — m.gavriilicheynko. Неудобненько.
  5. В то же самое время, использование опции ansible-playbook -e ansible_user=ci (для CI, например) с одной стороны позволяет использовать правильное имя вне зависимости от содержимого инвентори, с другой стороны ломает все нестандартные подключения (к коммутаторам, например).
  6. Если у вас стоит проблема “первого логина” (плейбука создаёт всех пользователей, но только после первого запуска), то первый запуск можно сделать и с опцией -u, и никто не помрёт.

В моей практике (и обстоятельствах, в которых я работаю), мне удобно указывать ansible_user для “себя” (т.е. инвентори, к которыми работаю только я). Если инвентори используется более одним человеком — ansible_user используется только для специальных случаев (например, доступ к коммутаторам при первом провизе и т.д.), а обычные хосты ansible_user не используют.

Ansible – специальные команды

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

Например, вам нужно перезагрузить все серверы вашей компании. Для этого вы запустите команды Adhoc из / usr / bin / ansible .

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

ansible-playbook используется для управления конфигурацией и развертывания.

Параллелизм и Команды Shell

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

$ ssh-agent bash 
$ ssh-add ~/.ssh/id_rsa 

Чтобы выполнить перезагрузку для всех серверов вашей компании в группе «abc», в 12 параллельных ветвях –

$ Ansible abc -a "/sbin/reboot" -f 12

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

$ Ansible abc -a "/sbin/reboot" -f 12 -u username

Передача файла

Вы можете использовать специальные команды для одновременного выполнения большого количества файлов SCP (Secure Copy Protocol) на нескольких компьютерах.

Передача файла на многие серверы / машины

$ Ansible abc -m copy -a "src = /etc/yum.conf dest = /tmp/yum.conf"

Создание нового каталога

$ Ansible abc -m file -a "dest = /path/user1/new mode = 777 owner = user1 group = user1 state = directory" 

Удаление всего каталога и файлов

$ Ansible abc -m file -a "dest = /path/user1/new state = absent"

Управление пакетами

Специальные команды доступны для yum и apt. Ниже приведены некоторые специальные команды, использующие yum.

Следующая команда проверяет, установлен пакет yum или нет, но не обновляет его.

$ Ansible abc -m yum -a "name = demo-tomcat-1 state = present"

Следующая команда проверяет, что пакет не установлен.

$ Ansible abc -m yum -a "name = demo-tomcat-1 state = absent" 

Следующая команда проверяет, установлена ​​ли последняя версия пакета.

$ Ansible abc -m yum -a "name = demo-tomcat-1 state = latest" 

Сбор фактов

Факты могут быть использованы для реализации условных операторов в playbook. Вы можете найти специальную информацию обо всех ваших фактах с помощью следующей специальной команды –

$ Ansible all -m setup 

Ansible – Playbooks

В этой главе мы узнаем о Playbooks в Ansible.

Названия

Главная ошибка пользователя Ansible — это не знать как что называется. Если вы не знаете названий, вы не можете понимать то, что написано в документации. Живой пример: на собеседовании, человек, вроде бы заявлявший, что он много писал на Ансибле, не смог ответить на вопрос “из каких элементов состоит playbook’а?”. А когда я подсказал, что “ожидался ответ, что playbook состоит из play”, то последовал убийственный комментарий “мы этого не используем”. Люди пишут на Ансибле за деньги и не используют play. На самом деле используют, но не знают, что это такое.

Так что начнём с простого: как что называется. Может быть вы это знаете, а может и нет, потому что не обратили внимания, когда читали документацию.

ansible-playbook исполняет playbook. Playbook — это файл с расширением yml/yaml, внутри которого что-то такое:

---
- hosts: group1
  roles:
    - role1

- hosts: group2,group3
  tasks:
    - debug:

Мы уже поняли, что весь этот файл — плейбука. Мы можем показать где тут роли (roles), где таски (tasks). Но где тут play? И чем отличается play от role или playbook?

В документации это всё есть. И это пропускают. Начинающие — потому что там слишком много и всё сразу не запомнишь. Опытные — потому что “тривиальные вещи”. Если вы опытный — перечитывайте эти страницы хотя бы раз в полгода, и ваш код станет классом лучше.

Итак, запоминайте: Playbook — это список, состоящий из play и import_playbook.
Вот это — одна play:

- hosts: group1
  roles:
    - role1

и вот это тоже ещё одна play:

- hosts: group2,group3
  tasks:
    - debug:

Что же такое play? Зачем она?

Play — это ключевой элемент для playbook, потому что play и только play связывает список ролей и/или тасок с списком хостов, на которых их надо выполнять. В глубоких недрах документации можно найти упоминание про delegate_to, локальные lookup-плагины, network-cli-специфичные настройки, jump-хосты и т.д. Они позволяют слегка поменять место исполнения тасок. Но, забудьте про это. У каждой из этих хитрых опций есть очень специальные применения, и они точно не являются универсальными. А мы говорим про базовые вещи, которые должны знать и использовать все.

Если вы хотите “что-то” исполнить “где-то” — вы пишете play. Не роль. Не роль с модулями и делегейтами. Вы берёте и пишете play. В которой, в поле hosts вы перечисляете где исполнять, а в roles/tasks — что исполнять.

Просто же, да? А как может быть иначе?

Одним из характерных моментов, когда у людей возникает желание сделать это не через play, это “роль, которая всё настраивает”. Хочется иметь роль, которая настраивает и сервера первого типа, и сервера второго типа.

Архетипичным примером является мониторинг. Хочется иметь роль monitoring, которая настроит мониторинг. Роль monitoring назначается на хосты мониторинга (в соотв. play). Но, выясняется, что для мониторинга нам надо поставить пакеты на хосты, которые мы мониторим. Почему бы не использовать delegate? А ещё надо настроить iptables. delegate? А ещё надо написать/поправить конфиг для СУБД, чтобы мониторинг пускала. delegate! А если креатив попёр, то можно сделать делегацию include_role во вложенном цикле по хитрому фильтру на список групп, а внутри include_role можно ещё делать delegate_to снова. И понеслось…

Благое пожелание — иметь одну-единственную роль monitoring, которая “всё делает” — ведёт нас в кромешный ад из которого чаще всего один выход: всё переписать с нуля.

Где тут случилась ошибка? В тот момент, когда вы обнаружили, что для выполнения задачи “x” на хосте X вам надо пойти на хост Y и сделать там “y”, вы должны были выполнить простое упражнение: пойти и написать play, которая на хосте Y делает y. Не дописывать что-то в “x”, а написать с нуля. Пусть даже с захардкоженными переменными.

Вроде бы, в абзацах выше всё сказано правильно. Но это же не ваш случай! Потому что вы хотите написать переиспользуемый код, который DRY и похож на библиотеку, и нужно искать метод как это сделать.

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

Эта ошибка звучит так: роль — это библиотечная функция. Эта аналогия сгубила столько хороших начинаний, что просто грустно смотреть. Роль — не библиотечная функция. Она не может делать вычисления и она не может принимать решения уровня play. Напомните мне, какие решения принимает play?

Спасибо, вы правы. Play принимает решение (точнее, содержит в себе информацию) о том, какие таски и роли на каких хостах выполнять.

Если вы делегируете это решение на роль, да ещё и с вычислениями, вы обрекаете себя (и того, кто ваш код будет пытаться разобрать) на жалкое существование. Роль не решает где ей выполняться. Это решение принимает play. Роль делает то, что ей сказали, там, где ей сказали.

Почему заниматься программированием на Ансибле опасно и чем COBOL лучше Ансибла мы поговорим в главе про переменные и jinja. Пока что скажем одно — каждое ваше вычисление оставляет за собой нестираемый след из изменения глобальных переменных, и вы ничего с этим не можете сделать. Как только два “следа” пересеклись — всё пропало.

Замечание для въедливых: роль, безусловно, может влиять на control flow. Есть delegate_to и у него есть разумные применения. Есть meta: end host/play. Но! Помните, мы учим основы? Забыли про delegate_to. Мы говорим про самый простой и самый красивый код на Ансибл. Который легко читать, легко писать, легко отлаживать, легко тестировать и легко дописывать. Так что, ещё раз:

play и только play решает на каких хостах что исполняется.

В этом разделе мы разобрались с противостоянием play и role. Теперь поговорим про отношения tasks vs role.

Playbooks – это файлы, в которых написан код Ansible. Сборники написаны в формате YAML. YAML означает еще один язык разметки. Playbooks являются одной из основных функций Ansible и говорят Ansible, что выполнять. Они похожи на список дел для Ansible, который содержит список задач.

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

Структура Playbook

Каждая пьеса представляет собой совокупность одной или нескольких пьес в ней. Playbooks структурированы с использованием Plays. В сборнике пьес может быть несколько пьес.

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

YAML – строго типизированный язык; поэтому при записи файлов YAML необходимо соблюдать особую осторожность. Существуют разные редакторы YAML, но мы предпочитаем использовать простой редактор, такой как notepad ++. Просто откройте notepad ++, скопируйте и вставьте нижеследующий yaml и измените язык на YAML (Язык → YAML).

YAML начинается с — (3 дефиса)

Создать Playbook

Давайте начнем с написания примера файла YAML. Мы пройдемся по каждому разделу, записанному в файле yaml.

--- 
   name: install and configure DB
   hosts: testServer
   become: yes

   vars: 
      oracle_db_port_value : 1521
   
   tasks:
   -name: Install the Oracle DB
      yum: <code to install the DB>
    
   -name: Ensure the installed service is enabled and running
   service:
      name: <your service name>

Выше приведен пример Playbook, где мы пытаемся охватить основной синтаксис playbook. Сохраните вышеуказанный контент в файл как test.yml . Синтаксис YAML должен соответствовать правильному отступу, и нужно быть немного осторожным при написании синтаксиса.

Различные теги YAML

Давайте теперь рассмотрим различные теги YAML. Различные теги описаны ниже –

название

Этот тег определяет название книги воспроизведения Ansible. Как в том, что будет делать этот playbook. Любое логическое имя может быть дано пьесе.

хостов

Этот тег определяет списки хостов или группы хостов, для которых мы хотим запустить задачу. Поле / тег hosts является обязательным. Он сообщает Ansible, на каких хостах запускать перечисленные задачи. Задачи могут быть запущены на той же машине или на удаленной машине. Можно запускать задачи на нескольких машинах, и, следовательно, тег hosts может содержать также запись группы узлов.

вары

Тег Vars позволяет вам определять переменные, которые вы можете использовать в своей книге игр. Использование аналогично переменным в любом языке программирования.

задачи

Все пьесы должны содержать задачи или список задач для выполнения. Задачи – это список действий, которые нужно выполнить. Поле задач содержит название задачи. Это работает как текст справки для пользователя. Это не обязательно, но оказывается полезным при отладке playbook. Каждая задача внутренне связана с частью кода, называемой модулем. Модуль, который должен быть выполнен, и аргументы, необходимые для модуля, который вы хотите выполнить.

Ansible – Роли

Роли обеспечивают основу для полностью независимых или взаимозависимых наборов переменных, задач, файлов, шаблонов и модулей.

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

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

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

Playbook верхнего уровня – это мост, удерживающий хосты из файла инвентаризации для ролей, которые должны применяться к этим хостам.

Создание новой роли

Структура каталогов для ролей необходима для создания новой роли.

Ролевая структура

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

Каждая роль представляет собой дерево каталогов само по себе. Имя роли – это имя каталога в каталоге / role.

$ ansible-galaxy -h 

использование

ansible-galaxy [delete|import|info|init|install|list|login|remove|search|setup] [--help] [options] ... 

Опции

  • -h, –help – показать это справочное сообщение и выйти.
  • -v, –verbose – подробный режим (-vvv для большего, -vvvv для включения отладки соединения)
  • –version – Показать номер версии программы и выйти.

-h, –help – показать это справочное сообщение и выйти.

-v, –verbose – подробный режим (-vvv для большего, -vvvv для включения отладки соединения)

–version – Показать номер версии программы и выйти.

Создание каталога ролей

Приведенная выше команда создала каталоги ролей.

$ ansible-galaxy init vivekrole 
ERROR! The API server (https://galaxy.ansible.com/api/) is not responding, please try again later. 

$ ansible-galaxy init --force --offline vivekrole 
- vivekrole was created successfully 

$ tree vivekrole/ 
vivekrole/ 
├── defaults 
│   └── main.yml 
├── files ├── handlers 
│   └── main.yml 
├── meta 
│   └── main.yml 
├── README.md ├── tasks 
│   └── main.yml 
├── templates ├── tests │   ├── inventory 
│   └── test.yml 
└── vars 
    └── main.yml 
 
8 directories, 8 files

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

Использование ролей в Playbook

Это код пьесы, которую мы написали для демонстрации. Этот код из сборника пьес vivek_orchestrate.yml. Мы определили хосты: tomcat-node и назвали две роли – install-tomcat и start-tomcat .

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

--- 
- hosts: tomcat-node 
roles: 
   - {role: install-tomcat} 
   - {role: start-tomcat} 

Содержимое нашей структуры каталогов, откуда мы запускаем playbook.

каталог

$ ls 
ansible.cfg  hosts  roles  vivek_orchestrate.retry vivek_orchestrate.yml 

Роли

В каждом каталоге есть каталог задач, в котором содержится файл main.yml. Основным содержимым install-tomcat является:

--- 
#Install vivek artifacts 
-  
   block: 
      - name: Install Tomcat artifacts
         action: > 
            yum name = "demo-tomcat-1" state = present 
         register: Output 
          
   always: 
      - debug: 
         msg: 
            - "Install Tomcat artifacts task ended with message: {{Output}}" 
            - "Installed Tomcat artifacts - {{Output.changed}}" 

Содержимое main.yml начального tomcat –

#Start Tomcat          
-  
   block: 
      - name: Start Tomcat 
      command: <path of tomcat>/bin/startup.sh" 
      register: output 
      become: true 
   
   always: 
      - debug: 
         msg: 
            - "Start Tomcat task ended with message: {{output}}" 
            - "Tomcat started - {{output.changed}}" 

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

Разбивая Playbook на роль

Если не для ролей, содержимое main.yml соответствующей роли может быть скопировано в yml- файл playbook. Но чтобы иметь модульность, роли были созданы.

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

Запустил команду для запуска playbook.

-vvv option for verbose output – verbose output 
$ cd vivek-playbook/

Это команда для запуска playbook

$ sudo ansible-playbook -i hosts vivek_orchestrate.yml –vvv 
-----------------------------------------------------------------
----------------------------------------------------------------------- 

Выход

Сгенерированный вывод показан на экране –

Использование /users/demo/vivek-playbook/ansible.cfg в качестве файла конфигурации.

PLAYBOOK: vivek_orchestrate.yml *********************************************************
*********************************************************** 
1 plays in vivek_orchestrate.yml 

PLAY [tomcat-node] **********************************************************************
******** ************************************************* 
 
TASK [Gathering Facts] *************************************************
****************************** ********************************************* 
Tuesday 21 November 2017  13:02:05 +0530 (0:00:00.056) 0:00:00.056 ****** 
Using module file /usr/lib/python2.7/sitepackages/ansible/modules/system/setup.py 
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: root 
<localhost> EXEC /bin/sh -c 'echo ~ && sleep 0' 
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo 
   /root/.ansible/tmp/ansible-tmp-1511249525.88-259535494116870 `" && 
   echo ansible-tmp-1511249525.88-259535494116870="` 
   echo /root/.ansible/tmp/ansibletmp-1511249525.88-259535494116870 `" ) && sleep 0' 
<localhost> PUT /tmp/tmpPEPrkd TO 
   /root/.ansible/tmp/ansible-tmp-1511249525.88259535494116870/setup.py 
<localhost> EXEC /bin/sh -c 'chmod u+x 
   /root/.ansible/tmp/ansible-tmp1511249525.88-259535494116870/ 
   /root/.ansible/tmp/ansible-tmp-1511249525.88259535494116870/setup.py && sleep 0' 
<localhost> EXEC /bin/sh -c '/usr/bin/python 
   /root/.ansible/tmp/ansible-tmp1511249525.88-259535494116870/setup.py; rm -rf 
   "/root/.ansible/tmp/ansible-tmp1511249525.88-259535494116870/" > /dev/null 2>&1 && sleep 0' 
ok: [server1] 
META: ran handlers 
 
TASK [install-tomcat : Install Tomcat artifacts] ***********************************
*************************************************************** 
task path: /users/demo/vivek-playbook/roles/install-tomcat/tasks/main.yml:5 
Tuesday 21 November 2017  13:02:07 +0530 (0:00:01.515)       0:00:01.572 ****** 
Using module file /usr/lib/python2.7/sitepackages/ansible/modules/packaging/os/yum.py 
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: root 
<localhost> EXEC /bin/sh -c 'echo ~ && sleep 0' 
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo 
   /root/.ansible/tmp/ansible-tmp-1511249527.34-40247177825302 `" && echo 
   ansibletmp-1511249527.34-40247177825302="` echo 
   /root/.ansible/tmp/ansible-tmp1511249527.34-40247177825302 `" ) && sleep 0' 
<localhost> PUT /tmp/tmpu83chg TO 
   /root/.ansible/tmp/ansible-tmp-1511249527.3440247177825302/yum.py 
<localhost> EXEC /bin/sh -c 'chmod u+x 
   /root/.ansible/tmp/ansible-tmp1511249527.34-40247177825302/ 
   /root/.ansible/tmp/ansible-tmp-1511249527.3440247177825302/yum.py && sleep 0' 
<localhost> EXEC /bin/sh -c '/usr/bin/python 
   /root/.ansible/tmp/ansible-tmp1511249527.34-40247177825302/yum.py; rm -rf 
   "/root/.ansible/tmp/ansible-tmp1511249527.34-40247177825302/" > /dev/null 2>
   &1 && sleep 0' 
changed: [server1] => { 
   "changed": true, 
   "invocation": { 
      "module_args": { 
         "conf_file": null, 
         "disable_gpg_check": false, 
         "disablerepo": null, 
         "enablerepo": null, 
         "exclude": null, 
         "install_repoquery": true, 
         "installroot": "/", 
         "list": null, 
         "name": ["demo-tomcat-1"], 
         "skip_broken": false, 
         "state": "present", 
         "update_cache": false, 
         "validate_certs": true 
      } 
   }, 
   "msg": "", 
   "rc": 0, 
   "results": [ 
      "Loaded plugins: product-id, 
      search-disabled-repos, 
      subscriptionmanager\nThis system is not registered to Red Hat Subscription Management. 
      You can use subscription-manager to register.\nResolving Dependencies\n--> 
      Running transaction check\n---> 
      Package demo-tomcat-1.noarch 0:SNAPSHOT-1 will be installed\n--> Finished Dependency 
      Resolution\n\nDependencies Resolved\n
      \n================================================================================\n 
      Package Arch Version Repository         
      Size\n==================================================================\nInstalling:\n 
      demo-tomcat-1 noarch SNAPSHOT-1 demo-repo1 7.1 M\n\nTransaction 
      Summary\n==================================================================\nInstall  1 
      Package\n\nTotal download size: 7.1 M\nInstalled size: 7.9 M\nDownloading 
         packages:\nRunning transaction 
      check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n  Installing : 
      demotomcat-1-SNAPSHOT-1.noarch 1/1 \n  Verifying  : 
      demo-tomcat-1-SNAPSHOT-1.noarch 1/1 \n\nInstalled:\n  
      demo-tomcat-1.noarch 0:SNAPSHOT-1 \n\nComplete!\n" 
   ] 
} 
 
TASK [install-tomcat : debug] **********************************************************
*************************************************************************** 
task path: /users/demo/vivek-playbook/roles/install-tomcat/tasks/main.yml:11 
Tuesday 21 November 2017  13:02:13 +0530 (0:00:06.757) 0:00:08.329 ****** 
ok: [server1] => { 
   "changed": false, 
   "msg": [ 
      "Install Tomcat artifacts task ended with message: {
         u'msg': u'', u'changed': True, u'results': 
         [u'Loaded plugins: product-id, 
         search-disabledrepos, 
         subscription-manager\\nThis system is not registered to Red Hat Subscription Management. 
         You can use subscription-manager to register.\\nResolving Dependencies\\n--> 
         Running transaction check\\n---> 
         Package demo-tomcat-1.noarch 0:SNAPSHOT-1 will be installed\\n--> 
         Finished Dependency Resolution\\n
         \\nDependencies 
         Resolved\\n\\n==================================================================\\n 
         Package Arch Version Repository         
         Size\\n======================================================================== 
         =====\\nInstalling:\\n demo-tomcat-1 noarch SNAPSHOT-1 demo-repo1 7.1 M\\n\\nTransaction 
         Summary\\n=========================================================\\nInstall  1 
         Package\\n\\nTotal download size: 7.1 M\\nInstalled size: 7.9 M\\nDownloading 
            packages:\\nRunning 
         transaction check\\nRunning transaction test\\nTransaction test succeeded\\nRunning 
            transaction\\n  
         Installing : demo-tomcat-1-SNAPSHOT-1.noarch 1/1 \\n  Verifying  : 
         demo-tomcat-1-SNAPSHOT-1.noarch
         1/1 \\n\\nInstalled:\\n  demo-tomcat-1.noarch 0:SNAPSHOT-1  \\n\\nComplete!\\n'], u'rc': 0
      }", 
      "Installed Tomcat artifacts - True" 
   ] 
} 
 
TASK [install-tomcat : Clean DEMO environment] ****************************************
************************************************************ 
task path: /users/demo/vivek-playbook/roles/install-tomcat/tasks/main.yml:19 
Tuesday 21 November 2017  13:02:13 +0530 (0:00:00.057) 0:00:08.387 ****** 
[WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or 
   {% %}. Found: {{installationOutput.changed}} 
 
Using module file /usr/lib/python2.7/sitepackages/ansible/modules/files/file.py 
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: root 
<localhost> EXEC /bin/sh -c 'echo ~ && sleep 0' 
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo 
   /root/.ansible/tmp/ansible-tmp-1511249534.13-128345805983963 `" && echo 
   ansible-tmp-1511249534.13-128345805983963="` echo 
   /root/.ansible/tmp/ansibletmp-1511249534.13-128345805983963 `" ) && sleep 0' 
<localhost> PUT /tmp/tmp0aXel7 TO 
   /root/.ansible/tmp/ansible-tmp-1511249534.13128345805983963/file.py 
<localhost> EXEC /bin/sh -c 'chmod u+x 
   /root/.ansible/tmp/ansible-tmp1511249534.13-128345805983963/ 
   /root/.ansible/tmp/ansible-tmp-1511249534.13128345805983963/file.py && sleep 0' 
<localhost> EXEC /bin/sh -c '/usr/bin/python 
   /root/.ansible/tmp/ansible-tmp1511249534.13-128345805983963/file.py; rm -rf 
   "/root/.ansible/tmp/ansible-tmp1511249534.13-128345805983963/" > /dev/null 2>&1 
   && sleep 0' 
changed: [server1] => { 
   "changed": true, 
      "diff": { 
         "after": { 
            "path": "/users/demo/DEMO", 
            "state": "absent" 
      }, 
      "before": { 
         "path": "/users/demo/DEMO", 
         "state": "directory" 
      } 
   },

   "invocation": { 
      "module_args": { 
         "attributes": null, 
         "backup": null, 
         "content": null, 
         "delimiter": null, 
         "diff_peek": null, 
         "directory_mode": null, 
         "follow": false, 
         "force": false, 
         "group": null, 
         "mode": null, 
         "original_basename": null, 
         "owner": null, 
         "path": "/users/demo/DEMO", 
         "recurse": false, 
         "regexp": null, 
         "remote_src": null, 
         "selevel": null, 
         "serole": null, 
         "setype": null, 
         "seuser": null, 
         "src": null, 
         "state": "absent", 
         "unsafe_writes": null, 
         "validate": null 
      } 
   }, 
   "path": "/users/demo/DEMO", 
   "state": "absent" 
} 
 
TASK [install-tomcat : debug] ********************************************************
************************************************************* 
task path: /users/demo/vivek-playbook/roles/install-tomcat/tasks/main.yml:29 
Tuesday 21 November 2017  13:02:14 +0530 (0:00:00.257)       0:00:08.645 ****** 
ok: [server1] => {
   "changed": false, 
   "msg": [ 
      "Clean DEMO environment task ended with message:{u'diff': {u'after': {u'path': 
         u'/users/demo/DEMO', u'state': u'absent'}, 
      u'before': {u'path': u'/users/demo/DEMO', u'state': u'directory'}}, u'state': u'absent', 
         u'changed': True, u'path': u'/users/demo/DEMO'}", 
      "check value  :True" 
   ] 
} 
 
TASK [install-tomcat : Copy Tomcat to user home] *************************************
******************************************************** 
task path: /users/demo/vivek-playbook/roles/install-tomcat/tasks/main.yml:37 
Tuesday 21 November 2017  13:02:14 +0530 (0:00:00.055)       0:00:08.701 ****** 
[WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or 
   {% %}. Found: {{installationOutput.changed}} 
 
Using module file /usr/lib/python2.7/sitepackages/ansible/modules/commands/command.py 
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: root 
<localhost> EXEC /bin/sh -c 'echo ~ && sleep 0' 
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo 
   /root/.ansible/tmp/ansible-tmp-1511249534.43-41077200718443 `" && echo 
   ansibletmp-1511249534.43-41077200718443="` echo 
   /root/.ansible/tmp/ansible-tmp1511249534.43-41077200718443 `" ) && sleep 0' 
<localhost> PUT /tmp/tmp25deWs TO 
   /root/.ansible/tmp/ansible-tmp-1511249534.4341077200718443/command.py 
<localhost> EXEC /bin/sh -c 'chmod u+x 
   /root/.ansible/tmp/ansible-tmp1511249534.43-41077200718443/ 
   /root/.ansible/tmp/ansible-tmp-1511249534.4341077200718443/command.py && sleep 0' 
<localhost> EXEC /bin/sh -c '/usr/bin/python 
   /root/.ansible/tmp/ansible-tmp1511249534.43-41077200718443/command.py; rm -rf 
   "/root/.ansible/tmp/ansibletmp-1511249534.43-41077200718443/" > /dev/null 2>&1 
   && sleep 0' 
changed: [server1] => { 
   "changed": true, 
   "cmd": [ 
      "cp", 
      "-r", 
      "/opt/ansible/tomcat/demo", 
      "/users/demo/DEMO/" 
   ],
   "delta": "0:00:00.017923", 
   "end": "2017-11-21 13:02:14.547633", 
   "invocation": { 
      "module_args": { 
         "_raw_params": "cp -r /opt/ansible/tomcat/demo /users/demo/DEMO/", 
         "_uses_shell": false, 
         "chdir": null, 
         "creates": null, 
         "executable": null, 
         "removes": null, 
         "warn": true 
      } 
   }, 
   "rc": 0, 
   "start": "2017-11-21 13:02:14.529710", 
   "stderr": "", 
   "stderr_lines": [], 
   "stdout": "", 
   "stdout_lines": [] 
} 
 
TASK [install-tomcat : debug] ********************************************************
********************************************************** 
task path: /users/demo/vivek-playbook/roles/install-tomcat/tasks/main.yml:47 
Tuesday 21 November 2017  13:02:14 +0530 (0:00:00.260)       0:00:08.961 ****** 
ok: [server1] => { 
   "changed": false, 
   "msg": "Copy Tomcat to user home task ended with message {
      'stderr_lines': [], u'changed': True, u'end': u'2017-11-21 13:02:14.547633', u'stdout': 
      u'', u'cmd': [u'cp', u'-r', u'/opt/ansible/tomcat/demo', u'/users/demo/DEMO/'], u'rc': 0, 
      u'start': u'2017-11-21 13:02:14.529710', u'stderr': u'', u'delta': u'0:00:00.017923', 
      'stdout_lines': []}" 
} 
 
TASK [start-tomcat : Start Tomcat] **************************************************
********************************************************** 
task path: /users/demo/vivek-playbook/roles/start-tomcat/tasks/main.yml:5 
Tuesday 21 November 2017  13:02:14 +0530 (0:00:00.044)       0:00:09.006 ****** 
Using module file /usr/lib/python2.7/sitepackages/ansible/modules/commands/command.py 
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: root 
<localhost> EXEC /bin/sh -c 'echo ~ && sleep 0' 
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo 
   /root/.ansible/tmp/ansible-tmp-1511249534.63-46501211251197 `" && echo 
   ansibletmp-1511249534.63-46501211251197="` echo 
   /root/.ansible/tmp/ansible-tmp1511249534.63-46501211251197 `" ) && sleep 0' 
<localhost> PUT /tmp/tmp9f06MQ TO 
   /root/.ansible/tmp/ansible-tmp-1511249534.6346501211251197/command.py 
<localhost> EXEC /bin/sh -c 'chmod u+x 
   /root/.ansible/tmp/ansible-tmp1511249534.63-46501211251197/ 
   /root/.ansible/tmp/ansible-tmp-1511249534.6346501211251197/command.py && sleep 0' 
<localhost> EXEC /bin/sh -c '/usr/bin/python 
   /root/.ansible/tmp/ansible-tmp1511249534.63-46501211251197/command.py; rm -rf 
   "/root/.ansible/tmp/ansibletmp-1511249534.63-46501211251197/" > /dev/null 2>&1 
   && sleep 0' 
changed: [server1] => { 
   "changed": true, 
   "cmd": [ "/users/demo/DEMO/bin/startup.sh" ], 
   "delta": "0:00:00.020024", 
   "end": "2017-11-21 13:02:14.741649", 
   "invocation": { 
      "module_args": { 
         "_raw_params": "/users/demo/DEMO/bin/startup.sh", 
         "_uses_shell": false, 
         "chdir": null, 
         "creates": null, 
         "executable": null, 
         "removes": null, 
         "warn": true 
      } 
   }, 
   "rc": 0, 
   "start": "2017-11-21 13:02:14.721625", 
   "stderr": "", 
   "stderr_lines": [], 
   "stdout": "Tomcat started.", 
   "stdout_lines": [ "Tomcat started." ] 
} 
 
TASK [start-tomcat : debug] *************************************************
********************************************************************** 
task path: /users/demo/vivek-playbook/roles/start-tomcat/tasks/main.yml:10 
Tuesday 21 November 2017  13:02:14 +0530 (0:00:00.150)       0:00:09.156 ****** 
ok: [server1] => { 
   "changed": false, 
   "msg": [ 
      "Start Tomcat task ended with message: {'
         stderr_lines': [], u'changed': True, u'end': u'2017-11-21 13:02:14.741649', u'stdout': 
         u'Tomcat started.', u'cmd': [u'/users/demo/DEMO/bin/startup.sh'], u'rc': 0, u'start': 
         u'2017-11-21 13:02:14.721625', u'stderr': u'', u'delta': u'0:00:00.020024', 
         'stdout_lines': [u'Tomcat started.']}", 
      "Tomcat started - True" 
   ] 
} 
META: ran handlers 
META: ran handlers 
 
PLAY RECAP ******************************************************************************* 
********************************************************* 
server1  : ok = 9    changed = 4    unreachable = 0    failed = 0 
 
Tuesday 21 November 2017  13:02:14 +0530 (0:00:00.042)       0:00:09.198 ****** 
=============================================================================== 
install-tomcat : Install Tomcat artifacts ------------------------------- 6.76s 
/users/demo/vivek-playbook/roles/install-tomcat/tasks/main.yml:5 -------------- 
Gathering Facts --------------------------------------------------------- 1.52s 
 ------------------------------------------------------------------------------ 
install-tomcat : Copy Tomcat to user home ------------------------------- 0.26s 
/users/demo/vivek-playbook/roles/install-tomcat/tasks/main.yml:37 ------------- 

install-tomcat : Clean DEMO environment --------------------------------- 0.26s 
/users/demo/vivek-playbook/roles/install-tomcat/tasks/main.yml:19 ------------- 

start-tomcat : Start Tomcat --------------------------------------------- 0.15s 
/users/demo/vivek-playbook/roles/start-tomcat/tasks/main.yml:5 ----------------

install-tomcat : debug -------------------------------------------------- 0.06s 
/users/demo/vivek-playbook/roles/install-tomcat/tasks/main.yml:11 ------------- 

install-tomcat : debug -------------------------------------------------- 0.06s 
/users/demo/vivek-playbook/roles/install-tomcat/tasks/main.yml:29 ------------- 

install-tomcat : debug -------------------------------------------------- 0.04s 
/users/demo/vivek-playbook/roles/install-tomcat/tasks/main.yml:47 ------------- 

start-tomcat : debug ---------------------------------------------------- 0.04s 
/users/demo/vivek-playbook/roles/start-tomcat/tasks/main.yml:10 --------------- 

Нажмите следующий URL, и вы будете перенаправлены на страницу, показанную ниже – http://10.76.0.134:11677/HelloWorld/HelloWorld

Привет, мир

В развернутой войне только есть сервлет, который отображает «Hello World». Подробный вывод показывает время, затрачиваемое каждой задачей из-за записи, добавленной в файл ansible.cfg –

[defaults] 
callback_whitelist = profile_tasks 

Ansible – переменные

Переменные в playbooks очень похожи на использование переменных в любом языке программирования. Это поможет вам использовать и назначить значение переменной и использовать ее в любом месте книги. Можно поставить условия вокруг значения переменных и соответственно использовать их в сборнике игр.

пример

- hosts : <your hosts> 
vars:
tomcat_port : 8080 

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

Теперь, взяв ссылку из примера, поделился. Следующий код принадлежит одной из ролей (install-tomcat) –

block: 
   - name: Install Tomcat artifacts 
      action: > 
      yum name = "demo-tomcat-1" state = present 
      register: Output 
          
   always: 
      - debug: 
         msg: 
            - "Install Tomcat artifacts task ended with message: {{Output}}" 
            - "Installed Tomcat artifacts - {{Output.changed}}" 

Здесь выводом является используемая переменная.

Давайте пройдемся по всем ключевым словам, использованным в приведенном выше коде –

  • block – Доступный синтаксис для выполнения данного блока.
  • name – Соответствующее имя блока – это используется при ведении журнала и помогает в отладке того, все блоки которого были успешно выполнены.
  • action – код рядом с тегом action – это задача, которая должна быть выполнена. Действие снова является ключевым словом Ansible, используемым в yaml.
  • register – Выходные данные действия регистрируются с использованием ключевого слова register, а Output – это имя переменной, которая содержит выходные данные действия.
  • always – снова ключевое слово Ansible, оно указывает, что ниже всегда будет выполняться
  • msg – отображает сообщение

block – Доступный синтаксис для выполнения данного блока.

name – Соответствующее имя блока – это используется при ведении журнала и помогает в отладке того, все блоки которого были успешно выполнены.

action – код рядом с тегом action – это задача, которая должна быть выполнена. Действие снова является ключевым словом Ansible, используемым в yaml.

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

always – снова ключевое слово Ansible, оно указывает, что ниже всегда будет выполняться

msg – отображает сообщение

Использование переменной – {{Output}} ->

Это будет читать значение переменной Output. Кроме того, поскольку он используется на вкладке msg, он напечатает значение выходной переменной.

Кроме того, вы также можете использовать вложенные свойства переменной. Как в случае с проверкой {{Output.changed}}, изменился ли вывод, и, соответственно, использовать его.

Обработка исключений в Playbooks

Обработка исключений в Ansible похожа на обработку исключений в любом языке программирования. Пример обработки исключений в playbook показан ниже.

tasks: 
   - name: Name of the task to be executed 
      block: 
         - debug: msg = 'Just a debug message , relevant for logging' 
         - command: <the command to execute> 
      
      rescue: 
         - debug: msg = 'There was an exception.. ' 
         - command: <Rescue mechanism for the above exception occurred) 
      
      always: 
         - debug: msg = "this will execute in all scenarios. Always will get logged" 

Ниже приводится синтаксис для обработки исключений.

  • спасение и всегда являются ключевыми словами, специфичными для обработки исключений.
  • Блок – это место, где написан код (все, что должно быть выполнено на Unix-машине).
  • Если команда, записанная внутри функции блока, не выполняется, то выполнение достигает блока спасения, и оно выполняется. В случае отсутствия ошибки в команде в функции блокировки, восстановление не будет выполнено.
  • Всегда исполняется во всех случаях.
  • Так что если мы сравниваем то же самое с Java, то это похоже на попытку, поймать и, наконец, блокировать.
  • Здесь Block аналогичен блоку try, в котором вы пишете код для выполнения, а функция спасения аналогична блоку catch и всегда похожа на finally .

спасение и всегда являются ключевыми словами, специфичными для обработки исключений.

Блок – это место, где написан код (все, что должно быть выполнено на Unix-машине).

Если команда, записанная внутри функции блока, не выполняется, то выполнение достигает блока спасения, и оно выполняется. В случае отсутствия ошибки в команде в функции блокировки, восстановление не будет выполнено.

Всегда исполняется во всех случаях.

Так что если мы сравниваем то же самое с Java, то это похоже на попытку, поймать и, наконец, блокировать.

Здесь Block аналогичен блоку try, в котором вы пишете код для выполнения, а функция спасения аналогична блоку catch и всегда похожа на finally .

Loops

Ниже приведен пример, демонстрирующий использование Loops в Ansible.

Задача состоит в том, чтобы скопировать набор всех файлов war из одного каталога в папку tomcat webapps.

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

Изначально в команде ‘shell’ мы сделали ls * .war. Итак, он перечислит все военные файлы в каталоге.

Вывод этой команды берется в переменную с именем output.

Для цикла используется синтаксис with_items.

with_items: “{{output.stdout_lines}}” -> output.stdout_lines дает нам построчный вывод, а затем мы зацикливаемся на выходе с помощью команды with_items из Ansible.

Прикрепление выходных данных примера, чтобы понять, как мы использовали stdout_lines в команде with_items.

--- 
#Tsting 
- hosts: tomcat-node 
   tasks: 
      - name: Install Apache 
      shell: "ls *.war" 
      register: output 
      args: 
         chdir: /opt/ansible/tomcat/demo/webapps 
      
      - file: 
         src: '/opt/ansible/tomcat/demo/webapps/{{ item }}' 
         dest: '/users/demo/vivek/{{ item }}' 
         state: link 
      with_items: "{{output.stdout_lines}}"

Блоки

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

Пример блоков описан выше в использовании переменных, обработке исключений и циклах.

Conditionals

Условные выражения используются там, где необходимо выполнить определенный шаг на основе условия.

--- 
#Tsting 
- hosts: all 
   vars: 
      test1: "Hello Vivek" 
   tasks: 
      - name: Testing Ansible variable 
      debug: 
         msg: "Equals" 
         when: test1 == "Hello Vivek" 

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

Условный вывод

Просто измените значение переменной test1 с Hello Vivek на Hello World и посмотрите результат.

Измененный условный вывод

Ansible – Расширенное исполнение

В этой главе мы узнаем, что такое расширенное выполнение с Ansible.

Как ограничить выполнение задач

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

Здесь в оригинальной пьесе «Остановка и запуск» были частью разных ролей в одной и той же пьесе, но это можно сделать с помощью тегов. Мы можем предоставить разные теги для разных ролей (которые, в свою очередь, будут иметь задачи), и, следовательно, на основе тегов, предоставленных исполнителем, выполняется только указанная роль / задача. Поэтому для приведенного выше примера мы можем добавить теги, подобные следующим:

- {role: start-tomcat, tags: ['install']}} 

Следующая команда помогает в использовании тегов –

ansible-playbook -i hosts <your yaml> --tags "install" -vvv

С помощью приведенной выше команды будет вызываться только роль start-tomcat. Предоставленный тег чувствителен к регистру. Убедитесь, что точное соответствие передается команде.

Как ограничить выполнение хостами

Есть два способа добиться выполнения определенных шагов на определенных хостах. Для конкретной роли определяют хосты – для каких конкретных хостов должна выполняться эта роль.

пример

- hosts: <A> 
   environment: "{{your env}}" 
   pre_tasks: 
      - debug: msg = "Started deployment. 
      Current time is {{ansible_date_time.date}} {{ansible_date_time.time}} " 
     
   roles: 
      - {role: <your role>, tags: ['<respective tag>']} 
   post_tasks: 
      - debug: msg = "Completed deployment. 
      Current time is {{ansible_date_time.date}} {{ansible_date_time.time}}" 
 
- hosts: <B> 
   pre_tasks: 
      - debug: msg = "started.... 
      Current time is {{ansible_date_time.date}} {{ansible_date_time.time}} " 
        
   roles: 
      - {role: <your role>, tags: ['<respective tag>']} 
   post_tasks: 
      - debug: msg = "Completed the task.. 
      Current time is {{ansible_date_time.date}} {{ansible_date_time.time}}" 

Согласно приведенному выше примеру, в зависимости от предоставленных хостов, соответствующие роли будут вызываться только. Теперь мои хосты A и B определены в хостах (файл инвентаря).

Альтернативное решение

Другим решением может быть определение хостов playbook с помощью переменной, а затем передача определенного адреса хоста через –extra-vars –

# file: user.yml  (playbook) 
--- 
- hosts: '{{ target }}' 
   user: ... 
playbook contd…. 

Запуск Playbook

ansible-playbook user.yml --extra-vars "target = "<your host variable>"

Если {{target}} не определен, книга воспроизведения ничего не делает. Группа из файла hosts также может быть пропущена, если это необходимо. Это не вредит, если дополнительные переменные не предоставляются.

Playbook ориентирован на одного хоста

$ ansible-playbook user.yml --extra-vars "target = <your hosts variable>" --listhosts 

Ansible – Устранение неполадок

Наиболее распространенные стратегии отладки PlaySbooks Ansible используют модули, приведенные ниже –

Отладка и регистрация

Эти два модуля доступны в Ansible. Для целей отладки нам нужно разумно использовать два модуля. Примеры демонстрируются ниже.

Используйте многословие

С помощью команды Ansible можно обеспечить уровень многословия. Вы можете запускать команды с уровнем детализации один (-v) или два (-vv).

Важные моменты

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

Если вы не цитируете аргумент, который начинается с переменной. Например,

vars: 
   age_path: {{vivek.name}}/demo/ 
   
{{vivek.name}} 

Это выдаст ошибку.

Решение

vars: 
   age_path: "{{vivek.name}}/demo/" – marked in yellow is the fix. 
 
How to use register -> Copy this code into a yml file say test.yml and run it  
--- 
#Tsting 
- hosts: tomcat-node 
   tasks: 
 
   - shell: /usr/bin/uptime 
      register: myvar 
      - name: Just debugging usage 
         debug: var = myvar 

Когда я запускаю этот код с помощью команды Ansible-playbook -i hosts test.yml, я получаю вывод, как показано ниже.

Если вы видите yaml, мы зарегистрировали вывод команды в переменную myvar и просто распечатали вывод.

Текст, помеченный желтым, говорит нам о свойстве переменной –myvar, которое можно использовать для дальнейшего управления потоком. Таким образом, мы можем узнать о свойствах, которые предоставляются конкретной переменной. Следующая команда отладки помогает в этом.

$ ansible-playbook -i hosts test.yml 

PLAY [tomcat-node] ***************************************************************
**************** ****************************************************************
*************** ****************************** 
 
TASK [Gathering Facts] *****************************************************************
************** *****************************************************************
************** ************************** 
Monday 05 February 2018  17:33:14 +0530 (0:00:00.051) 0:00:00.051 ******* 
ok: [server1] 
 
TASK [command] ******************************************************************
************* ******************************************************************
************* ********************************** 
Monday 05 February 2018  17:33:16 +0530 (0:00:01.697) 0:00:01.748 ******* 
changed: [server1] 
 
TASK [Just debugging usage] ******************************************************************
************* ******************************************************************
************* ********************* 
Monday 05 February 2018  17:33:16 +0530 (0:00:00.226) 0:00:01.974 ******* 
ok: [server1] => { 
   "myvar": { 
      "changed": true, 
      "cmd": "/usr/bin/uptime", 
      "delta": "0:00:00.011306", 
      "end": "2018-02-05 17:33:16.424647", 
      "rc": 0, 
      "start": "2018-02-05 17:33:16.413341", 
      "stderr": "", 
      "stderr_lines": [], 
      "stdout": " 17:33:16 up 7 days, 35 min,  1 user,  load average: 0.18, 0.15, 0.14", 
      "stdout_lines": [ 
         " 17:33:16 up 7 days, 35 min,  1 user,  load average: 0.18, 0.15, 0.14" 
      ] 
   } 
} 
 
PLAY RECAP ****************************************************************************
**********************************************************************************
 ************************************** 
server1 : ok = 3    changed = 1    unreachable = 0    failed = 0 

Общие проблемы Playbook

В этом разделе мы узнаем о нескольких распространенных проблемах Playbook. Проблемы –

  • квотирование
  • вдавливание

Playbook написан в формате yaml, и две вышеупомянутые проблемы являются наиболее распространенными в yaml / playbook.

Yaml не поддерживает отступы на основе табуляции и поддерживает отступы на основе пробелов, поэтому нужно быть осторожным с этим.

Примечание. Как только вы закончите писать yaml, откройте этот сайт ( https://editor.swagger.io/ ) и скопируйте и вставьте ваш yaml с левой стороны, чтобы обеспечить правильную компиляцию yaml. Это всего лишь совет.

Swagger квалифицирует как ошибки, так и ошибки.

Источник

Просмотры: 1 368

Понравилась статья? Поделить с друзьями:
  • Дарсонваль в гинекологии инструкция по применению взрослым
  • Руководство по ремонту kia carens
  • Hyundai грузовые руководство по ремонту
  • Руководство по эксплуатации kuppersbusch
  • Где взять инструкции по сборке лего