Continuous Integration в Дневник.ру

от автора

В этой статье мы решили немного рассказать о средствах continuous integration (CI), которые используем в компании Дневник.ру, и поделиться небольшими наработками в этом направлении. Большая часть материала может показаться банальной рекламой выбранного движка CI или попыткой вызвать holy war (причем не один), но подобного не было в целях. Статья также не является путеводителем или описанием каких-либо фитч и может быть расценена как статья от кэпа – главное, чтобы она была интересна и вызвала дискуссию.

Jenkins

Одной из первых задач, которую я для себя поставил, придя в компанию около 3х месяцев назад, было внедрение «нормальной» (с моей точки зрения) практики CI. Не то чтобы в компании тогда не было подобной практики, но на тот момент CI в компании был представлен с помощью небезызвестного Jenkins. Это open source проект, являющийся ответвлением от другого большого CI инструмента — Hudson. Последний раз c Hudson я работал в 2008-ом году, и опыт был резко негативный. Меня не испугал интерфейс, я смог пережить сложности настройки, но ситуация, когда в итоге все это падает и спотыкается на ровном месте по 20 раз на дню, не стала для меня положительной характеристикой хорошо оттестированной стабильной системы. Как оказалось, время не лечит, и эти проблемы никуда не ушли – даже после 4-х лет и смены названия.

Виной всему основная идея, заложенная и в Hudson и в Jenkins – это просто максимально обобщённые движки для CI. Основная их мощь должна была заключаться в их расширяемости:

  • Берём движок
  • Ставим тыщщу плагинов на все случаи жизни
  • Пытаемся всё это настроить
  • Типа profit

Основной момент, который многие склонны забывать – плагины не всегда есть хорошо, а, зачастую, даже вредно. Плагины, как правило, пишут другие люди, и вы понятия не имеете: насколько хорошо каждый их них был оттестирован, не содержит ли он чего-нибудь неожиданного в коде, – примеров можно привести массу. Поэтому меня всегда забавляли люди, обвешавшие свои системы плагинами, как рождественские ёлки, а потом ругающие тормоза и падения в своих любимых: FireFox’е, Миранде, Visual Studio (добавьте по вкусу).

Использование проверенных источников и принцип минимализма всегда уберегали меня от подобных неприятностей. К сожалению, в случае с Jenkins всё было не так просто. Изначально имеющий в основе идею maximum generic solution, но, тем не менее, принадлежащий к миру Java, для мира .Net он, из коробки, не умел ровным счётом ничего.

Хочешь собирать .Net проекты? Ставь сторонний плагин. Хочешь LDAP аутентификацию? Ставь сторонний плагин (про костыли для подтягивания email адресов из LDAP я вообще молчу). Даже Subversion из коробки не поддерживается, нужно опять ставить ещё один плагин – причём, сомнительного качества и с весьма скудным набором функций. В общем, все это выливалось в титанические усилия по настройке и поддержке фермы плагинов (всего порядка 50). И, как я уже отметил, код может быть плохо оттестирован, поэтому попытка обновления версии какого-нибудь плагина легко могла привести к краху всего Jenkins. Неслучайно в нем предусмотрена возможность сделать downgrade прямо на странице управления плагинами (кнопка downgrade’а всего Jenkins также присутствует) – все это связано именно с проблемами стабильности и совместимости.

В итоге вопросы стабильности, бедность инфраструктуры для мира .Net, а также весьма значительные затраты на поддержку и администрирование билдов сводило на нет все преимущества Jenkins как бесплатной системы CI.

Team City

Сразу оговорюсь, я не мучился долго с выбором движка. Я сразу знал, что буду использовать JetBrains Team City. Причины достаточно просты:

  • Я его прекрасно знаю и внедрял в процесс множество раз;
  • Его легко администрировать;
  • Из коробки есть всё, что надо для .Net проектов;
  • Отличная интеграция с IDE и окружением разработчика;
  • Шаблонизатор проектов (этого очень не хватало в Jenkins);
  • Достаточно удобная лицензионная политика Jet Brains: бесплатная Professional редакция Team City содержит только два ограничения: 20 билд проектов и 3 билд машины;
  • Ну, и прочие вкусности.

