Резервное копирование для Jenkins
Все, кто занимается задачами CI/CD, так или иначе знают про Jenkins, даже если им посчастливилось не иметь с ним дела.
Этот непотопляемый кадавр продолжает жить и процветать по следующим причинам:
- гибкость настроек (от настраиваемых диалоговых окон до встроенного Groovy);
- архитектура с поддержкой плагинов и обширный набор готовых плагинов на все случаи жизни;
- и самое главное — большой накопленный объём пользовательских проектов, которые проще продолжать развивать и поддерживать в среде Дженкинса, чем пытаться мигрировать на более современные и приятные платформы.
Очевидно, что чем важнее данные внутри Дженкинса для тех, кто им пользуется, тем актуальнее для них резервное копирование этих данных.
Однако здесь возникают два небольших препятствия:
- настройки, временные данные и файлы проектов смешаны внутри рабочего каталога в одну кучу;
- штатного механизма не существует, вместо него есть энное число посторонних костылей, распространяемых в виде плагинов, описаний и копипаст.
Поэтому, взяв за основу один из таких костылей, мы написали свой собственный.
Список предварительных пожеланий к нему был примерно таким:
- резервная копия должна иметь минимальный размер и максимальную скорость создания (например, дамп виртуальной машины или снимок ZFS с сотнями гигабайт проектов не годится);
- после восстановления нам достаточно иметь полностью настроенный сервер без предыдущего состояния — который знает, как выполнять новые job'ы, но ничего не помнящий про старые;
- поскольку настройки имеют текстовый вид, пусть они сохраняются в Git-репозиторий;
- если Jenkins предназначен для автоматического выполнения заданий, пусть самостоятельно выполняет всю работу по своему обслуживанию.
Общая схема:
- в $JENKINS_HOME создаётся Git-репозиторий;
- на Git-сервере для него создаётся origin (далее приводятся настройки для Gitlab);
- в Дженкинсе создаётся периодическое задание, которое сохраняет в Git ключевые файлы;
- для уменьшения размера от плагинов сохраняются только манифесты, при восстановлении плагины скачиваются заново.
Сначала создайте пользователя и репозиторий в Gitlab'e:
- пользователь = jenkins-backup-robot;
- репозиторий = jenkins-configs;
- URL репозитория скопируйте в буфер обмена;
- откройте Repository => Settings => Members;
- назначьте jenkins-backup-robot мантейнером (иначе Gitlab не даст сделать ему первый push в пустой репозиторий).
Теперь идите в командную строку сервера, на котором работает Jenkins:
- нам требуется создать SSH-ключ и Git-репозиторий:
sudo -Hiu jenkins
cd ~
git init
git config --global user.name Jenkins
git config --global user.email "jenkins@$(hostname -f)"
git remote add origin ВСТАВЬТЕ_ЗДЕСЬ_URL_GIT_РЕПОЗИТОРИЯ
test -s ~/.ssh/id_rsa.pub || ssh-keygen
cat ~/.ssh/id_rsa.pub
Созданный SSH-ключ надо импортировать в Gitlab:
- Это делается в разделе Admin => Users => jenkins-backup-robot => Impersonate => Personal Settings => SSH keys.
Создайте в Дженкинсе новое задание:
- Name: Backup Jenkins configs to Git
- Type: Free job
- Label: master
- SCM: None
- Build: Build Periodically
- Schedule: 20 04 * * *
- Build step: Execute Shell
- Command:
#!/bin/sh -e
cd "$JENKINS_HOME"
# Add general configurations, secrets, job configurations, nodes, user content, users and plugins info:
ls -1d *.xml secrets/ jobs/*/*.xml nodes/*/*.xml userContent/* users/*/config.xml \
plugins/*/META-INF/MANIFEST.MF 2>/dev/null | grep -v '^queue.xml$' | xargs -r -d '\n' git add --
# Track deleted files:
LANG=C git status | awk '$1 == "deleted:" { print $2; }' | xargs -r git rm --ignore-unmatch
LANG=C git status | grep -q '^nothing to commit' || {
git commit -m "Automated Jenkins commit at $(date '+%Y-%m-%d %H:%M')"
git push -q -u origin master
}
Пояснения:
- Метка “master” нужна, если у Дженкинса есть slave-узлы. Если их нет, метку в задании можете не указывать. Если они есть, то в списке узлов отредактируйте свойства мастера и в поле “Labels” добавьте “master”. Если вы этого не сделаете, Дженкинс попытается выполнять задание через агентов на всех узлах, а это явно не то, что нам требуется.
- Если для подключения к Git-серверу используется SSH, перед выполнением задания не забудьте подключиться к нему вручную, чтобы git push не завершался с ошибкой из-за StrictHostKeyChecking.
Заключительный шаг в Gitlab'e после успешного git push:
- в свойствах репозитория откройте раздел Members и понизьте уровень доступа для Дженкинса с Maintainer до Developer;
- в Settings => Repository => Protected branches поменяйте для ветки “master” разрешение “Allow to push” с Maintainers на Maintainers+Developers.
Восстановление плагинов:
- Т.к. мы сохраняем только манифесты плагинов, после восстановления из резервной копии нам потребуется просканировать каталог с манифестами и составить список команд для загрузки дистрибутивов:
sudo -Hiu jenkins
cd ~/plugins/
gawk 'BEGIN { RS = "\r\n" }
BEGINFILE { n = v = "" }
ENDFILE { printf "curl -sS -L -O http://updates.jenkins-ci.org/download/plugins/%s/%s/%s.hpi\n", n, v, n }
$1 == "Short-Name:" { n = $2 }
$1 == "Plugin-Version:" { v = $2 }
' ./*/META-INF/MANIFEST.MF > ./download_all_plugins.sh
sh download_all_plugins.sh