Jenkins CI — вещи, которых мне не хватало

в 7:37, , рубрики: Hudson, Jenkins CI, plugins, Песочница, тестирование, метки: , ,

Доброго времени суток, %username%. Не так давно, меня попросили настроить Continuous Integration сервер, настоятельно порекомендовав использовать JenkinsHudson. Я, как человек несведущий в данных делах, решил изучить советы и обзоры по настройке CI систем в сети. Хочется выразить благодарность Wolonter за эту статью, она помогла мне понять общую концепцию и предоставила широчайший набор полезных плагинов. Но, как выяснилось, далеко не все, что я хотел реализовать, оказалось возможным найти в «интернетах». Для тех, кому интересны маленькие(и не очень) шалости и то, как я к ним пришел — прошу под кат.
image

Этапы постановки задачи

И так, первоначальной задачей была научить систему просто запускать web-сервера, отслеживать изменения в репозиториях и запускать jUnit, TestNG и Selenium тесты. Со временем эта задача обросла кучей мелочей, о которых будет рассказано позже. Вроде бы не сложно, но, как оказалось, первое мнение бывает ошибочным. С чистой совестью я установил centos 6 x64 на предоставленную мне виртуальную машину, и несколькими движениями установил CI:

sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
sudo rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key
sudo yum install jenkins

Естественно нужно не забыть установить jdk и проверить, добавился ли jenkins в автозагрузку. Примерно так, произошел мой первый неудачный запуск Jenkins CI — ajp порты Jenkins'a конфликтовали с одним из ранее установленных веб серверов(по совместительству нереляционной БД). После отключения ajp в конфигурационном файле все стало на свои места, и наконец-то я смог увидеть все чудеса воочию.

Ошибки

Пока я занимался настройкой, мной было допущено много ошибок, по этому предлагаю небольшой список, для людей которые любят экономить свое время:

  1. Иерархия проектов: если у вас довольно сложные иерархические зависимости, не поленитесь взять листик и продумать, что от чего зависит, и как в идеале это должно быть.
  2. Не пытайтесь настроить все проекты сразу. Старайтесь каждый проект довести до рабочего, промежуточного состояния, и переключайтесь на следующий. Навести марафет вы всегда успеете.
  3. Не вылизывайте конфигурацию проекта, до окончания промежуточной настройки всех зависимых проектов. Вполне возможно, что функционал, который вы решили реализовать — будет либо очень затратной по времени, либо вообще не реализуемой задачей на данном этапе. Писать свой плагин — не всегда оправданное занятие.
  4. Имена проектов. В некоторых ситуациях переименовать проект бывает затратно. А если вдруг выяснилось, что нужно переименовать 130 проектов без никакой структуры в названиях? Так и скрипт на коленке не напишешь, и руками переименовывать — жаба давит. Из простых советов к названиям: оно не должно быть сильно длинным, и должно нести в себе полезную информацию. Например, если у вас несколько версий одного продукта запущены на разных портах, почему бы их не назвать в духе «ProjectAbbr9587», где 9587 — это номер порта. Такая политика имен, позволяет быстро понять, и уменьшает возможность перепутать о чем идет речь.
  5. Если у вас стоит выбор ОС, на какой планируете запускать Jenkins CI, то Unix выбирайте в том случае, когда вы уверенныйпродвинутый пользователь. У меня большинство проблем возникло с правами доступа пользователя от которого запущен Jenkins и другие приложения. Если это не сложно, то постарайтесь запускать все от имени одного пользователя.
  6. Помните, Jenkins, больше Хайдегера любит плодить сущности. По этому заранее увеличьте максимальное количество запускаемых thread'ов и процессов от имени jenkins пользователя. Особенно, если у вас будет высоконагруженный CI сервер
  7. Не стоит устанавливать все plugin'ы которые видите. Часто, плагин нужен для «одноразового использования» и такой плагин можно заменить элементарным скриптом. Они могут не делать ничего полезного, но съедать лишние ресурсы(это не касается ChuckNorris Plugin'a).
  8. Заранее оцените ресурсоемкость. Подумайте, сколько сборок имеет смысл хранить. Какое количество артефактов действительно необходимо. Если у сервера маленькая производительность — продумайте расписание сборок так, что бы много ресурсоемких сборок не были запущены одновременно, а если вы не хотите следить за количеством используемых ресурсов — убедитесь, что максимальное число запускаемых сборок больше, или хотя бы такое же, как и число проектов.
  9. Информативность jUnit, TestNG etc. Если у вас запускается более 5000 юнит тестов, не особо сгруппированных, то даже красивый вывод как у дженкинса, не всегда способен быть информативным. Может есть смысл перегруппировать и разнести тесты по разным отчетам?
  10. Безопасность. Не стоит о ней забывать. Переход на парольный доступ не только умерит пыл саботажно настроенных особей, но и в случае «наезда» начальника «ты нам все сломал» будет документированное доказательство опровергающее аргумент «это был не я».
  11. Не стоит забывать, что Jenkins CI — это вспомогательный инструмент разработчика, и если не получается что-то сделать — не стоит на этом зацикливаться. Быть может гениальное решение придет к вам в голову через неделю, а может через эту же неделю придет осознание, что не так уж и нужна эта фича. Иногда эту мудрость стоит объяснить менеджеру проекта, его желание, что бы все было красиво — не всегда оправдано.

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

