Ansible

  • Tutorial
Ansible — yet another система управления конфигурациями. Отличительная особенность — простота, при большой гибкости. И это не просто слова — дальше я покажу на примерах несколько простейших операций и познакомлю вас с некоторыми “бест практис”.

Итак, у нас есть группы хостов:
WebServersG1 webserver1-g1, webserver2-g1
WebServersG2 webserver1-g2, webserver2-g2
WebServersProxy webserver-proxy1, webserver-proxy2
DataBase db1, db2
DataBaseSlave dbs1, dbs2
SomeServers someserver1, someserver2

Мы хотим подготовить все хосты к адекватной работе — установить необходимый набор софта (htop, zsh, vim, iftop, sudo, mc, tmux, wget), скопировать свои ключи и конфиги и поставить и сконфигурировать софт специфичный для этого сервера.
Ansible подразумевает минимум два файла для начала работы — инвентарный файл, в который мы пишем список хостов и делим их по группам — inventory и файл задачplaybook.
Они нужны для того, чтобы когда мы все сделаем запустить все красиво:

ansible-playbook -i инвентарный_файл playbook.yml

Давайте создадим инвентарный файл по имени “infrastructure” на основе наших хостов:

[WebServersG1]
webserver1-g1
webserver2-g1
[WebServersG2]
webserver1-g2
webserver2-g2
[WebServersProxy]
webserver-proxy1
webserver-proxy2
[DataBase]
db1
db2
[DataBaseSlave]
dbs1
dbs2
[SomeServers]
someserver1
someserver2

Собственно все не плохо, но наши хосты из групп WebServersG1 и WebServersG2 отличаются только структурой каталогов, количеством подключений и репозиторием. А WebServersProxy отличается от них только конфигом nginx и отсутствием какого-то софта. К тому же может понадобиться сделать какую-то задачу сразу на всех трех группах. Так что давайте сродним эти три группы и дадим им родителя:

[WEB:children]
WebServersG1
WebServersG2
WebServersProxy

По структуре инвентарного файла: ansible считает группой все, чья строка в описании начинается с ‘[‘ и заканчивается ‘]’. Все что под этой строчкой и до начала следующей группы — хосты. У группы могут быть дети — другие группы, которые перечисляются после [название группы:children] и существуют.
Еще я коротко коснусь переменных, которые тоже можно описывать сразу в этом списке.

webserver1-g1 ansible_ssh_port=5555 ansible_ssh_host=192.168.1.50

Тут мы назначили ssh порт и ssh хост. По практике скажу что другие переменные в inventory файле его захламляют и делают нечитаемым. Да и нестандартные порты лучше перечислять в таком виде:

webserver1-g1:5555

И раз уж зашла речь о портах — порт по умолчанию для всех хостов (как и многое другое) можно назначить и в ansible.cfg, но не суть.
Итак, мы только что создали список хостов. Давайте теперь создадим список задач, а я окунусь в воспоминания.
В далекие времена (примерно май-июль этого года), до выхода версии 1.2 ролей не было в принципе, мы довольствовались обычными тасками и плейбук выглядел как елка пестревшая инклудами. Потом появились роли, а совсем недавно, неделю-две назад, в версии 1.3 — наследование ролей. И мы, как истинные джедаи, будем пользоваться тем что имеем сейчас. И давайте разберемся, наконец, что такое эти плейбуки и роли, а то непонятно.

Плейбуки — исполняемый набор чего-угодно. Они, в отличии от chef, запускаются один раз и только по вашей команде. Хотя нет ничего, что бы помешило поставить задачу в крон. Раньше под плейбуком подразумевался основной список задач, но сейчас они превратились в набор указателей на нужные нам роли.
Роли, в свою очередь, это набор задач, шаблонов, тригеров-обработчиков, переменных, файлов и ссылок на другие роли распиханых по каталогам в стандартной для ansible структуре. Роли лучше всего группировать по логическим группам. Я, например, в рамках поставленной выше задачи, выделю такие роли:
1) преподготовка — установка разных админских софтов, создание пользователей с ключами, генерация локалей, копирование конфигов и т.п.
2) установка софта для нужного в итоге сервиса и копирование его конфигов
3) создание необходимой структуры директорий, копирование гита и т.п.

