Debian. Continuous Integration сервер Jenkins для непрерывного тестирования Django-проектов

Сегодня я расскажу как реализовать непрерывное тестирование Django-проекта с помощью Jenkins CI. Мы также будем собирать и выводить разного рода отчеты по тестам, для этого мы установим и настроим дополнительные плагины. Следующие статьи будут о том, как развернуть Jenkins-ноды на FreeBSD, как запустить BDD-тесты с поддержкой Selenium WebDriver через Lettuce и splinter.
В качестве поставщика тестируемого кода будем использовать git, в качестве транспорта для него будем использовать ssh (у меня для этого используется gitolite), адрес сервера будет git.example.org. Адрес сервиса с запущенным Jenkins будет .
Ранее, я уже упоминал о Jenkins, там же я говорил о Attlassian Bamboo и Hudson, но для своих проектов я выбрал Jenkins за его простоту, функциональность, количество полезных плагинов и истории-успеха среди Django-проектов. Что касается Hudson, то Jenkins это его форк, о причинах разделения проектов вы можете почитать в статье на хабре.
Кстати, любителям SaaS можно не устанавливать Jenkins, а воспользоваться ShiningPanda.
Терминология
В терминологии Jenkins слово Job и Project это одно и тоже, мы же просто будем называть их "задачами".
Термин Build (далее билд) - это результат выполненой задачи, может находится в трех оконченных состояниях:
- SUCCESS (успешный билд, за всю работы задачи небыло ошибок);
- FAILED (произошла какая-то ошибка);
- ABORTED (билд был прерван по какой-либо причине, в осноновном это - ручное прерывание).
Термином View (а конкретно "List View") обозначается некая страница, на которой перечислены задачи, настраивается она либо выбором через чекбоксы, либо по совпадению по заданному паттерну с именами задач.
В процессе чтения статьи вы встретите переменные окружения:
- $JENKINS_HOME - это путь до домашнего каталога Jenkins, скорее всего это "/var/lib/jenkins". Там будут храниться все конфиги Jenkins, задачи, билды и т.д., вообщем всё что относится к Jenkins.
- $WORKSPACE - путь до каталога, в котором выполняется задача;
Установка Jenkins на Debian
Добавляем ключ и репозиторий:
wget -q -O - | sudo apt-key add - sudo sh -c 'echo deb binary/ > /etc/apt/sources.list.d/jenkins.list'
Обновляемся и устанавливаем:
sudo apt-get update sudo apt-get install openjdk-8-jre jenkins
Инструкции взяты отсюда.
Настройка Jenkins
Открываем на редактирование /etc/default/jenkins и добавляем следующие директивы:
# Указываем нужный порт, # на который далее мы будем проксировать через Nginx PORT=8090 # Устанавливаем нужную нам кодировку, # чтобы текст, который попал в stdout (например из тестов) выводился корректно. # А также устанавливаем нужную таймзону, у меня это UTC JAVA_ARGS="-Dfile.encoding=UTF8 -Duser.timezone=UTC"
Теперь запустим Jenkins:
sudo service jenkins start
После запуска Jenkins вы можете увидеть текущую кодировку и таймзону в разделе "Manage Jenkins" > "System Information" (прямой адрес ).
Теперь произведем базовые настройки Jenkins, перейдите в "Manage Jenkins" > "Configure System":
- В секции Jenkins Location укажем следующее:
- В поле Jenkins URL укажите адрес ;
- В поле System Admin E-mail Address укажите почту, от которой будут приходить письма от Jenkins, например "jenkins@example.org".
Далее, нам надо настроить отправку уведомлений по почте, переходим в секцию E-mail Notification и настраиваем SMTP:
- В поле SMTP server указываем "localhost", либо другой обслуживающий ваш сервер почтовый сервер. Если используете локальный sendmail, то установите его.
В секции Git plugin:
- В поле Global Config user.name Value укажите "Jenkins";
- В поле Global Config user.email Value укажите "jenkins@example.org".
Настроим проксирование через Nginx
Вот пример конфига для Nginx, работать будем через самописный ssl-сертификат и добавим base-auth:
server { listen 443 ssl; server_name jenkins.example.org; ssl_protocols SSLv3 TLSv1; ssl_certificate /etc/nginx/certs/jenkins.pem; ssl_certificate_key /etc/nginx/certs/jenkins.key; location / { auth_basic "Password, please!"; auth_basic_user_file /etc/nginx/auth-file; proxy_read_timeout 300; proxy_send_timeout 300; proxy_pass } } server { listen 80; server_name jenkins.example.org; return 301 }
Сгенерируем пару логин/пароль для "base-auth" авторизации:
htpasswd -cb /etc/nginx/auth-file jenkins MY_SECRET_PASSWORD
Сгенерируем самописный ssl-сертификат для шифрования трафика:
mkdir /etc/nginx/certs cd /etc/nginx/certs openssl req -new -x509 -nodes -newkey rsa:1024 -days 3650 -keyout jenkins.key -out jenkins.pem \ -subj "/C=RU/ST=/L=/O=/OU=/CN=jenkins.example.org/emailAddress=jenkins@example.org"
Для применения настроек перезагрузим Nginx:
nginx -s reload
Не забудем скрыть порт 8090 за фаерволлом:
iptables -t filter -A INPUT -i lo -p tcp --dport 8090 -j ACCEPT iptables -t filter -A INPUT -i eth0 -p tcp --dport 8090 -j DROP
Где "eth0" - внешний сетевой интерфейс.
Теперь сохраним наши правила в "/etc/iptables.rules":И добавим в автозагрузку:iptables-save > /etc/iptables.rulesecho "pre-up iptables-restore < /etc/iptables.rules" >> /etc/network/interfacesЕсли быть точнее, то надо в секцию вашего интерфейса добавить pre-up, но так как чаще всего именно в конце файла определён нужный интерфейс, то рекомендация выше будет верна. Развернутый пример:auto eth0 iface eth0 inet static hwaddress aa:bb:cc:dd:ee:ff address 203.0.113.42 netmask 255.255.240.0 gateway 203.0.113.1 pre-up iptables-restore < /etc/iptables.rules
Установка плагинов
Переходим в "Manage Jenkins" > "Manage Plugins" > "Available", выбираем:
- GIT plugin, с ним также установится плагин Git Client Plugin - так как мы пользуемся git, то очевидно что надо установить плагин для работы с этой SCM;
Если у вас возникли проблемы с GIT plugin, и его версия 1.3.0, а версия GIT client plugin 1.0.4, то прочитайте комментарии к багу JENKINS-17204, там есть мой комментарий, который вам непременно поможет. А если вкратце, то в новой версии плагина есть баг, и надо просто откатиться на версию назад.
- AnsiColor Plugin (форк и более свежий оригинал на GitHub) - с помощью него можно лицезреть цветные логи, которые генерирует ваша программа (например в harvest есть verbose-режим с цветный выводом и т.п.);
В настройках задачи установите чекбокс на Color ANSI Console Output и выберите нужный ANSI color map, у меня это "xterm".
- Timestamper - будет выводить текущее время на каждую строчку вывода лога из консоли;
В настройках задачи установите чекбокс на Add timestamps to the Console Output.
- xUnit Plugin - если ваши тесты умеют генерировать отчеты в формате xUnit (а Lettuce и django-jenkins это умеют делать), то вы сможете выводить информацию о результатах тестирования в удобном для человека виде (графики и таблицы), например вот так:

- Build-timeout Plugin - будет прерывать зависшие задачи в Jenkins;
- Cron Column Plugin - выводит информацию о заданных периодах выполнения задачи при выводе списка ваших задач.
Опционально можно дополнить следующими плагинами:
- Cobertura Plugin - для вывода отчета о покрытии кода тестами (code coverage);
- Violations - для вывода отчетов по результатам работы pylint, pyflakes и pep8.

Настройка git-плагина
Входим на сервер, где вы установили Jenkins и выполняем следующее:
sudo su - jenkins mkdir ~/.ssh && cd ~/.ssh emacs config
Помещаем в файл config следующие строки:
Host git.example.org HostName git.example.org User git IdentityFile ~/.ssh/git_example_org IdentitiesOnly yes
Теперь создадим ключи для авторизации:
ssh-keygen -t rsa
Добавляем в gitolite (либо в GitHub, Assembla и т.д., смотря чем вы пользуетесь) созданный публичный ключ ~/.ssh/git_example_org.pub.
Тут же на сервере, не помешает указать пользователя для git:
git config --global user.email "jenkins@example.org" git config --global user.name "Jenkins"
Проверяем работоспособность ssh-ключей и настроек:
git clone git@git.example.org:repo-name.git /tmp/test
Устанавливаем и настраиваем django-jenkins
Это приложение позволяет упростить процесс интеграции с Jenkins CI, нам оно поможет сгенерировать нужные для нас отчеты. Создайте отдельный "settings" в проекте для него, например project/settings/jenkins.py:
import settings # Добавим это приложение в INSTALLED_APPS INSTALLED_APPS = settings.INSTALLED_APPS + ( 'django_jenkins', ) # Укажем нужные нам ранеры JENKINS_TASKS = ( # 'django_jenkins.tasks.with_coverage', # 'django_jenkins.tasks.django_tests', 'django_jenkins.tasks.dir_tests', 'django_jenkins.tasks.run_pep8', 'django_jenkins.tasks.run_pyflakes', 'django_jenkins.tasks.run_pylint', # 'django_jenkins.tasks.run_jslint', # 'django_jenkins.tasks.run_csslint', # 'django_jenkins.tasks.run_sloccount', # 'django_jenkins.tasks.lettuce_tests', ) # И какие приложения нужно тестировать PROJECT_APPS = ( 'app1', 'app2', )
Также, при вызове команды manage.py jenkins можно передать дополнительные опции для указанных в JENKINS_TASKS раннеров, для этого смотрите manage.py jenkins --help.
Теперь установим в проект django-jenkins, а также указынные нами зависимости:
echo " django-jenkins pep8 pyflakes pylint " >> requirements/tests.txt pip install --upgrade -r requirements/tests.txt
Оригинальный репозиторий проекта находится на GitHub, установить можно с PyPI. Также рекомендую к прочтению туториал.
Добавление и настройка задачи (Job/Project)
Для добавления задачи необходимо нажать на "New Job", выбирать "Build a free-style software project".
В секции "Source Code Management" выбрать чекбокс на "Git Repositories". В "Repository URL" вставляем git@git.example.org:repo-name.git, а в "Branches to build" указываем нужную ветку, например "master".
Теперь настроим "Poll SCM" > "Schedule" (будем каждые 2 минуты проверять изменения в репозитории, если появились изменения, то запускается новый билд):
*/2 * * * *
Добавим секцию для запуска тестов "Build" > "Add build step" > "Execute shell":
# Создаем каталог виртуального окружения virtualenv --no-site-packages --distribute venv # Сделаем symlink на файл настроек для django_jenkins ln -fs $WORKSPACE/project/settings/jenkins.py $WORKSPACE/project/local_settings.py # Устанавливаем зависимости для проекта и тестов venv/bin/pip install --upgrade -r requirements/base.txt venv/bin/pip install --upgrade -r requirements/tests.txt # Запускаем тесты с использованием django_jenkins venv/bin/python ./manage.py jenkins --verbosity=2 --output-dir=reports
Я обычно это всё выделяю в sh-скрипт и кладу в репозиторий, после чего просто указываю его для вызова.
Теперь вы можете запустить задачу вручную, нажав "Build Now". Либо добавить коммит в репозиторий и через 2 минуты самостоятельно запустится задача.
После запуска тестов у вас должен появится каталог $WORKSPACE/reports, где будут хранится разного рода отчеты (xUnit, pep8, pylint и т.д.).
Бекап Jenkins
Я честно пробовал пользоваться плагином для бекапа thinBackup, но у него есть огромная проблема - он останавливает Jenkins для того чтобы сделать бекап.
В итоге я отказался от него и написал простой скрипт, который бекапит только конфиги от задач, а остальное мне и не надо.
cd /var/lib/jenkins/jobs for JOB in *; do BACKUP_JOB_DIR="/var/lib/jenkins/Backups/configs/$JOB" mkdir -p $BACKUP_JOB_DIR cp "$JOB/config.xml" "$BACKUP_JOB_DIR/config.xml"; # git commit =am "Autocommit (`hostname`): `date`" done
Просто создаете еще одну задачу, которая будет переодически бекапить конфиги.
Напоследок
Под строкой поиска находится ссылка ENABLE AUTO REFRESH, нажмите её чтобы автоматически обновлять страницу и сделать Jenkins более интерактивным.
Рекомендую почитать:
- Django и Continuous Integration - рассказывается о Jenkins CI, Shining Panda и Travis CI.
- Настройка Jenkins для django проекта с нуля - Хабрахабр
- HowTo: continuous integration Django в Jenkins с помощью Selenium - Хабрахабр
- Туториал по django-jenkins
Комментарии
Вы рассматривали какие-нибудь альтернативы помимо splinter? Использовать Selenium WebDriver или что-то совершенно другое.
Что для вас было решающим при определении выбора splinter?
splinter это обвязка вокруг селениума и других драйверов, как zope.testbrowser
Я выбрал splinter потому что он мне показался самым удобным и популярным
Как насчет (CasperJS + PhantomJS + GPU)
А сравнительные тесты перформанса где посмотреть?
вы явно делаете что то не так, настройка доступа к git производится на вкладке Credentials
@ALex_hha, для настройки нод использую Credentials, но для настройки гита это неудобно, потому что часто приходится вручну обновлять гит из консоли. Мой вариант закрывает сразу две потребности
Оставьте свой комментарий