Первые попытки

Естественно после первого запуска мне было непривычно смотреть на синие кружечки, но их цвет был изменен далеко не сразу. Я настроил SVN и Git, распределил иерархию проектов и все вроде бы заработало. На следующий день, на одной задаче было 4 запланированных сборки, а запущенная длилась уже порядка 9 часов. Так вышло, что именно в этот день, был сделан «плохой» коммит, который заставлял уходить unit тест в бесконечное ожидание. На помощь пришел Build-timeout Plugin. С другой стороны, у меня были проекты, которые должны находиться в «бесконечной сборке». Ant task'и, которые запускали веб сервера и в сборку выводили лог действий, что было очень удобно. Естественно эти сервера могли находиться по несколько дней в запущенном состоянии, и без лишней надобности их останавливать нет смысла, т.к. на них могут вестись работы, показываться результаты заказчикам и прочее. Было решено создать проект, который в определенное время суток проверяет, есть ли обновление ветки, и если есть — выкачивает обновление и перезапускает сборку. Больше всего убила реакция менеджеров, которые задали вопрос: «а почему полосочка сборки проекта красненькая? — ну наверно, потому что сейчас 10 утра и сборка последние 6 часов ничего не писала в лог». Решение перекраски полосок я еще не нашел(см. пункт 11), но думаю это не сложно. С незаканчивающейся сборкой возникли другие проблемы: как же запустить юнит тесты, на отдельном сервере не создавая 2 проекта? Ведь для реализации этого, нужно запустить 2 ant task'a: 'start-server' и 'junit', первый из которых, не заканчивается. Почему на отдельном сервере спросите вы: потому что не хорошо забивать тестами основной веб сервер. Более того, веб сервер для тестов должен запускаться по необходимости и только на время выполнения юнит тестов, да бы не тратить лишние ресурсы. Вспомнив о всяческих возможностях ant решено было сделать как-то так:

<target name="start-server-with-units">
          <parallel>
            <daemons>
                <antcall target="start-server">
               </antcall>
            </daemons>
            <sequential>
               <waitfor maxwait="10" maxwaitunit="minute">
                 <socket server="localhost" port="${port}" />
               </waitfor>
               <antcall target="junit">
               </antcall>
            </sequential>
          </parallel>
        </target>

Формально мы ждем пока сервер запустится, запускаем на нем юнит тесты, и выключаем сервер по завершению тестов. Собственно что и требовалось. Мне кажется, что средствами Jenkins одним проектом, подобное сделать невозможно.

Branches

На мой взгляд в Дженкинсе плохо реализована поддержка бренчевания. «svn://svn.adress:port/${branchName}», где branchName — это переменная среды, часто не решает проблемы. Во-первых, подобные сборки можно запускать только руками, или же с параметром по умолчанию, но запуск сборки по Pall SCM уже явно невозможен. Если кому-то нужен подобный функционал(как выяснилось мне он все же не понадобился), то он с чистой совестью может написать groovy скрипт, который при запуске будет менять значение branchName по умолчанию на значение, которое было задано при запуске и потом запускать проект по обычному расписанию, и при необходимости добавить проверку, на наличие обновления в системе контроля версий. Минус такого подхода, что в череде ненужных сборок, трудно найти что-то полезное. Но это тоже не беда, в тот же postBuild groovy script можно прикрутить «педали», которые будут удалять «холостые» сборки. А стоит ли придумывать этот чудо groovy script если можно создать еще один проект, который будет проверять на наличие обновлений?
Вывод: иногда все же стоит создать на один проект больше, чем писать очередной велосипед.
Так же к проблеме бренчевания я бы отнес Build Name Setter Plugin. Довольно приятно вместо номера сборки видеть имя бренча, но и тут все не так просто. Т.к. не у всех переменных есть атрибут length(например у переменных среды), с помощью которого можно обрезать максимальную длину строки. Согласитесь, подобный вид не очень мотивирует, а что если длина будет не 30, а 255 символов?
image
Эту проблему опять же можно решить с preBuild groovy script. Но в моей ситуации было проще попросить делать названия бренчей информативными и не очень длинными. Очень удобно, когда какие-то неудобства решаются не велосипедами, а здравым смыслом.