Итак, начнем создание плейбука main.yml c пока единственной ролью preconf

- hosts: all
  roles: 
    - preconf
  tags: preconf

Тут мы для всех хостов ‘all’ назначили роль preconf и добавили тег preconf. Теги нужны для того, чтобы потом иметь возможность исполнить только какую-то часть плейбука.
Дальше, когда ansible видит назначение роли он начинает судорожно искать одноименный каталог в ./roles. В нашем случае это будет ./roles/preconf
Там уже должна быть готова структура роли, а именно: tasks/main.yml
Это основной файл, в котором перечисляются задачи.
Также там может существовать каталог ‘templates’, в который мы складываем шаблоны конфиг-файлов, ‘files’, в котором будут лежать разные нужные нам файлы, ‘handlers’ — те самые преславутые тригеры-обработчики, а так же meta/main.yml в котором мы описываем ссылки на роль и variables — переменные роли.
Я предлагаю разбираться по-порядку, но если хотите — можете читать текст серху вниз резко дергая головой справа-налево.
Итак, задачи.
Для нашей роли preconf создадим файл ./roles/preconf/tasks/main.yml
Обычно задачи выглядят так:

- name: Имя задачи
  модуль: параметры модуля.

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

- name: installing zsh
  apt: pkg=zsh

В данном случае мы использовали модуль apt для установки на наши Debian сервера программы zsh.
Конечно, можно заспамить таск-лист отдельной задачей под установку каждой программы, но такие файлы очень плохо читаются. Поэтому мы воспользуемся очередью, котораю можно вызвать через ‘with_items’:

- name: installing zsh
  apt: pkg=$item
  with_items:
    - zsh
    - htop
    - sudo
    - iftop
    - tcpdump
    - mc
    - wget
    - vim
    - tmux
    - facter

В синтаксисе YAML, если я ничего не путаю, подобная запись обозначает массив. И элементы этого масива, словно бы пройдя через xargs по очереди присваиваются переменной item, которую мы вызвали выше и уже с новым значением выполняются в задаче. Раз за разом, пока не закончится список.
Теперь мы создадим всех пользователей отдела сопровождения и к случаю воспользуемся переменными.
Добавление пользователей выглядит как-то так:

- name: Add User Pupkin
  user: name=’pupkin’

Так наш пользователь добавится на ура. Но мы же хотим чтобы пользователь пупкин пользовался zsh и являлся членом группы sudo? А давайте так и сделаем, ведь модуль ‘user’ поддерживает кучу всего.

- name: Add User Pupkin
  user: name=’pupkin’ shell=’/bin/zsh’ groups=’sudo’

Мы уже выучили with_items, поэтому мы им воспользуемся для добавения нескольких пользователей… Но… это ведь надо две переменных передавать в потоке…
Ничего сложного. Ansible поддерживает хеши — массивы в виде ‘ключ: значение’. Удобнее всего записывать хеши в jinja2 формате.

- name: Add BestAdminsTeam
  user: name={{ item.user }} shell={{ item.shell }} groups=’sudo’
  with_items:
    - { user: ‘pupkin’, shell: ‘/bin/zsh’ }
    - { user: ‘oldfag’, shell: ‘/bin/sh’ }

Собственно что мы только что сделали? Мы создали массив такого вида:
{ {user: ‘pupkin’, shell: ‘/bin/zsh’ }, { user: ‘oldfag’, shell: ‘/bin/sh’ } } из которого будем брать элементы по-одному и присваивать их переменной $item ( или, выражаясь jinja2 языком — {{ item }} ). После этого мы будем открывать элементы хеша — {{ item.user }} и {{ item.shell }} соответственно. То есть мы получим консистентный список переменных для каждого пользователя.
Теперь давайте добавим ключи нашим пользователям. Для этого существует отличный модуль ‘authorized_key