Самая важная причина, про которую я помню всегда: open source — это для бедных. Тут я сразу призываю всех любителей поучаствовать в специальной олимпиаде не воспринимать эту фразу буквально. Я придаю этому более глубокий смысл. Open Source – это хорошо, и спасение для многих компаний, но суть всегда остаётся одна: open source-решение в 90% случаев будет проигрывать аналогичной коммерческой разработке. Именно поэтому я даже не стал смотреть в сторону CC.Net и прочих.

Может возникнуть закономерный вопрос: почему не Atlassian Bamboo или TFS? Всё очень просто. Выбрать TFS означало бы сжигание мостов для других технологий, а я не сторонник таких мер и уверен, что наш проект очень скоро выйдет за рамки 100% .Net решения. Да, его можно использовать для других технологий, но только с использованием костылей, и вновь теряя время.

Atlassian Bamboo – сам по себе не плох, отличная интеграция с Jira, неплохой UX. Но отсутствие pre-commit билдов и бедноватая интеграция с окружением разработчика, отсутствие поддержки NuGet и прочие мелочи склонили чашу весов в пользу Team City.

Удивление вызовет то, что внутри Team City использует тот же самый подход, за который я хаял Jenkins/Hudson – это plugin система, и весь его функционал представлен исключительно плагинами. Да, это так, но с одним исключением: все плагины, входящие в стандартную поставку TeamCity, проходят тестирование силами JenBrains. Сказывается коммерческая сущность продукта, они не могут позволить себе выкинуть на рынок пригоршню поделок. Люди платят за это деньги, а это значит, что и требования куда суровее.

Плюс, что мне всегда нравилось в продуктах JetBrains – это то, что фраза user experience для них не пустой звук. Тебя с самого начала берут за ручку и проводят через все тернии установки и администрирования с максимальным комфортом. Я ценю такую заботу и считаю, что именно так и надо делать профессиональные продукты.

Внедрение

Я не буду расписывать процесс установки и настройки TeamCity, это не является целью данной статьи и не сильно интересно.

Скажу лишь, что систему я ставил на Windows Server 2008 R2, и в качестве базы данных использовал MS Sql Server 2008 R2. Быстро выяснилась одна особенность. В схеме базы данных для Ms Sql Server они не везде поддерживают юникод. Особенно это было ощутимо, когда разработчики писали комментарии к коммиту на русском. Эту проблему было решить достаточно просто. В базе данных, в таблице vcs_history тип колонки description был изменён с varchar(max) на nvarchar(max). Да, это может повлечь проблемы с апгрейдом на последующие версии, но это было необходимо.

Главную сложность представляла не настройка Team City, а сами разработчики. Точнее, попытка заставить их воспринимать поломку билда на CI как из ряда вон выходящее событие, которое должно быть исправлено немедленно. Здесь рецепт может быть только один: тщательное документирование процесса и личный контроль исполнения. Через какое-то время люди поняли, что от них требуется, и теперь мне даже не приходится это контролировать.

Первый билд, который мы установили – обычная интеграционная сборка .Net солюшена. Для того чтобы более правильно понимать, кто именно завалил билд и чем, была настроена политика – собирать билд на каждый коммит в svn.

Этого оказалось мало, так как простой интеграционный билд не проверял ошибки компиляции Asp.Net.
Немного подискутировав на эту тему, мы решили добавить во все проекты веб-приложений новую конфигурацию CI Build и прописать следующий target в проектных файлах

<Target Name="AfterBuild" Condition="'$(Configuration)' == 'CI Build'">     <AspNetCompiler VirtualPath="temp" Clean="true" PhysicalPath="$(ProjectDir)" />   </Target> 

Таким образом, каждый разработчик мог перед коммитом запустить Asp.Net компиляцию на проекте.

Появилась проблема с интеграционным билдом. Когда к нему добавилась Asp.Net компиляция, время полного билда выросло с 3х минут до 20-ти, что сводило на нет все преимущества билда на каждый коммит. Нам необходимо было получать как можно быстрее и оперативнее сообщения об ошибках компиляции. Поэтому было принято решение разнести интеграционный билд на 2 части:

  • Msbuild компиляция
  • Asp.Net компиляция

TeamCity поддерживает так называемые snapshot dependency. Вкратце это работает следующим образом:

  • Сначала исполняется обычная интеграционная сборка проекта, как отдельная билд конфигурация.
  • В случае успеха запускается другая билд конфигурация, проводящая asp.net компиляцию проекта, причём, запускается она именно на этом же snapshot’е исходников, на котором была проведена предварительная интеграционная сборка – т.е. на той же ревизии исходников.

