...

четверг, 3 октября 2013 г.

ansible

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

Итак, у нас есть группы хостов:



























WebServersG1webserver1-g1, webserver2-g1
WebServersG2webserver1-g2, webserver2-g2
WebServersProxywebserver-proxy1, webserver-proxy2
DataBasedb1, db2
DataBaseSlavedbs1, dbs2
SomeServerssomeserver1, 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 nxinx




И 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. Мой внутренний редактор ушел в отпуск. Прошу простить за ошибки и неровный почерк. Я всегда читаю ЛС и всегда говорю «спасибо» за исправления :)


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий