DOU Labs: как в EVO разработали Vagga и контейнеризировали всё

В рубрике DOU Labs мы приглашаем IT-компании делиться опытом собственных интересных разработок и внутренних технологических инициатив. Вопросы и заявки на участие присылайте на [email protected].

Эта история о том, как мы в EVO разрабатывали Vagga — инструмент для управления зависимостями, сборки контейнеров и менеджмента процессов на машинах разработчиков. Мы стремились стандартизировать рабочее окружение и навсегда избавить коллег от отслеживания зависимостей вручную, а также подготовить почву для микросервисов. В некоторой степени нам это удалось — мы сделали Vagga, и это полностью open source проект.

Когда я присоединился к prom.ua (теперь — группа компаний EVO) в январе 2014 года, Docker’у еще не было даже года от роду. Тем не менее, уже на собеседовании мы говорили о том, чтобы попробовать его в компании. Смешно вспоминать, но тогда я отозвался о технологии как о чем-то дико новом и магическом и даже высказал свои опасения по этому поводу. За последующий год я узнал о контейнерах так много, что мы написали свою систему контейнеризации. Ниже — обо всем по порядку.

Мотивация

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

Первая — простая и банальная — у нас было около 100 зависимостей. Некоторые из них были настолько старые, что их не было на PyPI (Python Package Index). Кроме того, чтобы собрать Javascript и CSS файлы, нужно было установить Ruby и Node.js. В общем, разворачивание окружения на новой машине занимало кучу времени.

Вторая причина — размер команды. В 2014 году это была компания, которая делает один проект. 35 человек, которые ежедневно вносят правки в prom.ua. Для одного проекта это уже довольно большая команда, и мы были готовы расти еще больше. Как вы думаете, как часто в такой команде возникают проблемы, вроде «works on my machine»? Насколько повсеместно находят баги, связанные с неправильными версиями зависимостей? Сколько времени занимает выяснение, что зависимость нужно было обновить, но этот конкретный разработчик этого не сделал?

Конечно же, мы ухватились за Docker, как за спасательный круг. Первое, что мы выяснили, — использовать docker напрямую практически не возможно. Например, вот как можно запустить какой-нибудь проект для работы:

docker run -i -t --rm --volume=$(pwd):/work --workdir=/work my-app:v1.2.3 my command

Ну и это безумие нужно вводить каждый раз. Поэтому, первый вариант контейнеризации был набором shell-alias’ов поверх докера, чтобы упростить запуск команд.

Зарождение Vagga

Очень быстро стало понятно, что мы хотим не только избавиться от длинных docker’овых команд, но и упростить и документировать наши внутренние команды. Вот некоторые примеры того, что мы часто запускаем:

paster serve development-my.ini
paster celery development-my.ini worker
make test test_db_name=my_database
webpack --progress --colors --watch

Первое, что мы хотели — это уменьшить порог входа, чтобы команды, описанные выше, выглядели вот так:

Vagga run
Vagga celery-worker
Vagga test
Vagga make

Чаще всего это делают с помощью команды «make». Однако, «make» — это инструмент для сборки, а значит, вы не получите красивого списка команд с описаниями:

$ Vagga
Available commands:
    run                 Run application
    celery-worker       Run celery worker
    build-static        Build static files (JS, CSS, sprites)
    test                Run short set of unit and functional tests

Проблема зависимостей

Но это был лишь первый шаг. Вторая проблема заключается в добавлении зависимости в проект. Старый подход состоит из множества шагов:
1) Добавляем пакет в requirements.txt;
2) Пишем всем разработчикам на почту, чтобы обновились;
3) Уведомляем админов, чтобы установили его на всех серверах.

Ну и ждём, пока самые ленивые увидят ошибку, отвлекут от дел всю команду, чтобы помогли выяснить, в чем проблема, и потом услышат негодование: «Ну так письмо же было! Видел?»

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

Как это починить? Vagga смотрит на «requirements.txt» (ну или «package.json», «Gemfile» и т.д.) и создаёт новую версию контейнера. Версия контейнера — это хеш от списка зависимостей.

