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

Доброго времени суток, %username%. Не так давно, меня попросили настроить Continuous Integration сервер, настоятельно порекомендовав использовать Jenkins\Hudson. Я, как человек несведущий в данных делах, решил изучить советы и обзоры по настройке 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, но не запустились проекты, которые должны быть постоянно запущены? Каждый раз запускать ручками — глупо. Так что, очень хочется галочку: «запускать ли данный проект, после перезагрузки?»
SVN\Git custom branch + patch, если обновляться указанными выше образами, то он просто переписывает все существующие локальные изменения. Это делает очень неудобным применение патчей. Уж больно не нравятся мне мои скрипты, которые отменяют патч, выкачивают нужную ветку и применяют патч обратно.
System Message. Что бы результат выполнения 'jenkins\configuration\System Message' отображался на всех страницах, а не только на view. И вообще, хотелось бы больше плагинов по изменению внешнего вида. Т.к. в первую очередь, это облегчает работу менеджерам, а чем меньше вопросов по эксплуатации, тем больше времени остается на разработку.
Rebuild Plugin. Хочу, что бы Rebuild Plugin научился останавливать предыдущую сборку, а то нажал кнопку «rebuild», а он только запланировал еще одну сборку.
P.S. Я отдаю себе отчет в том, что вещи которые мне хочется, далеко не вещи первой необходимости, но это те вещи, которых мне наверное не хватает для полного счастья. Надеюсь данная статья будет кому-то полезной.
  • +11
  • 12,7k
  • 5
Поделиться публикацией