Велосипеды

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

if(manager.build.result == hudson.model.Result.ABORTED){
    manager.build.@result = hudson.model.Result.SUCCESS
}

Т.к. у меня были проекты, которые можно было завершить только по нажатию крестика(что означает «отмена сборки»), меня очень сильно угнетали серые шарики, по этому мне захотелось сделать сборку успешной(пусть не портит статистику, я же знаю, что все правильно). Plugin'a реализующего это я не нашел, зато нашел Groovy Postbuild Plugin, который я активно пиарил и антипиарил выше. Так вот, groovy postbuild API (назовем его так), предлагает метод buildSuccess(), для задания успешного состояния сборки, и подобные для других состояний. Проблема в том, что данный метод ничего не делает. Все семейство может только ухудшить текущее состояние сборки, а т.к. Aborted намного хуже чем Success, то состояние сборки не менялось. В свое время на Jenkins'e я нашел три issue с просьбой реализации полноценного функционала изменения статуса сборок. Представленный выше кусок кода, настолько прост, что он просто не мог уложиться мне в голову. Задав себе вопрос: «а может быть у groovy реализован аналог reflections?» решение проблемы пришло моментально. Надеюсь любители извращений изысков оценят.

Build promotion. Да, я знаю про существование Promoted Builds Plugin'a, но в моем случае он опять же не помог. Был один сервер, который обновлялся только руками и поднимал прописываемый в ручную бренч, и был проект, который запускался раз в 2 часа, узнавал гитовую ветку первого проекта, скачивал ее и запускал юнит тесты. Количество сборок в проектах было разное. По договоренности было решено давать звезду, если последние юнит тесты были успешны. При чем звезда присваивается только по завершению работы первого сервера. Согласитесь, довольно необычная реализация promoted plugin заточенная чисто под личные нужды:

item = hudson.model.Hudson.instance.getItem("jUnitJobName") 
def build = item.getLastBuild()
if(build.getResult() == null){
    build = build.getPreviousCompletedBuild()
}
def result = build.getResult()
if(result == hudson.model.Result.SUCCESS){
    manager.addBadge("star-gold.gif", "Success Unit Tests")
} else {
    manager.addBadge("warning.gif", "Unit Tests Failure")
}

Борьба с ручным тестированием
Так уж сложилось, что тестеры бывают разные. И очень обидно, когда они скорее всего знают, почему что-то работает не так, но заблаговременно пытаются спихнуть ответственность, аргументируя «я все делал(а) хорошо, а оно сломалось, ты виноват, ты чини». Именно для таких случаев очень хорошо помогает AnsiColor Plugin. Если и это не подействует, то настоятельно рекомендую воспользоваться более обидными мерами: Build User Vars Plugin — личное обращение делает ваши действия более постыдными(откуда вообще машина узнала как меня зовут?). Главное не переусердствовать, возможна ситуация, когда скрипт некорректно работает, а тестеры боялись 3 дня спросить, ибо им в консоли писалось: «а ты точно все сделал правильно?». В этом случае все камни полетят в вас. Никто не любит выглядеть дураком. Но если накосчячил — получай что-то подобное(! красный цвет использовать обязательно):
image

Очень мне хотелось бы

Ну и в завершение напишу вещи — которые мне очень бы хотелось, но писать плагины я не стану. Может быть они и реализованы, но я их не нашел. За подсказки — буду благодарен.
Авто запуск сборок при старте. Допустим из-за скачка напряженияотсутствия света сервер перезагрузили. На нем запустился JenkinsCI, но не запустились проекты, которые должны быть постоянно запущены? Каждый раз запускать ручками — глупо. Так что, очень хочется галочку: «запускать ли данный проект, после перезагрузки?»
SVNGit custom branch + patch, если обновляться указанными выше образами, то он просто переписывает все существующие локальные изменения. Это делает очень неудобным применение патчей. Уж больно не нравятся мне мои скрипты, которые отменяют патч, выкачивают нужную ветку и применяют патч обратно.
System Message. Что бы результат выполнения 'jenkinsconfigurationSystem Message' отображался на всех страницах, а не только на view. И вообще, хотелось бы больше плагинов по изменению внешнего вида. Т.к. в первую очередь, это облегчает работу менеджерам, а чем меньше вопросов по эксплуатации, тем больше времени остается на разработку.
Rebuild Plugin. Хочу, что бы Rebuild Plugin научился останавливать предыдущую сборку, а то нажал кнопку «rebuild», а он только запланировал еще одну сборку.
P.S. Я отдаю себе отчет в том, что вещи которые мне хочется, далеко не вещи первой необходимости, но это те вещи, которых мне наверное не хватает для полного счастья. Надеюсь данная статья будет кому-то полезной.

Автор: kentilini

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js