Это означает, что если вы локально поменяли «requirements.txt» — контейнер пересоберётся. Если вы сделали «git push», а ваш коллега сделал «git pull», то у него соберётся точно такой же контейнер. Если вы переключились в ветку — вы получите те зависимости, на которые эта ветка рассчитывает. И если ваш CI (Continuous Integration) запускает тесты, то он тоже проверит их на правильных зависимостях.

Микросервисы

Я пришел в компанию EVO из небольшого игрового стартапа. Мы там всё делали, используя микросервисы. Для этого у нас был свой менеджер процессов. Его логику мы и реализвали в Vagga. Когда вы делаете сервис, вы тестируете его в окружении других сервисов. Чтобы не терять время, нам часто нужно запускать много сервисов одновременно. Быстро. Легко. Одной командой. И также быстро и легко их останавливать. Насовсем. Не беспокоясь, что какой-то из них останется работать где-то в фоне. Старый. Запущенный 3 дня назад. Вы же знаете о чём я говорю, правда?

Чтобы не терять время на отладку, нужно также быстро понимать, что один из сервисов упал. Vagga по-умолчанию поступает очень просто — убивает при этом все процессы, которые запустила. Это самый простой и надёжный способ показать, что что-то не так. В наших играх это происходило довольно часто — запускаешь проект, проходишь уровень-другой, потом замечаешь, что один из неинтерактивных (то есть не заметных сразу, но тем не менее важных для данного теста) сервисов не поднялся.

В web это заметно меньше и реже. Но тем не менее, мы начали запускать postgres и redis правильной версии. Мы включаем полнотекстовые индексы с помощью elasticsearch. Мы постепенно включаем все зависимости, которые раньше не запускали вместе с приложением, в главную команду запуска. Это удобно.

Где же суперсила?

В каждом проекте у нас есть, как правило, три команды:

vagga run
vagga test
vagga doc

Это значит, что любой — от программиста до тестировщика или менеджера проекта — может запустить проект. И это значит, что когда к вам приходит новый сотрудник, он моментально может приступить к работе:
— Он запустит «Vagga run» и зайдёт в браузере по ссылке, которую отпечатает эта команда;
— Он пойдёт в документацию и сразу же сможет починить в ней неясности, потому что знает, куда писать и как её потом собрать;
— Он посмотрит на команды, которые запускает «Vagga run», и увидит, какие конфиги там прописаны, какие процессы запускаются, какие базы данных используются;
— Он попробует что-то поправить и увидит результат, так как «Vagga run», как правило, включает в себя настроенный live-reload;
— Он запустит просто «Vagga», посмотрит, что там есть, ну например: запуск celery-задач, миграции базы данных, разные режимы запуска приложения, нагрузочные тесты, ну и, в конце концов, даже команда для выкатки проекта на боевые сервера (но без необходимых ключей, как вы можете догадаться);
— Он уже знает, как запустить тесты и как добавить еще один тест (просто посмотрев в определение команды «Vagga test»);
— Он уже знает, как добавить в проект зависимость так, чтобы не испортить никому
другому рабочий день

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

Итоги

Разработка Vagga была непростой. Мы столкнулись с самыми разными проблемами, начиная от хитростей всевозможных менеджеров пакетов до тонкостей
настройки linux-дистрибутивов и багов ядра linux. Но сложнее всего оказалось донести до пользователей необходимость поменять их устои. Большинство программистов считают, что проблемы с обновлением и установкой зависимостей у них достаточно редки, чтобы изучать какой-то новый инструмент. Так мыслят и внутри компании, и такие же отзывы мы слышали, когда говорили об этом на конференциях.

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

Vagga — это open source проект. Мы хотим, чтобы его попробовали и вы. Надеюсь, он и вас избавит от головной боли и позволит вам делать ваш продукт лучше.

Все про українське ІТ в телеграмі — підписуйтеся на канал DOU

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному0
LinkedIn



41 коментар