- name: Add BestAdminsKey
  authorized_key: user={{ item.user }} key="{{item.key}}"
  with_items:
    - { user: ‘pupkin’, key: ‘ssh-rsa pupkin_pub_key’ }
    - { user: ‘oldfag’, key: ‘ssh-rsa oldfag_pub_key’ }

В принципе оно работать будет и так, но немного неудобно каждый раз перечислять пользователей: очень легко забыть куда-то добавить нового члена команды и мне надо как-то рассказать про переменные, а тут такой случай.
Давайте заведем переменную, в которую складем всех наших пользователей с их ключами, шелами, возможно, путями к конфигам и всем остальным. Скласть юзеров в переменную легко, но куда скласть саму переменную?
Для этого у ansible существует несколько вариантов решения.
Можно указывать переменные конкретно для каждого хоста в папке ./host_vars/имя_хоста — это не пододит для нашей цели
Можно делать переменные для группы хостов (группы, напомню, мы делали в файле инвентаризации) и ложить их в ./group_vars/имя_группы — можно, конечно, создать переменную тут, для группы all. Но это не “чистая работа”.
Можно присваивать переменные прямо в плейбуках. Но так смотриться неопрятно.
Еще у каждой роли есть “значения по умолчанию” в каталоге defaults, но это не совсем то, что нам надо
Ну и наконец переменные роли — похоже это самое то.

Создадим файлик ./roles/main/variables/main.yml в который запишем:

ssh_super_team:
    - { user: ‘pupkin’, key: ‘ssh-rsa pupkin_pub_key’, shell: ‘/bin/zsh’}
    - { user: ‘oldfag’, key: ‘ssh-rsa oldfag_pub_key’, shell: ‘/bin/sh’ }

Теперь в задаче можем использовать переменную $ssh_super_team вот так:

- name: Add BestAdminsTeam
  user: name={{ item.user }} shell={{ item.shell }} groups=’sudo’
  with_items:
    - $ssh_super_team

- name: Add BestAdminsKey
  authorized_key: user={{ item.user }} key="{{item.key}}"
  with_items:
    - $ssh_super_team

Ну и чтобы сделать всем удобно — скопируем нашим пользователям файл .vimrc — одинаковый для обоих пользователей. В каталог ./roles/main/files/ сунем файл по имени ‘vimrc’ и сделаем такой таск:

- name: copy vimrc file
  copy: src=”vimrc” dest=”/home/{{item.user}}/.vimrc”
  with_items:
    - $ssh_super_team

Вуаля.

Кстати, я иногда перечитываю то, что пишу — получилось обьемно. Пора закруглять все что еще не круглое, поэтому я сразу перескочу на конфигурацию nginx:
По кальке создадим роль nginx и в файл ./roles/nginx/tasks/main.yml запишем:

- name: install nginx 
  apt: pkg=’nginx’

Хм, но ведь не может быть на всех серверах nginx с одинаковым конфигом? Может, конечно, но это неудобно. Давайте придумаем шаблон — в ansible для этого надо использовать знакомый многим jinja2 синтаксис.
Создаем файл в ./roles/nginx/templates/ и назовем его nginx_site_conf.j2
А содержимое сделаем таким:

server {
  listen       80;
  server_name  {{ item.sitename }};
  root “/var/www”
}

Дальше нам надо разложить уникальные для каждого хоста перменные: накидаем файлов в ./host_vars/имя_хоста

nginx:
  - { sitename: “www.example.com” }

И определим переменную по-умолчанию для этой роли: в файле ./roles/nginx/defaults/main.yml

nginx:
  - { sitename: “default” }

Теперь создадим задачу:

- name: nginx config for sites
  template: src=”nginx_site_conf.j2” dest=”/etc/nginx/sites-enabled/{{ item.sitename }}
  with_items:
    - $nginx
  notify:
    - restart nginx