Комментарии 5

    0
    Статьи про CI, а тем-более про Jenkins это всегда здорово!

    Не стоит устанавливать все plugin'ы которые видите.… Они могут… съедать лишние ресурсы… .

    Кроме того после установки практически каждого plugin'a расширяются системные настройки и/или настройки проекта, что может усложнить процесс настройки для начинающего пользователя. Опираясь на свой незначительный опыт могу сказать, что незнакомые plugin'ы однозначно удобнее устанавливать по одному, особенно (!) если это plugin'ы схожи по назначению.

    … Если у сервера маленькая производительность — продумайте расписание сборок так, что бы много ресурсоемких сборок не были запущены одновременно, а если вы не хотите следить за количеством используемых ресурсов — убедитесь, что максимальное число запускаемых сборок больше, или хотя бы такое же, как и число проектов.

    На мой взгляд выгода от данной рекомендации весьма сомнительна. По умолчанию Jenkins создает executer'ов по количеству ядер, что (при однопоточной сборке проектов) позволяет исключить взаимное влияние на наличие ресурсов между одновременно запущенными проектами. Если сборка проекта не однопоточнная, то за ней можно закрепить большее число executor'ов.
    Если вы создадите большее число executor'ов чем количество ядер на вашем сервере, то, на мой взгляд, ваши проекты хоть и начнут собираться одновременно, но закончат они все-равно не раньше чем если бы они были запущенны один за другим.

    Безопасность. Не стоит о ней забывать. ...

    Однозначно полезный совет. Касательно безопасности хочется добавить, что помимо настройки глобальных прав Jenkins позволяет «уточнять» права индивидуально для каждого проекта и я бы рекомендовал сразу же следовать этой практике. Т.е. глобально всем пользователям (кроме админа) уровень Read, а вот уже на каждом отдельном проекте индивидуально для каждого пользователя устанавливаются конкретные права; в 99% случаев для всех пользователей от проекта к проекту права будут одинаковы, но когда наступит тот самый 1%, то такой подход себя оправдает!

    Например, если у вас несколько версий одного продукта запущены на разных портах, почему бы их не назвать в духе «ProjectAbbr9587», где 9587 — это номер порта. Такая политика имен, позволяет быстро понять, и уменьшает возможность перепутать о чем идет речь.

    Обратите внимание на multi-configuration project, возможно это позволит сократить число проектов и сгруппировать их.

    Естественно после первого запуска мне было непривычно смотреть на синие кружечки, но их цвет был изменен далеко не сразу.

    Все правильно! Синие кружочки это моветон! Кружочки должны быть исключительно зелеными ;)!

    С другой стороны, у меня были проекты, которые должны находиться в «бесконечной сборке». Ant task'и, которые запускали веб сервера и в сборку выводили лог действий, что было очень удобно. Естественно эти сервера могли находиться по несколько дней в запущенном состоянии, и без лишней надобности их останавливать нет смысла, т.к. на них могут вестись работы, показываться результаты заказчикам и прочее.
    Допустим из-за скачка напряжения\отсутствия света сервер перезагрузили. На нем запустился JenkinsCI, но не запустились проекты, которые должны быть постоянно запущены? Каждый раз запускать ручками — глупо. Так что, очень хочется галочку: «запускать ли данный проект, после перезагрузки?»
    Т.к. у меня были проекты, которые можно было завершить только по нажатию крестика(что означает «отмена сборки»), меня очень сильно угнетали серые шарики, по этому мне захотелось сделать сборку успешной(пусть не портит статистику, я же знаю, что все правильно).

    Вообще ничего не понял. Возможно сказывается тот факт, что я не знаком с Java, но я никак не могу понять для каких задач может потребоваться такое? «которые можно было завершить только по нажатию крестика», — а как разработчики у себя на рабочих машинах собирают эти проекты?
      0
      Если сборка проекта не однопоточнная, то за ней можно закрепить большее число executor'ов.
      Если вы создадите большее число executor'ов чем количество ядер на вашем сервере, то, на мой взгляд, ваши проекты хоть и начнут собираться одновременно, но закончат они все-равно не раньше чем если бы они были запущенны один за другим.

      Тут скорее нужно бояться не длительности выполнения, а возможности невыполнения сборки. У нас ограниченное максимальное число запланированных сборок, и соответственно возможна ситуация, когда некоторые сборки будут проигнорированы, хот\ это и маловероятно.

      Обратите внимание на multi-configuration project, возможно это позволит сократить число проектов и сгруппировать их.
        0
        Случайно отправил недописав
        Если сборка проекта не однопоточнная, то за ней можно закрепить большее число executor'ов.
        Если вы создадите большее число executor'ов чем количество ядер на вашем сервере, то, на мой взгляд, ваши проекты хоть и начнут собираться одновременно, но закончат они все-равно не раньше чем если бы они были запущенны один за другим.

        Тут скорее нужно бояться не длительности выполнения, а возможности невыполнения сборки. У нас ограниченное максимальное число запланированных сборок, и соответственно возможна ситуация, когда некоторые сборки будут проигнорированы, хот\ это и маловероятно.

        обратите внимание на multi-configuration project, возможно это позволит сократить число проектов и сгруппировать их

        От мультиконфигураций отказались осознанно. Например на дженкинсе у нас запускаются тестовые сервера, и нам нужно уметь запускать конкретные сервера(конкретные бренчи) по требованию, а не сразу все. Более того в силу особенностей — это бесконечные сборки(подробнее ниже), и останавливать один сервер, что бы запустить два — не рационально.

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

        Средства джава позволяют запускать подобные проекты и другими способами, просто гораздо удобнее читать основной лог веб сервера в логе сборки и на ходу понимать что к чему, чем копаться по логам сохраненным на сервере(выгрузить как артефакты их не удастся), к которому не у всех есть доступ.

        ну а по поводу безопасности у дженкинса реализован отличнейший функционал плагинов, позволяющих быстро и удобно определять политику пользователей. Если интересно — могу более подробно написать
          0
          У нас ограниченное максимальное число запланированных сборок, и соответственно возможна ситуация, когда некоторые сборки будут проигнорированы,
          Хм, не сталкивался. Знаю, что один и тот же проект в очередь больше двух раз не встанет, но разные проекты становятся в очередь «без потерь».

          ну а по поводу безопасности у дженкинса реализован отличнейший функционал плагинов, позволяющих быстро и удобно определять политику пользователей. Если интересно — могу более подробно написать
          Если это удобнее чем тот вариант, который описал я, то однозначно нужно.
            0
            Хм, не сталкивался. Знаю, что один и тот же проект в очередь больше двух раз не встанет, но разные проекты становятся в очередь «без потерь».

            средства дженкинса(а может и какие-то плагины точно не скажу) позволяют определять максимальное количество запланированных сборок. Я не уверен, но вроде как возможно запретить планировать сборки в принципе.

            Если это удобнее чем тот вариант, который описал я, то однозначно нужно.

            из того что могу сказать по памяти — был нехороший опыт с LDAP логинами. Ну и наиболее удобный мне способ, позволял определять правила не для каждого проекта, а для группы проектов названия которых совпадают с регулярным выражением(еще один плюс в пользу структурированных названий проектов)

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

      Самое читаемое