Підписатись на коментаріВідписатись від коментарів Коментарі можуть залишати тільки користувачі з підтвердженими акаунтами.

Хотел задать вопрос по поводу работы в macOS, и нашёл какой-то враппер vagga-docker, который юзает docker for mac. Насколько вообще там всё хорошо, плохо, сложно в этой области?

Да есть vagga-docker, но docker for mac пока очень глючный. Главный глюк обещали починить прям сегодня и выкатить в beta27. Может будет лучше. + там медленная файловая система которая смотрит в хост.

Сейчас более юзабельная обёртка вокруг virtualbox.

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

О еще забыл сказать что vagga-box умеет по nfs расшаривать файловую систему контейнеров. Так что ты можешь посмотреть содержимое контейнера, и добавить его в свой IDE. (Ну и даже поредактировать в экстренных случаях).

В linux’е это тоже работает если что (и без nfs).

Не нашел преимуществ перед docker-compose, кроме:
Build container and run program with single command, right after git pull
Automatically rebuild container if project dependencies change

Зачем вообще билдить контейнеры на машине у девелопера? Обычно этим занимается CI, который уже и пушит в registry/hub. Аналогично, для разработки достаточно спуллить контейнеры с docker registry и запускать, а разрабатываемый сервис, запускать отдельно.
Единственное что приходит в голову, так это сервисы с нативными зависимостями (например caffe или tensorflow) их таки придеться локально билдить и ребилдить и еще и маунтить исходники на volume.

Зачем вообще билдить контейнеры на машине у девелопера? Обычно этим занимается CI, который уже и пушит в registry/hub.

Так и есть, я имею ввиду с vagga’ой то же. Но дьявол как всегда в деталях:

  1. Когда вы что-то поправляете в зависимостях сами, или делаете merge локально, вам нужно чтобы контейнер был правильный
  2. Опять же, как узнать когда делать docker pull после git pull ?
  3. Каким тегом помечать имидж? (понятно же почему latest плохо?)

Ну и еще я комментировал немножно тут

Чи може Vagga вирішити проблему, коли скажімо треба мати певну версію JavaEE Application Server, але з доданими/заміненими окремими бібліотеками? Наприклад, JBoss з доданим драйвером PostgreSQL. Можна такий кейс описати в файлі з залежностями?

Да, конечно. У нас есть некоторые удобства для python, node.js, ruby, php, пакетов ubuntu и alpine. Но в целом всё что вы можете установить командами shell’а также работает. Если нужно версионировать дополнительные файлы есть тег !Depends filename. (Суть поддержки языков программирования сводится к тому что мы умеем версионировать package.json и requirements.txt не как текст, а немножно более умно, и автоматически конфигурим папки для кеша).

Воно, як docker, тільки прокидає порти до контейнеру? Чи можна в ньому запустити ucarp?

По умолчанию оно как docker --net=host. Т.е. вся сеть что работает в хостовой системе работает в vagga нормально. Только управлять сетевыми интерфейсами нельзя из-за прав.

С другой стороны не понятно зачем вам ucarp на рабочей станции или CI. Запускать vagga в production’е крайне не рекомендуется.

docker run -i -t —rm —volume=$(pwd):/work —workdir=/work my-app:v1.2.3 my command
Ну и это безумие нужно вводить каждый раз.

А чем вас docker-compose не устроил?

Ну честный ответ такой: когда мы сделали vagga docker-compose еще и в планах не было. С тех пор я не пробовал docker-compose.

Было бы хорошо если бы кто-то, кто часто использует docker-compose попробовал vagga и отписался какая в этом разница. Ну или я бы позадавал вопросы человеку кто пользуется docker-compose регулярно, здесь в комментариях, потому в каких-то штуках про него я могу ошибиться.

Из того что навскидку: вот есть у вас проект с тестами и документацией. Что должен запускать docker-compose up, проект же, верно? Как запустить тесты? Как собрать документацию? Это отдельные docker-compose файлы? Но как минимум контейнеры для тестов зависят от основных контейнеров, верно? Как это записать в docker-compose?