И ansible, при проходе этого таска попробует скопировать шаблон ”nginx_site_conf.j2” с переменной $nginx, которая будет прочитана либо из переменных хоста, либо из дефолтных переменных роли. И если копируемый шаблон будет отличаться от конфига на машине — выполнится handler, который выглядит так: ./roles/nginx/handlers/main.yml

- name: restart nginx
  action: service name=nginx state=reloaded

Теперь соберем из этих двух намеренно упрощенных ролей одну единственную для группы хостов WebServersG1. Сейчас мы не будем ничего усложнять, а просто сделаем в ней ссылку на две уже готовые роли. Запишем в файл roles/myapp/meta/main.yml такие строчки:

---
dependencies:
  - { role: preconf }
  - { role: nginx }

И, наконец, плейбук playbook.yml:

- hosts: WebServersG1
  roles:
    - WebServersG1

Кстати, во время построения зависимостей тоже можно использовать переменные для уточнения какой-то информации конкретно для этой роли. Но об этом, как и о зависимостях, сложных шаблонах, проверки выполнения задачи, распаралеливание выполнения и многом-многом другом мы поговорим уже в следующий раз.
А пока можно почитать отличную, не то что у других, документацию: www.ansibleworks.com/docs

P.S. Мой внутренний редактор ушел в отпуск. Прошу простить за ошибки и неровный почерк. Я всегда читаю ЛС и всегда говорю «спасибо» за исправления.

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

