yum update
и apt-get upgrade
.Конечно, это неправильно. Я давно присматривался к chef и Puppet, которые, как я думал, решат все мои проблемы. Но я смотрел на конфиги знакомых проектов и откладывал. Это же нужно изучать, разбираться с ruby, бороться с многочисленными, по отзывам, косяками и ограничениями. Две недели назад статья Георгия amarao стала животворящим пинком. Даже не сама статья, а перечисление систем управления конфигурацией. После чтения комментариев и лёгкого гугления решил: возьму Ansible. Потому что питон, и на проблемы никто не жалуется.
Что ж, тогда я первым буду.
Сначала я нарыл кучу документации и учебников по Ansible, начиная с бесполезного видеоролика Quick Start на официальном сайте. Их, конечно, много, сделаны для разных задач и написаны разными людьми, но объединяет их одно: учебники делали для людей, которые уже понимают Ansible. Для людей со сферическим сервером в вакууме, которым достаточно подсказать, что бывают роли, модули и таски. Но я пришёл с clean slate и собрал все грабли, какие нашёл. Надеюсь, эта заметка поможет вам их обойти.
От систем управления конфигурациями я ждал чудес, вроде автоматически обновляемых приложений из git. Но оказалось, что Ansible — это лишь способ сохранить последовательность действий при настройке нового сервера. Вы сможете сделать в Ansible только то, что умеете делать из консоли самостоятельно. Чудес нет.
Начало. Vagrant
Задача: не делаю новый хост, потому что хочу сохранить ip. То есть, очищу дроплет через контрольную панель, затем инициализирую с помощью Ansible. План: написать playbook и отладить его на Vagrant.
Начать очень сложно. Все учебники по Ansible начинаются с описания inventory, куда нужно прописывать адрес сервера. Но какой ip у вагранта? Чёрт его знает. В документации по Ansible есть инструкция, как запустить playbook в Vagrant; в документации по Vagrant есть инструкция по подключению Ansible, и они не то чтобы идентичны. В итоге, забил на поиск ip и взял общее: минимальный
Vagrantfile
, который запускает playbook.Vagrant.configure(2) do |config|
config.vm.box = "ubuntu/xenial64"
config.vm.network "forwarded_port", guest: 80, host: 8080
# так и не знаю, зачем это:
config.ssh.insert_key = false
config.vm.provision "ansible" do |ansible|
ansible.verbose = "v"
ansible.playbook = "playbook.yml"
end
end
Набросал черновик playbook-а, создал заготовки ролей и запустил
vagrant up
. Не взлетело. Поскольку официальный образ xenial — только для VirtualBox, а в Fedora Linux виртуализация через libvirt. Долго вспоминал правильную команду: vagrant up --provider virtualbox
. Затем правил синтаксические ошибки в yaml (зачем там в начале обязательные три дефиса?). Помним, что после запуска коробки для перезапуска Ansible пишем vagrant provision
.И первый сюрприз: в коробке Ubuntu 16.04 нет python по умолчанию! Дикость для федоры, где пакетный менеджер написан на питоне. Ansible, как я узнал, загружает свои модули на сервер и выполняет их там. Идём на StackOverflow, находим волшебный таск (точнее, десять вариаций одного таска и непонятно, как лучше):
- name: Install python for Ansible
become: yes
raw: test -e /usr/bin/python || (apt -qy update && apt install -y python-minimal)
register: output
changed_when: output.stdout
Суперпользователь, become!
Даже с документацией и примерами многое непонятно. Не понимаю, например, почему Vagrant переопределяет
remote_user
, и как так получается, что в каждой коробке свой суперпользователь. Я же буду запускать playbook на чистом сервере, где будет только root, и нужно будет сделать своего суперпользователя. Но делать это под вагрантом нужно иначе, чем на чистом сервере, видимо. Вообще непонятно: получится два playbook, для стейджинга и для продакшена?Или вот
become
и become_user
: одно не подразумевает другого. Что из этого нужно указывать в корневом playbook, если для настройки сервера постоянно нужно включать рута? Я сначала поставил туда become: yes
и в каждом втором таске писал become_user: root
. Потом оказалось, что без become_user
тоже всё работает от рута! Потому что root — это значение по умолчанию и я, по сути, с самого начала сделал sudo -i
без возможности отпустить.Где-то тут я вспомнил, что давно не обновлял систему на своём ноутбуке, и запустил
dnf update
. Продолжая колупаться с плейбуком. Vagrant работал, а dnf в соседней вкладке обновлял VirtualBox. Кажется, так делать не нужно, потому что очередной vagrant provision
сказал: «всё сломалось и я не виноват». Ему не хватало VirtualBox, который «terminated unexpectedly during startup with exit code 1 (0x1)» — и хоть ты тресни. Команда vboxheadless -h
(я не настоящий девопс, я гуглил) показала ошибку -1912. В интернете все как один отвечают: переустанови VirtualBox. Хрен там, не помогает. Отчаявшись, нашёл коробку xenial для libvirt и перешёл на него. Хорошо, когда есть выбор.Из какого-то примера скопировал таск вызова apt с кучей параметров, а потом узнал, что
update_cache=yes
хорошо бы сделать отдельной задачей. И эта задача, вот беда, всё время возвращает «changed». Оказалось, нужно прописать cache_valid_time=3600
, чтобы проверять обновления не чаще раза в час. Сначала подумал написать 86400 (сутки), но я же не в кроне буду Ansible вызывать, а раз в месяц — пусть живёт.Развернём базу данных
Установка PostgreSQL — пять строчек в консоли или целая эпопея в Ansible. В определённый момент нужно сделать
become_user: postgres
. И тут коробка выдала странную ошибку: «Failed to set permissions on the temporary files Ansible needs to create when becoming an unprivileged user». Помните, как Ansible загружает модули на сервер и там запускает? Ну так вот, загружает он их от root или от другого суперпользователя, а потом у пользователя postgres нет к ним доступа. Вот незадача.StackOverflow снова в помощь: оказывается, есть три выхода. Один из них — сделать
ansible.cfg
и прописать внутрь pipelining=True
(а для решения какой-то другой возникшей проблемы я временно ставил pipelining=False
). Второй выход — буквально, «не делайте так». И третий самый простой: ставите пакет acl
и всё волшебным образом работает. Вернее, не работает другим способом: «sudo: a password is required». Ну что за дела, откуда здесь вообще пароли, я же с ключом захожу?Оказалось, захожу в виртуалку без ключа, пользователем vagrant. Который был сделан до нас и за нас. Ansible при
become_user
, видимо, делает sudo -u postgres
, а оно требует пароль пользователя vagrant. Пароля нет.Начинаю перебирать варианты.
become_method: su
вылетает по таймауту, потому что сервер спрашивает пароль, а Ansible этого не понимает. Что он там делает — непонятно, потому что у меня sudo su postgres
пароль не спрашивает. Есть вариант в файле /etc/sudoers.d/vagrant
прописать «vagrant ALL=(ALL) ...
», потому что слово в скобках позволит делать sudo -u
без пароля. Но тогда playbook становится заточенным под Vagrant, а мне его ещё в проде запускать. Неаккуратненько.От безысходности пробую вообще убрать
become
. Постгрес ожидаемо цедит: «Peer authentication failed for user „postgres“». Выкапываю стюардессу. Новый план: запускать роль под пользователем zverik, у которого есть все на свете права. Разбиваю playbook на два: в первом устанавливаю питон и делаю пользователя, вторым ставлю и настраиваю всё остальное с remote_user: zverik
. Запускаю. И снова «sudo: a password is required». Почему? А, ну да, Vagrant передаёт значение remote_user
и не даёт его поменять. Ну блин.Чтобы отвлечься, открыл текстовый редактор и начал писать эти заметки. К этому моменту я разбираюсь с Ansible вот уже неделю по полтора-два часа, а ещё даже базу данных в постгресе не создал. В учебниках это всё выглядит так просто… Посчитал вкладки, связанные с Ansible в фаерфоксе: 48 штук. Сорок восемь. Примерно одна шестая от общего числа.
Тут я отключил
ansible.force_remote_user
в Vagrantfile и перезапустил provision
. Ура, новая ошибка! Напоминает, что вход пользователем zverik работает только по сертификату. Но у меня же есть сертификат, и vagrant ssh -p
работает и впускает без пароля. Нагуглил решение: нужно указать путь к сертификату в ansible.cfg
. Оно не сработает по той же причине, что и remote_user
: Vagrant побеждает. На этот раз проще переопределить главную переменную: добавляем в playbook «ansible_ssh_private_key_file: "{{ lookup('env', 'HOME') }}/.ssh/id_rsa"
» и всё работает! Не очень красиво получилось, но ура!После того, как я разобрался с пользователями, написание ролей пошло как по маслу. Уже готова одна роль из шести, шестьдесят тасков. Но начать сложнее, чем кажется по учебникам.
Полезные штуки
Во время написания playbook-ов находишь или нагугливаешь много полезных мелочей. Какие-то описаны в документации, какие-то — в статьях (поищите «Ansible» на хабре). Вот несколько из них.
Для выполнения команд — только модули
command
или shell
. Последний, как пишет документация, только в крайних случаях, поэтому забудьте про перенаправление вывода и &&
. Результат всегда «changed», что плохо. Управляйте результатом либо параметром creates
(удобнее — в блоке args
, вместе с chdir
), либо register
и changed_when
. Полезно проверить условия перед выполнением: сначала рекогносцировка command + register + changed_when: False
, а затем с помощью when
проверяем сохранённый stdout на необходимость запуска команды.Чем меньше вызовов модуля
command
, тем лучше. Гуглите: почти всегда есть модуль. Например, я сначала сделал command: npm install -g {{ item }}
, а потом обнаружил, что можно npm: name={{ item }} global=yes
. Модуль всегда лучше, чем команда, потому что не нужно проверять конфигурацию и потому что результат работы будет не в строке stdout, а в удобной структуре.Файлы конфигурации почти всегда правим через
lineinfile
, который ищет строчку по регулярному выражению и заменяет на другую. Модуль blockinfile
добавляет целые блоки текста. С ним есть нюанс: если несколько тасков пишут в один файл, то нужно переопределять marker: # {mark} block name
. Иначе все будут затирать чужие блоки.Перед изменением таблиц PostgreSQL удобно проверять их состояние с помощью pg_tables. Например:
command: psql -A -t -d {{ gisdb }} -c "SELECT tableowner FROM pg_tables WHERE schemaname = 'public' AND tablename = 'spatial_ref_sys'"
Наследование — наше всё: если можно вместо двух почти одинаковых тасков написать один с условными выражениями и
with_items
, то делайте так. Группу повторяющихся тасков с похожими параметрами выносите в отдельный файл и вызывайте через include_role
с vars
. Тут ещё должно быть про параметризацию ролей, но я ещё только учусь и роль у меня одна.В одной из статей нашёл совет не переизобретать велосипед, а искать подходящие роли в каталоге Ansible Galaxy. Действительно, php-fpm и postfix ставили тысячи людей до вас, и часто найдётся хорошо написанная роль с удобными значениями по умолчанию.
С другой стороны, какой смысл качать роль
geerlingguy.apache
, когда apt: pkg=apache2
решает все мои задачи? Или, вот, нашёл роль для установки osm2pgsql из исходников, а она 2014 года и там используется устаревший sudo: yes
. То есть, я, конечно, записал roles_path = roles.galaxy:roles
в ansible.cfg
и сделал playbook для установки всех ролей, но ставить пока нечего. Вот как он выглядит:- hosts: localhost
vars:
galaxy_path: roles.galaxy
tasks:
- name: Remove old galaxy roles
file: path={{ galaxy_path }} state=absent
- name: Install Ansible Galaxy roles
local_action: command ansible-galaxy install -r requirements.yml --roles-path {{ galaxy_path }}
И в
requirements.yml
пишете строчки для каждой роли из Galaxy:- src: автор.роль
Написали playbook и он отработал в Vagrant до конца? Отлично, теперь сделайте
vagrant destroy
и создайте коробку заново. Стопроцентно обнаружите несколько косяков: забытые sudo, пропущенные mode: 0755 для исполняемых файлов, недостающие пакеты (помогают dnf provides
или apt-file
, который нужно устанавливать). Наконец, самое главное: после второго запуска vagrant provision
должно быть «changed: 0».***
Переводить серверы под систему управления конфигурациями сложно, какую бы систему вы ни выбрали. Но после начального поля граблей программирование playbook-а спорится. Главное — не забывать о цели, чтобы не перегореть: вон, сейчас у меня целевая операционка Ubuntu 16.04, а через месяц я без особых сложностей переведу сервер на 18.04. А удовольствие от полнофункционального сервера с нуля по одной команде в консоли поможет в пути.
Комментарии (52)
amarao
02.04.2018 13:45+1Да, ещё: если проект живёт и в command что-то, что не получается модулями, но нужно, то можно написать свой модуль. Модуль на питоне это лучше, чем bash + register с yaml'ом и jinja для парсинга вывода модуля.
Модули в репозитории с проектом — это нормально.Zverik Автор
02.04.2018 13:51Спасибо за советы, жду ещё :)
Насчёт модуля, кстати, у меня та же мысль была. Но я не разобрался, можно ли, и как, запихать модуль в роль. В доках их советуют куда-то в чулан файловой системы пихать отдельно. Подскажи, пожалуйста, как их хранить вместе?amarao
02.04.2018 13:53Полный список мест не знаю, я обычно в корне гита храню (в соотв. каталоге). Вот что документация говорит:
You can enable a custom action plugin by either dropping it into the action_plugins directory adjacent to your play, inside a role, or by putting it in one of the action plugin directory sources configured in ansible.cfg.
raskal
03.04.2018 01:38Создавать в роли каталог library и в него складывать модули. Вот полная дока:
docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html#embedding-modules-and-plugins-in-roles
elve
02.04.2018 13:56Статья — брюзжание. А заголовок обещал что-то новое и интересное…
Zverik Автор
02.04.2018 14:00По-моему, не совсем. Ни разу я не пишу, что Ansible или Vagrant плохие. Когда я понял, что решил много проблем, с которыми могут столкнуться другие, я решил сэкономить им часы гугления, описав, как я эти проблемы решал. Ansible мне нравится, пилю сейчас playbook со всё возрастающей скоростью.
throttle
02.04.2018 14:25+1Кому как.
Лично я во многом с автором согласен. Например, в том, что ansible не так прост, как про него рассказывают — это вот совершенно в точку. И про yaml — вообще не понимаю восторгов по поводу этого формата. Хотя это, возможно, уже вкусовщина.amarao
02.04.2018 14:32+1Читать удобно. Много строк, но разобраться в чужой плейбуке, если там выкрутасов на сайд-эффектах не делали, чаще всего очень легко. А ещё из-за того, что yaml, то очень легко получаются вот такие конструкции:
- name: Configure settings become: yes copy: dest: /etc/default/foo content: | # managed by ansible PORT={{iinternal_port}} ADDRESS={{internal_ip}}
Даже питоновские тройные кавычки не катят —
|
позволяет иметь правильное многострочное форматирование с сохранением отступов. Более того, я не знаю ни одного другого формата энкапсуляции, который бы такое позволял.throttle
02.04.2018 14:54Не знаю, меня дико выбивает синтаксис. С одной стороны, все должно быть просто, а с другой — пропустил где-то пробел после дефиса — и все рушится. И не поймешь, где. Или отступы не так поставил. В этом смысле мне куда проще понять строгий синтаксис со скобками, точказапятыми, и вот этим вот всем.
Или вот в твоем примере — строка# managed by ansible
— она ведь будет с нулевым отступом. А здесь — шесть пробелов. Неочевидно.
Или то, насколько по-разному оформляются списки и словари в однострочном и многострочном формате…
Конечно, когда ко всему этому привыкнешь — оно все понятным и логичным выглядит. Я никак не привыкну, для меня yaml-синтаксис — даже незнаю, как описать. Он не сложный, нет, он «неправильный» какой-то.amarao
02.04.2018 15:00Почему не очевидно? Табуляция отрезается по первому элементу. Если хочется с пробелами в начале, есть
|+4
и т.д.
yaml надо изучить, но когда изучишь, лучше языка (для написания данных) трудно придумать. Именно потому, что в нём есть ответы на все вопросы.
Насчёт опечаток — есть линтеры для yaml'а и ансибла, они ошибки подсвечивают.
throttle
02.04.2018 15:19Мы об одном и том же, но разными словами. :)
Я в целом про то, что:
процесс не smooth, и что там постоянно кулибинство
amarao
02.04.2018 15:22+1Ну вот я yaml-то это не надо гнать. У yaml всё ровненько и хорошо. Выучил возможности (их конечное количество и они не меняются) и ладушки.
Вот у ansible — что ни версия, то революция. То им loop вместо with_ захотелось, то им with надо с кавычками (а раньше без), то у них теперь dynamic includes которые глючат и бибикают и перезаписывают default'ами роли set_facts, и т.д.
К ансиблу тут претензий куча. Но yaml за что гнобить-то?throttle
02.04.2018 15:29Дак я и не гноблю. Сразу сказал, что это он для меня неудобен, а не вообще неудобен. Допускаю, что со временем пройдет. В равной степени допускаю, что есть другие такие, кому он неудобен (на начальных этапах, или вообще).
Насчёт опечаток — есть линтеры для yaml'а и ансибла, они ошибки подсвечивают.
Посоветуй, чем сам пользуешься?
Zverik Автор
02.04.2018 15:29Кстати да, я захотел проитерироваться по файлам, захожу в доку — а там какие-то
loop
вместоwith_*
. Оказалось, между 2.4 и 2.5 все циклы переделали, документацию переписали с нуля. Я немного в шоке от таких «минорных» изменений.amarao
02.04.2018 15:522.5 — мажорный релиз, а у ансибла довольно крутые изменения каждый мажорный релиз, так что нужно использовать выбранную версию. А между ними миграция класса «c postgres 9.3 на 10». Кстати, такая же фигня и у остальных систем — рядом отдел планирует переезд на новый chef уже пол-года.
Я планирую миграцию на 2.5 после следующего минорного релиза. Пока только changelog глянул и всё.
amarao
02.04.2018 14:31Это не брюзжание. Это непрерывное ощущение, что процесс не smooth, и что там постоянно кулибинство. К сожалению, более адекватной системы нет.
vkflare
02.04.2018 14:37+1Зашел почитать, как страшен Ansible, а прочел, как ужасен Vagrant + Virtualbox. Ну да ладно.
Группу повторяющихся тасков с похожими параметрами выносите в отдельный файл и вызывайте через include_role
Потенциально очень багоемкая директива, рекомендую во всех возможных случаях юзать import_* или вообще объявлять роли списком в теле плейбука (1.4-стайл). Разница между статической и динамической загрузкой тут: docs.ansible.com/ansible/2.4/playbooks_reuse_includes.html
сделал playbook для установки всех ролей
А зачем?Zverik Автор
02.04.2018 14:41Роли я просто перечисляю, внутри них пару раз использовал `include_tasks` вместо `import_tasks`, потому что решил, что переменные во втором случае не передадутся. Это, конечно, развенчивает документация, спасибо.
Playbook для Galaxy — чтобы не вспоминать параметры команды. Я его почти целиком скопировал из какой-то статьи типа «пять полезных штук для удобства».
shoomyst
02.04.2018 14:48У ансибла в принципе нормальная документация для старта. Если делать всё последовательно, а не скакать по разделам. Сначала с adhoc поиграться локально, потом можно к плейбукам переходить на удаленных машинах. Но это если поставить задачу разобраться в инструменте, а не сделать всё за час.
Самая большая проблема у них с багами. Их очень много. Достаточно посмотреть issues на гитхабе. Скорее всего это связано с очень активной разработкой, они так активно пилят фичи, что просто не в силах всё контролировать и тестировать. Ну и правила версионирования и обратная совместимость у них очень странная. Постоянно что-то меняют, короче со стабильностью у них есть проблемы и очень большие.
Для простых статических плейбуков всё обычно работает, но стоит чуть усложнить — подключить переменные, динамические загрузки тасков и ролей, и можно получить очень много сюрпризов. Из недавнего вспоминаю баг, когда в роли переменные не передавались. Я всё так красивенько на ролях сделал, и тут такой облом :) Я был в таком шоке, что запилил себе свою собственную систему «ролей», используяinclude_tasks
. Сейчас они роли уже пофиксили, но я пока их использую очень осторожно.
Пока у меня еще остались сложности с переменными в ансибле. Я пока не до конца понимаю механизм их работы, а именно области видимости. Как-то они начинают рекурсивно раскручиваться в момент использования в таске похоже. И тут можно получить совсем неожиданные результаты, т.к. ты думаешь, что ты уже присвоил где-то значение переменной, а оно каким-то образом заменяется в другом месте)) Одним словом, на императивность рассчитывать особо не стоит. Ну и разные приоритеты set_fact, include_vars и прочее. В общем, с переменными надо быть настороже.
HeOS
02.04.2018 18:45+1И первый сюрприз: в коробке Ubuntu 16.04 нет python по умолчанию!
Если речь идет именно об Ubuntu 16.04, то там там уже python3 по умолчанию. В самом ansible это можно поправить, например, указав у нужного хоста переменную вот таким образом:
hostname01 ansible_host=192.168.1.10 ansible_python_interpreter=/usr/bin/python3
Сколько использую, пока не встретил проблем с этим.bfuvx
03.04.2018 09:28+1Сколько использую, пока не встретил проблем с этим.
И все же, если это не какая-то исключительная ситуация, лучше пока устанавливать python 2.x, как и сделал автор поста.
Из faq последней версии ansible:
Python 3 support is being worked on but some Ansible modules are not yet ported to run under Python 3.0. This is not a problem though as you can just install Python 2 also on a managed host.
И в документации поддержка python 3.x все еще помечена как technology preview feature.HeOS
03.04.2018 11:34Да, в случае с 1-2 серверами этот подход окей. Но когда их уже становится даже больше 10, то идти на них и ставить python2.7 становится утомительно. :)
bfuvx
03.04.2018 16:08Так а зачем на них идти? Речь идет об использовании модуля raw для изначальной инсталляции python 2.x, если отсутствует /usr/bin/python. Т.е. добавляется дополнительный task в «bootstrap» playbook.
AlexGluck
03.04.2018 03:08Спасибо полистал статью и комменты. Как всегда в комментах есть, что-то интересное.
Для тех кто захочет на opensuse 42.3 minimal заюзать ансибл, из коробки не будет пакета python-xml. К чему это приведёт, ну модули package и zypper работать не будут. Догадаетесь, какие есть варианты установки пакета? Всё тот же command или shell.
Но самое вкусное, это когда вы захотите подключить репозитории внешние, например nginx или zabbix: во время попытки установить нджинкс из его стейбл или мейнлайн репы, вместо репы ОС, у вас ничего не выйдет без тех же command или shell. А если вы обновляете заббикс с 2.2 из реп ОС до 3.4 то опять же работать можно только через command и shell. Собственно вопрос, будете ли вы пилить модуль 2-10 дней для фикса багов или вставите костыль за 30 минут… Проблемы есть конечно всегда, но со всеми suse линуксами ансибл придётся костылять в 10 раз больше, чаще дешевле переехать на другой дистр. Из всех здесь описанных багов ансибл я не встретил ничего кроме багов в zypper модуле.
Kylin
03.04.2018 14:20+2по поводу синтaксиса yaml три дефиса в начале как shebang в баше и питоне, вы же не будете писать скрипт не выставив shebang?
по поводу правки конфигурационных файлов, lineinfile конечно годен, но только при одном изменении в файле, про blockinfile никогда не слышал и даже страшно стало что вы там с ним за хаки делаете: несколько тасков да в один файл. В ansible есть templates, именно их надо использовать для правки конфигурационных файлов. Это один из плюсов использования roles, одна role, если это конечно хорошо написаная role, а не говно мамонта 2014 года :) обычно выполняет не только установку пакета, но и устанавливает в нужные места templates, которые содержат variables, которые в свою очередь можно выставлять на уровне playbook'a
TrogWarZ
04.04.2018 01:29Спасибо за пост и особенно за комментарии!
Те же самые ощущения помешали мне в своё время всё автоматизировать с помощью ansible/vagrant. Брал даже готовые сборки с надеждой поменять какие-то мелочи (puphpet внезапно потребовал меньше всего напильника).
В конечном итоге, за неделю сделал три варианта деплоя (на разных стеках), в каждом из которых были мелкие, но очень неприятные проблемы, требующие какого-нибудь простого костыля.
Но это было два года назад. Как сейчас обстоят дела с готовыми «тематическими» сборками? Что-то вроде уже настроенных разбирающимися людьми ubuntu/nginx/php-fpm-7x/symfony/pgsql/xdebug/metrics/...? Для использования в простых проектах.Zverik Автор
04.04.2018 10:48Сейчас, я так понимаю, достаточно сделать playbook из готовых ролей Ansible Galaxy для всего этого, и базовый сервер будет настроен. Там даже есть всякие lamp.
Ну и описанное не помешало: я уже автоматизировал примерно две трети одного сервера, сейчас затык с собственными криво написанными скриптами, нужно будет переписать для порядка.
amarao
Ух. Много. Я попробую ответить на некоторые вопросы, но в целом хочу сказать, что все приключения с vagrant'ом связаны с тем, что vagrant — ужас. Не используйте его, у него слишком много полиси. Для начала освоения — поднимите стейджинг руками, настройте начального пользователя и юзайте его. Автоматизация того, что не понимаешь — это боль и отчаяние, потому что от тебя скрывают ошибки нижележащего уровня.
По сказанному: update_cache надо делать только в качестве параметра для установки пакетов. Отдельной таской — глупо (и не понятно, оно changed или нет, и даже если файлы поменялись, то нас это касается или нет?). Если очень хотите, то можно в эту таску написать changed_when: False, но я обычно делаю вот так:
При этом у меня есть playbook'а для update (которая устанавливает обновления софта) — она переопределяет дефолтное apt_cache_valid_time в 1. Фигня с retries — баг в lxc-транспорте, который я юзаю на локальной машине (он падает если в выводе больше 64кб текста).
Become для не-рута и sudo:
1) Пакет acl — на все хосты обязательно. Вся фигня возникла из-за security-дыры на многопользовательских машинах, которую решили залатать, даже если она не кусает однопользовательские.
2) все атрибуты become/become_user наследуются от уровня выше. become на tasklist (рядом с hosts) наследуется на все такски. Так же как и become_user. Моё собственное правило — минимизировать become, т.е. вешать его только на таски внутри ролей.
Насчёт начального появления питона на хосте.
Вот мой bootstrap.yaml, позволяющий настроить python и т.д. даже если хост был переустановлен:
В этом скрипте много боли и хаков, но он (почти) работает (предполагается, что хосты resolvable). Иногда на ребутах отваливается чуток (после того, как всё сделал), планирую поправить.
Остальное попробую прокомментировать чуть позже.
shoomyst
Первый код должен упасть, если packages не определен.
Проверка
when: packages is defined
не поможет. Как минимум: заменитьwith_items
на"{{ packages|default([]) }}"
amarao
Неа. is defined проверяет на то, определено или нет. Если нет, то блок skipped.
ansible-playbook -i localhost, 1.yaml
shoomyst
Хм, странно. When проверяется не до выполнения цикла, а для каждый итерации. А цикл по undefined foo должен завершиться ошибкой. Возможно у них есть какая-то проверка для таких случаев.
amarao
Я думаю, это результат ленивых вычислений jinja2. item — это же foo|next, правильно?
shoomyst
Такой упадет. Мне кажется в первом варианте просто какой-то частный случай, который явно проверяется в ансибле, у них куча таких хаков в коде. Но спорить не буду, я эту часть не особо разбирал, надо будет посмотреть.
amarao
is not string
очевидно упадёт и без всякогоwith_items
, потому что чтобы сделатьtype(foo)
нужно иметьfoo
определённым.shoomyst
Без
with_items
не упадет, будет skipped. Только с циклом падаетamarao
ansible-playbook -i localhost, 1.yaml
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'foo' is undefined\n\nThe error appears to have been in '/tmp/1.yaml': line 5, column 6, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n — debug: msg=\"{{foo}}\"\n ^ here\nWe could be wrong, but this one looks like it might be an issue with\nmissing quotes. Always quote template expression brackets when they\nstart a value. For instance:\n\n with_items:\n — {{ foo }}\n\nShould be written as:\n\n with_items:\n — \"{{ foo }}\"\n\nexception type: <class 'ansible.errors.AnsibleUndefinedVariable'>\nexception: 'foo' is undefined"}
ЧЯДНТ?
Мораль: is not string не катит для несуществующих переменных.
shoomyst
Не делаете
set_fact
:)Если мы делаем
foo: "Hello"
, тоwhen: foo is not string
будет False, а значит по идее блок debug должен быть пропущен, и это так, но только если нет цикла. Если же цикл есть, то блок валится с ошибкой.Короче, что я хочу сказать: when + loop действует не так, как многие предпогалают, поэтому тут надо быть внимательнее
shoomyst
У нас получается 2 случая:
В обоих случаях выражение в when равно False, но при этом первый работает, а второй валится
shoomyst
Не, мой косяк. С
with_items
работает и второй, но падает сloop
. Хм :)amarao
В моих тестах ваш второй пролетал успешно как skipped, но если убрать set_fact, падал. is defined видимо, специально обрабатывают.
shoomyst
Да, я тупанул :(
Уже на
loop
перешел, думал он сейчас аналогичноwith_items
работаетZverik Автор
Окей, после очередного глюка вагранта, когда после серии halt/up/resume всё равно не удаётся войти по ssh и ошибок нет, я готов сменить его на что-нибудь более вменяемое.
1) Что значит «поднять стейджинг руками» — оплатить дроплет и поднимать на нём? Но написание плейбука занимает несколько недель, не очень хочется тратить деньги, когда есть бесплатная, пусть и чуть более глючная альтернатива.
2) Насчёт таска apt: ты его держишь в отдельном файле и импортишь каждый раз, когда нужно что-то установить? По одному на роль, или где-то рядом с корнем? Как это вообще работает?
3) «update_cache надо делать только в качестве параметра для установки пакетов» — но при этом ты его вызываешь в конце bootstrap.yaml. Почему?
Ещё, 4) при установке munin нужно делать
munin-node-configure --sh | sh
. Можно ли обойтись без модуля shell?И 5) где обычно хранят большие файлы, необходимые для настройки — дампы БД, бинарники, файлопомойки? Распихивают по облакам, или есть какой-то общий рецепт? Не в репу же их загружать.
raskal
Давайте я вам отвечу, если не возражаете.
1. Правильно иметь тестовую машину такой же, как машину, на которую будете устанавливать роль. По этой причине плохо подходит вагрант — многовато отличий. Если у вас linux — поставьте один раз ubuntu в kvm и потом просто делайте с нее копии/снапшоты и на них тестируйте. Если у вас мак — один раз поставьте руками virtualbox и настройте в нем машину с сетью, потом копируйте ее и тестируйте на копиях. Это, конечно, не так просто, как сказать vagrant up, но зато вы не будете потом иметь неясного происхождения геморрой с запуском плейбуков.
2. Это зависит от того, что и как вам нравится устанавливать. Можно в pre_tasks его держать, можно в отдельном файле и импортировать.
3. Потому что до этого apt вызывается только если нужно устанавливать python и при этом явно зовется apt update. Его и не нужно раньше звать просто.
4. Можно не вызывать --sh, а зафиксировать версию munin, которую ставите и сделать все ln -sf, которые он вызывает, через модуль file. Это правильнее, потому что избежите ненужных changed, да и вообще неопределенного поведения, если вдруг версия munin обновится внезапно.
5. Дампы БД очень изменчивы. Обычно они нужны, если вы мигрируете с одного сервера на другой. На практике это довольно редкое занятие и я, например, делаю такие вещи руками — настолько нечасто это нужно делать. Бинарники — тут кто во что горазд. Для Go становится модно тянуть собранный бинарник с гитхаба, например. Мне этот подход не очень нравится, но это лучше, чем положить блоб в репу. В идеале, конечно, бинарь должен быть упакован в пакет под нужную ОС, но это в идеализированном мире девопсов с понями так происходит, а на практике делают костыли.
amarao
Я с некоторыми мелкими глюками использую LXC для быстрой разработки (если использовать LXC как виртуалку, т.е. по SSH, то глюков нет, я говорю про lxc-transport, который позволяет работать с контейнерами без ssh). В принципе, можно поднять любую виртуализацию руками — тот же вагрант вам всего лишь фигачит виртуалки в virtualbox'е. Если у вас локальный линукс — то lxc или libvirt + kvm (virtualbox — ужасен). На других платформах выбирайте сами, но идея состоит в том, что не надо пытаться автоматизировать всё сразу. Поднимите виртуалки старыми методами (install from ISO), работайте с ними как с серверами.
Насчёт стейджинга — обычно у людей есть доступ к вычислительным рерсурсам работодателя. Если нет, то неприятно, да. Хотя я когда xen отлаживал, то я просто старый компьютер юзал для этого.
2) apt слишком простой, чтобы делать его playbook'ой или ролью. У меня есть роль apt_install, которая вызывается из common.yaml и ставит туда стандартный набор пакетов, но это ерунда и не надо об этом заморачиваться. Таска и таска.
3) Я вызываю не update_cache, а dist=true, что означает "сделай мне apt-get update && apt-get dist-upgrade" — ставит все апдейты на сервер в процессе "bootstrap".
4)
munin-node-configure --sh | sh
— ужасная строчка. В таком виде что shell, что модуль — всё равно не ясно будет что там происходит внутри. Я бы попробовал расковырять и сделать руками (всё что там делается), но если трудно — обычного shell будет достаточно.5) Вопрос хранения больших файлов — очень, очень, очень обширный топик. Вариантов много: приватный репозиторий, swift (cloud), иногда — submodule в git'е. В репе бинарники лучше не хранить. Какие-то мелкие скрипты — может быть, блобы — однозначно нет.
Но у компании должен быть workflow для работы с блобами. Это большая задача и её нельзя "обойти".