Еще вопрос, нужно ли запускать docker-compose build после каждого git pull?

И еще в vagga есть много фичей из rocker’а.

Ну честный ответ такой: когда мы сделали vagga docker-compose еще и в планах не было. С тех пор я не пробовал docker-compose.
в этом — беда всех хипстерских приблуд. Кроме вас ещё 100500 команд написали свои похожие тулзы, но в итоге только одна-две из них станет действительно популярными. Например, тот же Rocker

Ну в данном случае, я удивлён, тому что этих инструментов так мало, а не тому что есть чуть-чуть конкуренции :)

Но Ваш комментарий был бы интереснее, если бы Вы привели аргументы почему Вам кажется что rocker будет популярный. Какие-то примеры как задачи решаются и там и там, и объясили бы почему какой-то из способов лучше.

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

Ну честный ответ такой: когда мы сделали vagga docker-compose еще и в планах не было
хмм, может я что то упускаю, но первый commit в compose был сделан Mon Dec 9 11:41:05 2013, у вас первый commit датируется Sat Jun 28 02:00:24 2014. Может у меня с математикой плохо конечно, но docker compose уже давно был в разработке ;)

Тогда она [называлась Fig](github.com/...er/compose/commit/2af7693) не была «под крылом» у Docker’а, и в целом не понятно было взлетит оно или нет (если лень открывать ссылку, Fig стал compose’ом 12 января 2015, а анонсирован compose был на пару месяцев раньше, если мне не изменяет память)

Я нашёл Fig уже после того как начал писать vagga (хотя и сильно раньше чем она стала называться docker-compose). И впринципе он сильно отличался (и до сих пор отличается) от того что мне удобно, по-этому делать что-то поверх не представлялось чем-то полезным.

Використовуємо конейнери тільки для деплойменту. Всі девелопери просто запускають сервіси в себе локально при необхідності приєднуючись до хмари. Поки що жодних проблем.

Як на мене докер на дев-машинах — непотрібний оверхед.

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

./gradlew bootRun

На комп’ютері має бути встановлена JDK. Це все.
Для пістонщиків походу pip install requirements і python app:app, щось таке. Але в нас дуже мало сервісів на пітоні.

В нас напевне більша гранулярність та ізольованість мікросервісів тому поки що проблем немає.

Ну с java’ой и правда может быть чуть легче, хотя вот еще саму java’у нужно установить, и она тоже в разных дистрибутивах разная.

Проблема pip install requirements в том, что там еще есть нативные библиотеки очень часто, и это пакеты, и они для ubuntu и archlinux (например) разные. А еще там обычно nodejs/npm (ну чтобы javascript/css/картинки собрать), и у неё тоже бывают бинарные зависимости.

А еще как узнать нужно ли запускать pip install requirements.txt после git pull ?

и они для ubuntu и archlinux (например) разные
Ну, в нас таких проблем немає.
А еще как узнать нужно ли запускать pip install requirements.txt после git pull ?
Мені ідея підсвічує що пакетів немає :)

В цілому рішення ваше напевне ок для python-розробки, сперечатися не буду. Але сама по собі python-розробка коли є оверхед на конфліктуючі версії натівних ліб це оверхед :)

Ну, в нас таких проблем немає.
А можно подробнее? Просто интересно. У вас у всех разработчиков один дистрибутив линукса? Нет Mac OS? Нет старых версий дистрибутива? Или всё в виртуалках?
Мені ідея підсвічує що пакетів немає :)

Вот это важное заблуждение. Вот взял Bob обновил пакет pysay==1.2.1 в пакет pysay=1.2.3. Обновление пакета чинит баг. А потом вот Alice делает себе git pull запускает проект, и видит что баг есть. Её действия? Чаще всего она либо создает/возвращает баг в багтрекере, или отвлекает Bob’а чтобы он посмотрел почему на её машине баг не воспроизводится.

Але сама по собі python-розробка коли є оверхед на конфліктуючі версії натівних ліб це оверхед :)

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