Что вам больше хочется увидеть по ansible?
Поделиться публикацией
Похожие публикации
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 40
  • +1
    Отличный инструмент, использую в продакшне уже больше года, сначала была проблема с нехваткой модулей, но сейчас почти все нужное уже написано. Из плюсов могу отметить легкость написания своих модулей, и легкость продвижения их в апстрим. Я сам написал парочку модулей, и один чужой модуль патчил — все изменения приняли в апстрим очень быстро. Ну и декларативность описания всего позволяет читать хорошо написанные плейбуки как английский текст — это супер.
    • 0
      Поделитесь ссылками на свои PR?
    • +1
      Сейчас стоим на пороге внедрения системы управления конфигурациями, и выбираем инструмент.
      Глянул на ansible в гитхабе — 767 репозиториев, а про puppet — за 10 тысяч.
      Подскажите на это стоит обратить внимание? Или в ansible важно другое?
      Просто разница в количестве кода колоссальна.
      • +1
        Я как-то не особо понял что значат репозитории на гитхабе для системы управления конфигурациями. Вы имеете ввиду что мало людей используют? Или что негде посмотреть как используют? Ну у ansible есть отличная документация. А используют ansible такие компании как evernote.
        К тому же ansible заметно выигрывает у puppet за счет простоты написания и читания конфигов. Это очень важно.
        • 0
          Количество репозиториев на гитхабе обозначает большое количество разных написанных модулей для системы.
          Я про это.
          Вы же не будете расписывать настройку LAMP каждый раз сами, а один раз выложите модуль на гитхаб и будете им пользоваться.
          • 0
            Эм. Настройка ламп делается через роль LAMP, которая включает в себя роли:

            разворачивание сервера в амазоне
            установка и конфигурирование через template — apache
            тоже для mysql
            тоже для php

            Суть в том, что вместо использования готовых модулей намного легче написать свою роль, используя модули ansible. К тому же так можно сделать отличную подгонку под свою же инфраструктуру.
            Например, я, в зависимости от того какая виртуализация, какая роль у сервера, что на нем стоит выставлял разные конфиги у collectd просто редактируя template и передаваемые при инклуде роли переменные.
        • 0
          Я в свое время перешел с Chef на Ansible именно из-за простоты написания/чтения задач, и декларативности. Если у вас не стоит задач управления динамично меняющейся средой, навроде облака серверов — Ansible позволяет решать задачи заметно проще. Ну и agentless работа по ssh это тоже очень круто.
          • 0
            К сожалению (или к счатью), наша среда крайне динамична и гетерогенна.
            • 0
              Тогда посоветую Chef, он создавался именно для этого, учтя все недостатки puppet. В Chef есть и заморозка версий, и databags, и environments, и главное — сервер знает все о своих нодах, т.е. можно строить конфиг с использованием этих знаний. В Ansible тоже есть знание о нодах в playbook, но конечно этого будет мало для серьезной динамики.
              • 0
                Ну вообще-то не так. ansible умеет инклудить в свой же inventory, может работать с списком хостов в группе, может прописывать дополнительные переменные для отдельных хостов. Я собственно, кроме заморозки, которая решается исключением хоста из группы и включением его в другую группу, ничего особого и не вижу.
                Хотя с гетерогенностью в ansible все действительно не особо гладко.
                • +1
                  Да я знаю, что может. Но в Chef это сделано удобнее, ну и если в Chef не использовать чужие cookbook, которые в основном жуткий императивный говнокод, то можно добиться хороших результатов по читаемости и понятности, сравнимых с Ansible. Для каждой задачи надо выбирать наиболее подходящий инструмент. До определенного уровня задачу проще решать с помощью Ansible, после — с помощью Chef.
                  • 0
                    Полностью соласен. Но по вопросу мне показалось что как раз готовые кукбуки и будут использоваться.
                    А самописные кукбуки на чефе очень крутые, но никто их не пишет, так как это достаточно сложно. Потому ansible и лучше :)
                    • 0
                      То есть я хочу сказать что лучше тот инструмент, который в принципе может немного меньше, но менее запутанный. И лучше он потому что им будут пользоваться и писать. В то время как чеф очень часто состоит из нескольких стандартных кукбуков апача, nginx, mysql и все.
            • 0
              Тут зависит от инфраструктуры, для жесткой — удобней декларативный инструмент, я вот когда первый раз документацию по puppet читал чуть мозг не сломал, но это может только у меня так.
              • –4
                Обратите внимание на CF-Engine. К моему глубокому сожалению, это на текущий момент единственный вменяемый инструмент управления конфигурациями. Ни Puppet, ни Chef, ни Ansible, к ещё более глубокому сожалению, не приспособлены для задач серьёзнее, чем «склонировать очередной LAMP» и «git clone this there».
                • 0
                  Опишите в чем преиущество? Насколько сложно в нем решать типовые задачи? А нетиповые?
                  Идеальный вариант ответа — отдельный топик :)
                  • 0
                    Преимущества ровно два: отсутствие зависимостей и pull вместо push.
                    • 0
                      В режиме pull умеют работать все вышеперичисленные. Или вы что то другое в виду имели?
                      • 0
                        pull — т.е. обновление инициируется с воркера?
                    • 0
                      Кстати, я cfengine смотрел, но не увидел в нем ничего особенного. Ansible, как и шеф, как и паппет умеет все — включая выполнение баша и команд с регистрацией exit статуса и дальнейшими действиями по ситуации.
                      • 0
                        Наверное нужно всё же определиться какую именно проблему мы пытаемся решить. Если нам нужно полностью автоматическое конфигурирование сервера, при чём так, чтобы последнее действие сделанное живым человеком было «вынуть загрузочную флешку» — вариантов без CFEngine нет совсем, а с ним это превращается в нетривиальную задачу. Если же нужно создавать виртуалки на амазоне для очередного социального облачного стартапа, то можно обойтись и скриптом на баше.

                        Хочу отдельно отметить, я не осуждаю ничей выбор. Реальность такова, что где-то задача с трудом решается только одним средством (и это очень, очень печально), а где-то задача решается чем угодно.
                        • +1
                          Я, конечно, не знаю, что какая у вас задача решается. Но у меня в полностью автоматическом режиме с голой ОС разворачивается довольно сложный проект с riak, redis, postgresql, rabbitmq, haproxy и nginx. И все это автоматом собирается в кластер. И задача эта решена без особых мучений с использованием только Ansible. И более того — я больше года работал с Chef, и точно знаю, что с его помощью подобная задача решается ничуть не сложнее, просто чуть более трудоемко. Так что я сомневаюсь, что CFengine в чем-то превосходит chef, ansible или puppet. Напишите статью, покажите его супер-возможности, а мы сравним.
                          • 0
                            Задача простая: есть сервер без ничего вообще, загрузочная флешка и условная обезьяна, которая умеет засунуть сервер в стойку, засунуть флешку в разъём и нажать на кнопку «Вкл». Нужно получить сервер, роль которого уже предопределена где-то в глобальной базе всех серверов. С учётом аспектов безопасности.
                            • 0
                              можно обойтись и без обезьяны:
                              В рамках одной сети это решается нетинсталом.
                              Если это не одна сеть — можно посмотреть на vagrant, например.

                              Если нужна обезьяна, то можно посмотреть в сторону preseed файлов, например.

                              В любом случае, если бы подобную задачу поставили мне я бы смотрел в сторону netinstall и назначения роли по имени сервера.

                              • 0
                                Без обезьяны невозможно. Сервер сам по себе в стойке не материализуется. Без preseed тоже невозможно. Сети очень разные. Разные континенты, города, датацентры, по всему миру. Netinstall не работает.

                                Про Vagrant хотелось бы послушать подробнее. Каким образом это поделие способно засетапить железку. Мне действительно очень интересно, возможно я что-то пропустил и не догоняю теперь.
                                • 0
                                  Кстати, а что насчет cobbler?
                                  • 0
                                    Главная проблема всё та же: внешние зависимости. Лично меня ещё не устраивает RedHat-ориентированность, но тут уж на вкус и цвет товарищей нет. Выбирая между Cobbler и Ansible, выбор однозначно в пользу скрипта на баше Ansiblе.
                                    • 0
                                      Ну… Ansible поддерживает cobbler.
                          • +1
                            Это общие слова. Или у вас cfengine управляет людьми?
                            • 0
                              Если бы CFEngine управлял ещё и людьми, счастье было бы очень близко. Но нет.
                    • 0
                      Спасибо.
                      Не в курсе, насколько он хорошо работает с FreeBSD (установка пакетов, управление пользователями — всё имеет несколько специфичный для линуксовых програм вид)?
                      • 0
                        C freeBSD он работает очень так себе. Собственно я в свое время ограничился заведением пользователей и установкой программ через pkg_add -r — там где он работал. Но вообще — все не очень хорошо, но можно самому написать модули и поделиться с сообществом. Все будут только благодарны.
                        • –1
                          Жаль. Хотелось найти какое-то уже более-менее работающее решение.
                      • 0
                        Спасибо, очень приятная статья. Как раз руки дошли до ansible.

                        У меня вопрос, никак не могу набрести на кошерное решение, как получать значения хостов и плейбуки динамически с VCS? В принципе нет проблемы сделать скрипт, который пуллит изменения из репы и потом запускает выполнение плейбука, но пахнет не кошерно :)
                      • 0
                        А кто-нить пробовал в playbook воткнуть что-то на подобии:
                        ps ax|grep bin/post|grep -v grep|awk '{print $NF}' | awk -F '/' '{print $5}'

                        ? Как использовать пайпы в комманда в playbook?
                        • 0
                          Попробуйте всю команду взять в кавычки:
                          command: bash -c "ps ax|grep bin/post|grep -v grep|awk '{print $NF}' | awk -F '/' '{print $5}'"

                          Если не поможет, то засунуть в файл и дергать файл из playbook'а.

                          З.Ы. вряд ли Ваш вопрос еще актуален :)
                          • 0
                            docs.ansible.com/command_module.html
                            It will not be processed through the shell, so variables like $HOME and operations like "<", ">", "|", and "&" will not work (use the shell module if you need these features).

                            Собственно.
                            • 0
                              Тем не менее
                              command: bash -c "cd $HOME && ps -o pid | xargs touch"

                              заработало
                              • +1
                                Ну, в общем, да, такое нормально передаётся. Но всё равно это надо вызывать специальным модулем shell, а не подпоркой.

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

                        Самое читаемое