Управление задачами в Jenkins CI

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

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

Jenkins является бесплатным инструментом, обладающим огромными возможностями в виде тысяч плагинов, которые постоянно добавляются и обновляются.

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

Ниже мы рассмотрим, как упростить и ускорить создание задач в Jenkins.

Инструменты, которые будем использовать

Jenkins job builder

Это python-утилита, позволяющая описывать задачи в YAML- или JSON форматах, которые через HTTP API загружаются на сервер. Для работы достаточно установить ее на локальной машине, написать конфигурацию с подключением и описать требуемые задачи.
Актуальная документация проекта

DSL Plugin

Это плагин для Jenkins, с помощью которого мы сможем описывать задачи на специальном языке (DSL, Domain Specific Language) и создавать их на основе этих описаний. Работает локально на самом Jenkins-сервере.
Страница плагина

Jenkins Pipeline

Это тип задачи в Jenkins, с помощью которого мы можем описать необходимый нам процесс деплоя или сборки приложения по стадиям. Описывать pipeline мы будем в специальном Jenkinsfile, который будет храниться в репозитории проекта.

Gitlab и Gitlab CI

Наиболее популярный и развивающийся сейчас selfhosted проект, который также используется во многих компаниях. Он включает в себя версию для сообщества и энтерпрайз-решение.

Внутри имеется собственный CI, написанный на Go, который через специальные подключаемые агенты (runners) позволяет нам проводить различные стадии сборки и деплоя.

Для начала использования достаточно в репозитории проекта создать файл .gitlab-ci.yml, в котором описать необходимую нам последовательность. Для работы отлично может использовать Docker.

Итак, начнем

Для начала поставим необходимые инструменты.

Jenkins-job-builder в нашем случае необходимо установить как на локальную машину, так и на машину, где будет работать агент gitlab. Можно обойтись просто локальной машиной gitlab.

Локально на linux и mac установим последнюю доступную версию:

pip install jenkins-job-builder
brew install jenkins-job-builder

Локально для описания нашего seed job мы сможем протестировать его работоспособность.

Добавлю, что seed job в терминологии DSL Plugin является задача типа Free-style project, которая создает остальные задачи из DSL-скриптов.

На gitlab-сервере установка будет ничем не отличаться от локальной установки на linux-машину:

pip install jenkins-job-builder

На всякий случай запустим утилиту и проверим, что она работает:

➜  ~ jenkins-jobs
usage: jenkins-jobs [-h] [--conf CONF] [-l LOG_LEVEL] [--ignore-cache]
                	[--flush-cache] [--version] [--allow-empty-variables]
                	[--user USER] [--password PASSWORD]
                	{update,test,delete,delete-all} ...

Установка плагина

Теперь установим плагин в Jenkins. Для установки можно воспользоваться как классическим UpdateCenter в разделе Manage Jenkins → Manage Plugins → Available, так и используя установку с помощью curl/wget, скачав плагин с официальной страницы плагина:

  • Версия gitlab — 9.4.4-ce.0
  • Версия gitlab-ci-multi-runner — 9.4.2

Создание репозитория

Создадим репозитории под код с описанием задач.

Для начала подготовим наш seed job — так называется задача, которая генерирует из себя все остальные. Репозиторий будет состоять из следующих файлов:

jenkins.ini

[job_builder]
ignore_cache=True
keep_descriptions=False
include_path=.
recursive=False
allow_duplicates=False

[jenkins]
user=username
password=secret-pass
url=http://example-ci.org

job-generator.yml

---
- job:
	name: job-generator
	description: 'Do not edit this job through the web!'
	scm:
  	- git:
      	url: git@gitlab-selfhosted.org:group/repo-dsl.git
      	branches:
        	- origin/master
      	credentials-id: some-credentials-id
      	timeout: 20
      	basedir: dsl
	triggers:
  	- gitlab:
      	trigger-push: true
      	trigger-merge-request: false
      	trigger-open-merge-request-push: both
      	ci-skip: false
      	set-build-description: false
      	add-note-merge-request: false
      	add-vote-merge-request: false
      	add-ci-message: true
      	allow-all-branches: true
      	branch-filter-type: RegexBasedFilter
      	target-branch-regex: '.*master*.'
	builders:
  	- dsl:
      	target: "dsl/*.groovy"
      	ignore-existing: "false"
      	removed-job-action: "DISABLE"
      	removed-view-action: "DELETE"
      	lookup-strategy: "SEED_JOB"

.gitlab-ci.yml

job:
  script: 'jenkins-jobs --conf jenkins.ini update job-generator.yml'

Подключим gitlab-runner к нашему проекту. Поскольку интерфейс gitlab последнее время часто меняется, то рекомендуем обращаться к официальной документации.

После подключения он будет выглядеть примерно так:

Теперь закоммитим изменения и в разделе Jobs у нашего проекта увидим следующее:

Вся схема выглядит следующим образом:

Подготовим репозиторий с описанием задач. Создадим его с названием repo-dsl и плоской структурой. Все файлы располагаются как есть.
Положим в него файл с нашим pipeline:

pipelineJob('testpipeline-build') {
	description('Build test docker image, test and push it to local registry')
	definition {
    	cpsScm {
      	scm {
        	git {
          	branch('origin/master')
          	remote {
            	url('git@gitlab-selfhosted.org:test-group/sample-project.git')
            	credentials('gitlab-creds')
          	}
        	}
      	}
      	scriptPath('Jenkinsfile')
    	}
  	}
}

И настроим webhook по запуску seed-job, который мы создали ранее.

В данной версии gitlab webhooks располагаются в проекте в разделе Settings → Integrations.

Создаем webhook на push (gitlab:pass@ci.org/project/job-generator). Схематично это выглядит вот так:

Теперь в нашем Jenkins есть две задачи:

  1. Задача-генератор seed-job
  2. Задача типа pipeline с названием testpipeline-build.

В тестовом pipeline мы будем собирать docker-образ, который после будет загружаться в локальный docker registry. Файл с образом будет браться с другого проекта.

В проекте с названием sample-project создадим Jenkinsfile следующего содержания:

node {
	def app

	stage('Clone repository') {

    	checkout([$class: 'GitSCM', branches: [[name: '*/master']],
    	doGenerateSubmoduleConfigurations: false, extensions: [],
    	submoduleCfg: [], userRemoteConfigs:
    	[[credentialsId: 'gitlab-repo-creds',
    	url: 'git@gitlab-selfhosted.org:test-group/docker-image-file.git']]])
	}
	stage('Build image') {
    	app = docker.build("docker-image")
	}

	stage('Test image') {
       	sh '''
       	if ! docker inspect docker-image &> /dev/null; then
          	echo 'docker-image does not exist!'
          	exit 1
       	fi
       	'''
	}

	stage('Push image') {

    	echo 'Push image'
    	docker.withRegistry('https://local-registry:9666', 'registry-creds') {
        	app.push("${env.BUILD_NUMBER}")
        	app.push("latest")
    	}
	}

	stage('Clean existing image') {
  	sh "docker rmi docker-image"
	}
}

В итоге готовый pipeline будет выглядеть следующим образом в интерфейсе blueocean в Jenkins:

Или так в классическом интерфейсе:

Для запуска этой задачи автоматически мы можем добавить webhook в проекте с docker-image, где после отправки изменений в репозиторий будет запускаться эта задача.

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