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

Сегодня я расскажу как реализовать непрерывное тестирование Django-проекта с помощью Jenkins CI. Мы также будем собирать и выводить разного рода отчеты по тестам, для этого мы установим и настроим дополнительные плагины. Следующие статьи будут о том, как развернуть Jenkins-ноды на FreeBSD, как запустить BDD-тесты с поддержкой Selenium WebDriver через Lettuce и splinter.
В качестве поставщика тестируемого кода будем использовать git, в качестве транспорта для него будем использовать ssh (у меня для этого используется gitolite), адрес сервера будет git.example.org. Адрес сервиса с запущенным Jenkins будет http://jenkins.example.org/.
Ранее, я уже упоминал о 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 - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian 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" (прямой адрес http://jenkins.example.com:8090/systemInfo ).
Теперь произведем базовые настройки Jenkins, перейдите в "Manage Jenkins" > "Configure System":
В секции
Jenkins Locationукажем следующее:- В поле
Jenkins URLукажите адресhttps://jenkins.example.org/; - В поле
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 http://localhost:8090;
}
}
server {
listen 80;
server_name jenkins.example.org;
return 301 https://jenkins.example.org$request_uri;
}
Сгенерируем пару логин/пароль для “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.rulesИ добавим в автозагрузку:echo "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 plugin1.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
Комментарии 6
Архив обсуждения. Новые комментарии закрыты.
Вы рассматривали какие-нибудь альтернативы помимо splinter? Использовать Selenium WebDriver или что-то совершенно другое.
Что для вас было решающим при определении выбора splinter?
splinter это обвязка вокруг селениума и других драйверов, как zope.testbrowser
Я выбрал splinter потому что он мне показался самым удобным и популярным
Как насчет (CasperJS + PhantomJS + GPU)
А сравнительные тесты перформанса где посмотреть?
вы явно делаете что то не так, настройка доступа к git производится на вкладке Credentials
@ALex_hha, для настройки нод использую Credentials, но для настройки гита это неудобно, потому что часто приходится вручну обновлять гит из консоли. Мой вариант закрывает сразу две потребности