- PVSM.RU - https://www.pvsm.ru -
Мы развивали DevOps как могли. Нас было 8 человек, и Вася был самым крутым по Windows. Внезапно Вася ушел, а у меня появилась задача вывести новый проект, который поставляет Windows-разработка. Когда я высыпал на стол весь стек Windows-разработки, то понял, что ситуация — боль…
Так начинается история Александра Синчинова на DevOpsConf [1]. Когда из компании ушел ведущий специалист по Windows, Александр задался вопросом, что теперь делать. Переходить на Linux, конечно же! Александр расскажет, как ему удалось создать прецедент и перевести часть Windows разработки на Linux на примере реализованного проекта на 100 000 конечных пользователей.
Как легко и непринужденно доставлять проект в RPM, используя TFS, Puppet, Linux .NET core? Как поддерживать версионирование БД проекта, если разработка впервые слышит слова Postgres и Flyway, а дедлайн послезавтра? Как интегрировать с Docker? Как мотивировать .NET-разработчиков отказаться от Windows и смузи в пользу Puppet и Linux? Как решать идеологические конфликты, если обслуживать Windows в продакшн нет ни сил, ни желания, ни ресурсов? Об этом, а также о Web Deploy, тестировании, CI, о практиках использования TFS в существующих проектах, и, конечно, о сломанных костылях и работающих решениях, в расшифровке доклада Александра.
Итак, Вася ушел, задача на мне, девелоперы ждут с вилами с нетерпением. Когда я окончательно осознал, что Васю не вернуть — приступил к делу. Для начала оценил процент Win VM в нашем парке. Счёт был не в пользу Windows.
Так как мы активно развиваем DevOps, я понял, что нужно что-то менять в подходе выноса нового приложения. Решение было одно — по возможности перевести все на Linux. Google мне помог — на тот момент уже был портирован .Net под Linux, и я понял, что это решение!
На это было несколько причин. Между «платить деньги» и «не платить» большинство выберет второе — как и я. Лицензия на MSDB стоит около 1 000 $, обслуживание парка виртуальных машин Windows исчисляется сотнями долларов. Для большой компании это большие затраты. Поэтому экономия — первая причина. Не самая важная, но одна из весомых.
Виртуальные машины Windows занимают больше ресурсов, чем их братья из Linux — они тяжелые. Учитывая масштаб большой компании мы выбрали Linux.
Система просто встраивается в существующий CI. Мы считаем себя прогрессивными DevOps’ами, используем Bamboo, Jenkins и GitLab CI, поэтому большая часть у нас крутится на Linux.
Последняя причина — удобное сопровождение. Нам нужно было снизить порог вхождения для «сопровожденцев» — ребят, которые понимают техническую часть, обеспечивают бесперебойность и обслуживают сервисы со второй линии. Они уже были знакомы со стеком Linux, поэтому им гораздо проще понять новый продукт, поддерживать и сопровождать, чем тратить дополнительные ресурсы, чтобы разобраться с аналогичным функционалом ПО для Windows платформы.
Первое и главное — удобство нового решения для разработчиков. Не все из них оказались готовы к переменам, особенно после произнесенного слова Linux. Разработчики хотят любимую Visual Studio, TFS c автотестами по сборкам и смузи. Как происходит доставка в продакшн — им не важно. Поэтому мы решили не менять привычный процесс и оставить для Windows-разработки всё без изменений.
Новый проект нужно встроить в существующий CI. Рельсы уже были и всю работу было необходимо выполнить с учетом параметров системы управления конфигурацией, принятых стандартов доставки и систем мониторинга.
Простота в поддержке и эксплуатации, как условие для минимального порога вхождения для всех новых участников со стороны разных подразделений и отдела сопровождения.
Дедлайн — вчера.
С чем тогда работала команда Windows?
Сейчас я могу уверенно сказать, что IdentityServer4 — это классная бесплатная альтернатива ADFS с аналогичными возможностями, или что Entity Framework Core — рай для разработчика, где можно не заморачиваться написанием SQL скриптов, а описывать запросы в БД в терминах ООП. Но тогда, на обсуждении плана действий, я смотрел на этот стэк как на шумерскую клинопись узнавая лишь PostgreSQL и Git.
На тот момент мы активно использовали Puppet как систему управления конфигурацией. В большинстве наших проектов мы применяли GitLab CI, Elastic, балансировали высоконагруженные сервисы с помощью HAProxy, следили за всем с помощью Zabbix, связки Grafana и Prometheus, Jaeger, и все это крутилось на железяках HP c ESXi на VMware. Всем знакомо — классика жанра.
Посмотрим и попытаемся понять, что же происходило до того, как мы затеяли все эти вмешательства.
TFS — это довольно мощная система, которая не только доставляет код от разработчика до конечной продакшн-машины, но также имеет набор для очень гибкой интеграции с различными сервисами — для обеспечения CI на кроссплатформенном уровне.
Раньше это были сплошные форточки. TFS использовал несколько Build-агентов, на которых собиралось множество проектов. В каждом агенте по 3-4 worker-a, чтобы распараллелить задачи и оптимизировать процесс. Дальше, согласно релизным планам, TFS доставлял свежеиспеченный Build на Windows-сервер приложений.
Для доставки и разработки используем TFS, а запускаем приложение на Linux Application server, и между ними какая-то магия. Этот Magic Box и есть соль предстоящей работы. Перед тем, как разобрать его по частям, сделаю шаг в сторону и скажу два слова о приложении.
Приложение предоставляет функциональность для оперирования предоплаченными картами.
Существовало два типа пользователей. Первый получал доступ, авторизуясь по сертификату SSL SHA-2. У второго был доступ по логину и паролю.
Дальше клиентский запрос попадал в HAProxy, который решал следующие задачи:
Проверка сертификата клиента шла по цепочке. Мы — authority и можем себе такое позволить, так как сами выдаем сертификаты клиентам сервиса.
Обратите внимание на третий пункт, чуть позже вернемся к нему.
Бэкенд планировали сделать на Linux. Бэкенд взаимодействует с БД, подгружает необходимый список привилегий и потом, в зависимости от того, какими привилегиями обладает авторизовавшийся пользователь, предоставляет доступ для подписания финансовых документов и отправки их на исполнение, либо генерации какого-то отчета.
Кроме двух контекстов, по которым ходил каждый из клиентов, существовал еще контекст identity. IdentityServer4 как раз позволяет авторизоваться, это бесплатный и мощный аналог для ADFS — Active Directory Federation Services.
Запрос в identity обрабатывался в несколько шагов. Первый шаг — клиент попадал в бэкенд, который обменивался данными с этим сервером и проверял наличие токена для клиента. Если не находил — запрос возвращался обратно на тот контекст, с которого он пришел, но уже с редиректом, и с редиректом шел на identity.
Второй шаг — запрос попадал на страницу авторизации в IdentityServer, где клиент регистрировался, а в базе данных IdentityServer появлялся тот самый долгожданный токен.
Третий шаг — клиент редиректился обратно на контекст, с которого он пришел.
У IdentityServer4 есть особенность: ответ на обратный запрос он возвращает по HTTP. Как ни бились с настройкой сервера, как ни просвещались документацией, но мы каждый раз получали первоначальный запрос клиента с URL, который пришел по HTTPS, а IdentityServer возвращал тот же самый контекст, но с HTTP. Мы были в шоке! И перевели все это через контекст identity на HAProxy, а в хедерах пришлось модифицировать протокол HTTP на HTTPS.
В чем же улучшение и где сэкономили?
Мы сэкономили деньги, используя бесплатное решение для авторизации группы пользователей, ресурсы, так как не выносили IdentityServer4 как отдельную ноду в отдельный сегмент, а использовали его совместно с бэкендом на том же самом сервере, где крутится бэкенд приложения.
Итак, как я обещал — Magic Box. Мы уже понимаем, что гарантированно движемся в сторону Linux. Давайте сформулируем конкретные задачи, которые требовали решения.
Манифесты Puppet. Чтобы доставлять и управлять конфигурацией сервиса и приложения, нужно было написать классные рецепты. Рулончик с карандашом красноречиво показывает как быстро и качественно это было сделано.
Способ доставки. Стандарт — это RPM. Все понимают, что в Linux без него никак, но сам проект после сборки представлял собой набор исполняемых DLL-файлов. Их было около 150, проект достаточно тяжелый. Единственное гармоничное решение — упаковать эту бинарщину в RPM и уже из нее разворачивать приложение.
Версионирование. Нам предстояло релизиться очень часто, и нужно было решить, каким образом формировать имя пакета. Это вопрос уровня интеграции с TFS. Build-агент у нас был на Linux. Когда TFS отправляет задачу обработчику — worker — на Build-агент, он передает ему еще и банч переменных, которые попадают в environment процесса обработчика. В этих переменных окружения передается имя Build, имя версии и другие переменные. Подробнее об этом в с разделе «сборка RPM-пакета».
Настройка TFS сводилась к настройке Pipeline. Раньше мы собирали на Windows-агентах все Windows-проекты, а сейчас появляется Linux-агент — Build-агент, который нужно включить в группу сборки, обогатить какими-то артефактами, сказать, какого именно типа проекты будут собираться на этом Build-агенте, и как-то модифицировать Pipeline.
IdentityServer. ADFS не наш путь, топим за Open Source.
Пройдемся по компонентам.
Состоит из четырех частей.
Linux Build-агент. Linux, потому что мы под него собираем — логично. Эта часть выполнялась в три шага.
RPM-repository. RPM-пакеты нужно было где-то хранить. Предполагалось, что мы будем использовать тот же самый корпоративный RPM-репозиторий, который доступен всем Linux хостам. Так и поступили. На сервере репозитория настроен webhook который скачивал из указанного места требуемый RPM-пакет. Версию пакета webhook’у сообщал Build-агент.
GitLab. Внимание! GitLab здесь используется не разработчиками, а отделом эксплуатации для контроля версий приложения, версий пакетов, контроля состояния всех Linux-машин и в нём хранится рецептура — все манифесты Puppet.
Puppet — разруливает все спорные моменты и доставляет именно ту конфигурацию, которую мы хотим, из Gitlab.
Начинаем погружаться. Как происходит доставка DLL в RPM?
Допустим, у нас есть рок-звезда разработки на .NET. Он использует Visual Studio и создает релизную ветку. После этого загружает ее в Git, и Git здесь — TFS-сущность, то есть это репозиторий приложения, с которым работает разработчик.
После чего TFS видит, что прилетел новый коммит. Какое приложение? В настройках TFS есть метка, какими ресурсами обладает тот или иной Build-агент. В данном случае он видит, что мы собираем .NET Core проект и выбирает Linux Build-агент из пула.
Build-агент получает исходники, выкачивает необходимые dependencies c репозитория .NET, npm и т.д. и после сборки самого приложения и последующей упаковки отправляет RPM-пакет в RPM-репозиторий.
С другой стороны происходит следующее. Инженер отдела эксплуатации занимается непосредственно выкаткой проекта: меняет версии пакетов в Hiera в репозитории, где хранится рецептура приложения, после чего Puppet триггерит Yum, забирает новый пакет из репозитория, и новая версия приложения готова к использованию.
На словах все просто, но что происходит внутри на самом Build-агенте?
Получены исходники проекта и задача на сборку от TFS. Build-агент запускает сборку самого проекта из исходников. Собранный проект доступен в виде множества DLL файлов, которые упакованы в zip-архив для снижения нагрузки на файловую систему.
ZIP-архив выкидывается в директорию сборки пакета RPM. Дальше Bash-скрипт инициализирует переменные окружения, находит версию Build, версию проекта, путь до директории сборки, и запускает RPM-build. По окончании сборки пакет публикуется в локальный репозиторий, который находится на Build-агенте.
Дальше, с Build-агента на сервер в RPM-репозитория отправляется JSON-запрос с указанием имени версии и билда. Webhook, про который я раньше говорил, выкачивает этот самый пакет из локального репозитория на Build-агенте и делает новую сборку доступной для установки.
Почему именно такая схема доставки пакета в репозиторий RPM? Почему нельзя сразу отправить собранный пакет в репозиторий? Дело в том, что это условие для обеспечения безопасности. Такой сценарий ограничивает возможность несанкционированной загрузки RPM-пакетов посторонними людьми на сервер, который доступен всем Linux-машинам.
На консилиуме с разработкой выяснилось, что ребятам ближе MS SQL, но в большинстве non-Windows проектов мы уже вовсю использовали PostgreSQL. Так как мы уже решили отказаться от всего платного, то стали использовать PostgreSQL и здесь.
В этой части хочу рассказать, как мы осуществляли версионирование БД и как выбирали между Flyway и Entity Framework Core. Рассмотрим их плюсы и минусы.
Flyway идёт только в одну сторону, мы не можем откатиться назад — это существенный минус. Сравнивать с Entity Framework Core можно по другим параметрам — с точки зрения удобства разработчика. Вы же помните, что мы это поставили во главу угла, и основным критерием было не изменить ничего для Windows-разработки.
Для Flyway нам требовалась какая-то обертка, чтобы ребята не писали SQL-запросы. Им гораздо ближе оперировать в терминах ООП. Написали инструкции по работе с объектами БД, сформировался SQL-запрос и выполнился. Новая версия БД готова, прокаталась — всё хорошо, всё работает.
У Entity Framework Core есть минус — при больших нагрузках он строит не оптимальные SQL-запросы, и просадка по БД может быть существенной. Но так как у нас не высоконагруженный сервис, мы не исчисляем нагрузку сотнями RPS, мы приняли эти риски и делегировали проблему будущим нам.
Entity Framework Core работает из коробки и удобен разработке, а Flyway легко интегрируется в существующий CI. Но мы же делаем удобно девелоперам:)
Puppet видит, что приходит изменение версии пакетов среди которых, тот, что отвечает за миграцию. Сначала устанавливает пакет, где содержатся миграционные скрипты и функционал завязанный на БД. После этого рестартуется приложение, которое работает с БД. Дальше идет установка оставшихся компонентов. Очередность установки пакетов и запуска приложений описаны в манифесте Puppet.
Приложения используют чувствительные данные, такие как токены, пароли к БД, все это подтягивается в конфиг с Puppet master, где они хранятся в зашифрованном виде.
После того, как мы определились и поняли, что у нас действительно все работает, я решил посмотреть, что творится со сборками в TFS в целом для отдела Win-разработки по другим проектам — быстро или нет собираемся/релизимся, и обнаружил существенные проблемы со скоростью.
Один из основных проектов собирается 12-15 минут — это долго, так жить нельзя. Быстрый анализ показал жуткую просадку по I/O, и это на массивах.
Проанализировав покомпонентно, я выделил три очага. Первый — «Kaspersky antivirus», который на всех Windows Build-агентах сканирует исходники. Второй — Windows Indexer. Он не был отключен, и на Build-агентах в реальном времени индексировалось все в процессе деплоя.
Третий — Npm install. Оказалось, что в большинстве Pipelines мы использовали именно этот сценарий. Чем он плох? Процедура Npm install запускается при формировании дерева зависимостей в package-lock.json, где фиксируются версии пакетов, которые будут использоваться для сборки проекта. Минус в том, что Npm install каждый раз подтягивает актуальные версии пакетов из интернета, а это немалое время в случае большого проекта.
Разработчики иногда экспериментируют на локальной машине, чтобы проверить работу отдельной части или проекта целиком. Иногда получалось, что локально все круто, но собирали, выкатывались — ничего не работало. Начинаем разбираться, в чем проблема — ага, разные версии пакетов с зависимостями.
Плюсы npm ci, в том, что мы собираем древо зависимостей единожды, и получаем возможность предоставить разработчику актуальный список пакетов, с которым он может сколько угодно экспериментировать локально. Это экономит время разработчиков, которые пишут код.
Сейчас немного о конфигурации репозитория. Исторически мы используем Nexus для управления репозиториями, в том числе Internal REPO. В этот внутренний репозиторий поставляются все компоненты, которые мы используем для внутренних целей, например, самописные мониторинги.
Мы также используем NuGet, так как он лучше кэширует по сравнению с другими пакетными менеджерами.
После того, как мы оптимизировали Build-агентов, среднее время сборки сократилось с 12 минут до 7.
Если посчитать все машины, которые мы могли бы использовать для Windows, но перевели на Linux в этом проекте, мы сэкономили порядка $10 000. И это только на лицензиях, а если учитывать содержание — больше.
На следующий квартал заложили в план работу над оптимизацией доставки кода.
Переход на пребилд Docker-образа. TFS — классная штука со множеством плагинов, которые позволяют интегрировать в Pipeline, в том числе, и сборку по триггеру, допустим, Docker-образа. Этот триггер мы хотим сделать на тот самый package-lock.json. Если каким-то образом меняется состав компонентов, которые используются для сборки проекта — у нас собирается новый Docker-образ. В дальнейшем он используется для развертывания контейнера с собранным приложением. Сейчас этого нет, но планируем перейти на микросервисную архитектуру в Kubernetes, который активно развивается в нашей компании и давно обслуживает продакшн решения.
Призываю всех выкинуть Windows, но это не потому, что я не умею ее готовить. Причина в том, что большая часть Opensource-решений — это Linux-стек. Вы хорошо сэкономите на ресурсах. На мой взгляд, будущее за решениями Open Source на Linux с мощным комьюнити.
Профиль спикера Александра Синчинова на GitHub [2].
DevOps Conf [1] — это конференция по интеграции процессов разработки, тестирования и эксплуатации для профессионалов от профессионалов. Именно поэтому проект, о котором рассказывал Александр? реализован и работает, а в день выступления проведено два успешных релиза. На DevOps Conf на РИТ++ [3] 27 и 28 мая будет еще больше подобных кейсов от практиков. Еще можно вскочить в последний вагон и подать доклад [4] или не торопясь забронировать [5] билет. Встретимся в Сколково!
Автор: Александр Титов
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/linux/315526
Ссылки в тексте:
[1] DevOpsConf: https://devopsconf.io/
[2] на GitHub: https://github.com/alextuning
[3] DevOps Conf на РИТ++: http://devopsconf.io/moscow-rit/2019
[4] подать доклад: https://conf.ontico.ru/lectures/propose/?conference=dc2019-rit
[5] забронировать: https://conf.ontico.ru/conference/join/rit2019.html?popup=3
[6] Источник: https://habr.com/ru/post/448496/?utm_source=habrahabr&utm_medium=rss&utm_campaign=448496
Нажмите здесь для печати.