Опять же проблема не в конфликтах версий в проекте, проблема в том, что у каждого разработчика своё понимание ОС на которой ему комфортно работать. У кого-то это до сих пор Ubuntu 14.04, у кого-то Ubuntu 16.10. В компании есть еще ArchLinux, NixOS, и отдельным особняком стоит OS X которой мы тоже стараемся сделать практически seamless поддержку. И вот собственно версии libpg или libxml у всех этих дистрибутивов разные.

А можно подробнее? Просто интересно. У вас у всех разработчиков один дистрибутив линукса?
У всіх OS X. Дехто дома на убунтах девелопить
Нет Mac OS? Нет старых версий дистрибутива? Или всё в виртуалках?

Всі аплікейшени запускаються в контейнерах. Конейнери визначають самі розробники.
Не знаю чи були в них проблеми з сумісністю натівних ліб, здається не було.

Вот это важное заблуждение.
Я pythonом мало займаюсь, в нас таких проблем з пакетами не було. Напевне тому що кодбейз не такий великий як в промі.

А якщо треба встановили дві конфліктуючі версії?

Не було таких кейсів. В світі jvm таке дуже-дуже рідко трапляється.

А virtualenv вам не допомагає?

В світі jvm таке дуже-дуже рідко трапляється.

Я оцінив жарт, дякую.

А virtualenv вам не допомагає?

Пітоновий? Для CARP? Ще краще.

Я оцінив жарт, дякую.
10 років з java працюю, не було такого. Напевне, не там працюю :)
Пітоновий? Для CARP? Ще краще.
В пітоні не дуже шарю. Напевне там це дійсно складно і докер має зміст.
10 років з java працюю, не було такого. Напевне, не там працюю :)

Саме так.

В пітоні не дуже шарю. Напевне там це дійсно складно і докер має зміст.

Ще раз: мені тут не треба нічого робити з Пітоном.

Ще раз: мені тут не треба нічого робити з Пітоном.
Сорі, думав ви з Prom.ua, там майже все на пітоні.
Як на мене докер на дев-машинах — непотрібний оверхед.
WAT???
вообще-то именно для этого его и придумали

Итить-колотить, техническая статья на доу!

Эх, жалко что так и не попал к вам:(

Я вроде всю статью прочел, но не совсем понял, какая история у Vagga после того, как код покидает компы разработчиков. Никакой, это только для облегчения разработки? Если так — что тогда вы для деплоя используете? Докеры, рокеты, кубернеты? Или по старинке?

Lithos для контейнеров, verwalter для оркестрации.

vagga для продакшина собирает контейнер и «придумывает» версию. vagga только локально и в CI (gitlab). На проде сборка контейнеров, версионирование, и т.д. не нужны, по-этому там другой инструмент, более простой и более надёжный.

На проде сборка контейнеров, версионирование, и т.д. не нужны, по-этому там другой инструмент, более простой и более надёжный
а что за инструмент? Если это не коммерческая тайна конечно
а что за инструмент? Если это не коммерческая тайна конечно
Так там же написано выше: lithos
Однако, «make» — это инструмент для сборки, а значит, вы не получите красивого списка команд с описаниями:

Ну вообще-то это очень легко сделать, просто написав первым блоком команду с названием `help`, например:

> make
Use `make <target>` with one of targets:
  test       run all tests
  quick      run quick tests
  devserver  run flask development server
  shell      run ipython shell

Не забыть .PHONY на него :) а так — да, 200% рабочее.

Ну конечно можно сделать. Но при добавлении команды, нужно не забыть её вписать еще и в help и в .PHONY. В vagga это в одном месте. И команда есть в списке даже если описание забыли. Но описание есть в большинстве соседних команд, по-этому, вроде, обычно пишут. Конечто же только ради этого я бы не менял make на самописный инструмент, но это тоже важно.

Ну да, если ты уже делаешь самописный инструмент, то конечно в нём стоит сделать хороший хелп.

Підписатись на коментарі