Таким образом, мы не загружали машины ненужной Asp.Net компиляцией в том случае, если интеграционный билд не прошел. И получали msbuild ошибки намного быстрее.

Помимо интеграционных билдов и запуска тестов, мы используем TeamCity для сборки установочных пакетов. Встроенная система артефактов позволяет указать, что будет являться артефактом сборки и, при необходимости, заархировать его. Эти артефакты можно скачать прямо на UI

Или получить через REST API.

До TeamCity сборка пакета осуществлялась с помощью вызова PowerShell скрипта, который просто использовал 7Zip и через command line инструктировал архиватор, какие типы фалов в архив включать не нужно (так как количество типов файлов, которые нужно включать в архив, было намного больше). Поэтому система артефактов вызвала небольшое разочарование. Во-первых, ни один из встроенных архиваторов не показал хорошего соотношения сжатие/скорость по сравнению с 7Zip. Ближе всех был tar.gz, но только что близок. Во-вторых, в скрипте артефактов нельзя было прописать конструкции на исключение из архива файлов определённых типов, что было крайне неудобно и заставляло полностью прописывать все то, что нужно включить (кстати, можете проголосовать за эту фитчу). Кроме того, размер архива был порядка 500 Mb, что заставляло UI задумываться о вечном.

Мы похоронили мысль использовать систему артефактов, и всё так же используем PowerShell, благо что у TeamCity есть встроенный runner для него.

NuGet

Одной из фитч, которая представляла для меня большой интерес, была заявленная поддержка NuGet сервера внутри TeamCity. Я уже давно задался идеей поднять корпоративный NuGet сервер с целью пресечь бесконтрольное добавление зависимостей в проект и просто для внутренних библиотек, которые было бы удобнее использовать через NuGet. Так что возможность использования для этих целей сам движок CI – вместо расшареной папки в сети – представлялась многообещающей.

TeamCity предоставляетдва NuGet Feed’а: один гостевой, без аутентификации, второй с установленным на TeamCity типом авторизации.

Feed можно подключить и в Visual Studiо, и в NuGet Restore Package (файл NuGet.targets), чтобы исключить возможность добавления новой зависимости втихаря.

<ItemGroup Condition=" '$(PackageSources)' == '' ">         <!-- Package sources used to restore packages. By default, registered sources under %APPDATA%\NuGet\NuGet.Config will be used -->         <!-- The official NuGet package source (https://nuget.org/api/v2/) will be excluded if package sources are specified and it does not appear in the list --> 			<PackageSource Include="http://teamcity/guestAuth/app/nuget/v1/FeedService.svc/" />             <!--<PackageSource Include="https://nuget.org/api/v2/" />          -->     </ItemGroup>  

К сожалению, ложка дёгтя всё-таки присутствует. Нет никакого UI для управления пакетами во встроенном NuGet сервере. Более того, основная идея, которую JetBrains закладывает в него – это то, что он должен использоваться только для NuGet пакетов, уникальных для вашей компании. С их слов, они не собираются конкурировать с официальным NuGet сервером.

Можно добавить NuGet пакет в feed, но для этого нужно сделать так, чтобы он оказался в списке артефактов. Что, собственно, я и сделал.

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

И как результат.

Это сработало, и все пакеты стали доступны и в feed’е.

Единственной проблемой оставалось удаление пакетов из feed’а. Это мы решили с помощью встроенного средства очистки TeamCity.

Таким образом, при удалении из svn пакет уже никогда не будет маячить в списке артефактов для предыдущих билдов – следовательно, навсегда исчезнет из feed’а.

Для того чтобы превратить свою библиотеку в NuGet пакет, достаточно использовать специальный runner NuGet Pack, который очень прост в настройке.

Послесловие

Это далеко не всё, что сделано и что запланировано в области CI: автоматический деплой приложения или базы данных, автогенерация тестовых данных, static code analysis и пр. пока остались вне этой статьи, и наш подход мы постараемся описать в следующих.

P.S. Как описано в начале статьи, она не является рекламой, и компания Дневник.ру не получает от JetBrains никаких лицензионных поблажек.

Автор статьи: Александр Лукашов, старший разработчик Дневник.ру.

ссылка на оригинал статьи http://habrahabr.ru/company/dnevnik_ru/blog/169329/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *