Для справки: на момент написания актуальная Jenkins LTS - 2.541.1 (январь 2026), а сам Jenkins требует уже Java 17 или новее, скоро минимум сместится на 21. Все примеры ниже - валидный declarative Jenkinsfile, это стандарт 2026 года.
Где выполняется конвейер: jenkins agent и его виды
Блок agent говорит контроллеру, на какой машине (или в каком контейнере) развернуть рабочую область и гонять шаги. Объявить его можно на двух уровнях: глобально для всего pipeline и локально внутри отдельного stage. Локальный перекрывает глобальный - это и есть главный рычаг управления.
Самые ходовые варианты jenkins agent:
- agent any - бери любого свободного исполнителя. Годится для проб, но в проде так почти не пишут: непредсказуемо.
- agent { label 'linux && docker' } - выбери агента по меткам. Метки навешиваются на узлы в настройках. Выражение поддерживает && и ||.
- agent none на уровне pipeline - глобального агента нет, каждый stage объявляет свой. Удобно, когда стадии разнородные.
- agent { docker { ... } } - подними контейнер и выполни шаги внутри него.
- agent { kubernetes { ... } } - закажи эфемерный pod-агент в кластере (об этом ниже).
Код: Выделить всё
pipeline {
agent none
stages {
stage('Build') {
agent { label 'linux && jdk17' }
steps {
sh 'mvn -B clean package'
}
}
stage('Test') {
agent { label 'linux && jdk17' }
steps {
sh 'mvn -B test'
}
}
}
}

docker jenkins: запуск стейджа в контейнере
Связка docker jenkins - один из самых частых способов получить чистое, воспроизводимое окружение без зоопарка тулчейнов на самих узлах. Вместо того чтобы ставить Node, Python и Go на каждый агент, ты говоришь: "выполни эту стадию внутри образа node:22". Главное условие - на узле должен быть установлен Docker, а сам узел иметь метку, по которой Jenkins его найдет.
Код: Выделить всё
pipeline {
agent {
docker {
image 'node:22-alpine'
label 'docker'
args '-v /tmp/npm-cache:/root/.npm'
}
}
stages {
stage('Install') {
steps {
sh 'node --version'
sh 'npm ci'
}
}
stage('Build') {
steps {
sh 'npm run build'
}
}
}
}
Можно задать docker-агента и для одной стадии, оставив остальные на обычных узлах:
Код: Выделить всё
stage('Lint') {
agent {
docker { image 'python:3.13-slim'; label 'docker' }
}
steps {
sh 'pip install ruff && ruff check .'
}
}
Если у тебя кластер, docker jenkins на статичных узлах часто заменяют на agent { kubernetes }. Тут под каждый билд kubernetes-плагин создает отдельный pod, гонит в нем шаги и удаляет его по завершении. Никаких "грязных" агентов с остатками прошлых сборок - каждый запуск с чистого листа.
Код: Выделить всё
pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: maven
image: maven:3.9-eclipse-temurin-21
command: [sleep]
args: ["infinity"]
'''
}
}
stages {
stage('Build') {
steps {
container('maven') {
sh 'mvn -B clean verify'
}
}
}
}
}
Блок environment и jenkins переменные
Теперь про окружение. Блок environment задает переменные окружения - те самые jenkins env, которые видны в sh, bat и в шагах. Объявить его можно на уровне pipeline (видно всем стадиям) или внутри stage (видно только ей). Обращаться к значению можно двумя путями: внутри Groovy - через env.NAME, а внутри shell-команды - через обычную интерполяцию ${NAME}.
Код: Выделить всё
pipeline {
agent any
environment {
APP_NAME = 'cyberlake-api'
BUILD_TAG = "${env.BUILD_NUMBER}-${env.GIT_COMMIT.take(7)}"
}
stages {
stage('Info') {
environment {
STAGE_REGION = 'eu-central'
}
steps {
echo "Сборка ${env.APP_NAME}, тег ${env.BUILD_TAG}"
sh 'echo "Region: $STAGE_REGION, workspace: $WORKSPACE"'
}
}
}
}
Про встроенные jenkins переменные. Jenkins сам наполняет окружение кучей полезных значений, главные из них:
- env.BUILD_NUMBER - порядковый номер сборки.
- env.GIT_COMMIT - хеш коммита (появляется после checkout).
- env.WORKSPACE - абсолютный путь к рабочей области на агенте.
- env.JOB_NAME, env.BUILD_URL - имя задачи и ссылка на сборку.
Секреты в environment через credentials()
Хардкодить пароли в Jenkinsfile - грубейшая ошибка безопасности. Правильный путь - функция credentials() в блоке environment. Она достает секрет из хранилища Jenkins по ID и кладет его в переменную, маскируя значение в логах звездочками.
Код: Выделить всё
pipeline {
agent any
environment {
REGISTRY = 'registry.cyberlake.ru'
DOCKER_CREDS = credentials('docker-registry')
SONAR_TOKEN = credentials('sonar-token')
}
stages {
stage('Push') {
steps {
sh '''
echo "$DOCKER_CREDS_PSW" | docker login "$REGISTRY" \
-u "$DOCKER_CREDS_USR" --password-stdin
docker push "$REGISTRY/app:${BUILD_NUMBER}"
'''
}
}
}
}
Типичные грабли
- Перепутаны ${env.X} (Groovy) и $X (shell). В sql '...' с одинарными кавычками работает только $X, в двойных - подставляет Groovy.
- agent none, а в pipeline остались steps или sh в глобальном post. Выполнять негде - падает на старте.
- Образ для docker есть, а узла с Docker и нужной меткой нет. Билд висит в очереди "ожидает исполнителя".
- В kubernetes-агенте забыт container('...') - шаги уходят в jnlp без инструментов, привет "not found".
- Пытаешься менять env.X присваиванием в середине стадии - в declarative так нельзя. Для динамики используй блок script { env.X = '...' } или библиотечный шаг.
- Ждешь env.GIT_COMMIT до шага checkout - переменная еще пустая.
Собери один Jenkinsfile и прогони его руками:
- Сделай pipeline с agent none и двумя стадиями: первая на agent { docker { image 'node:22-alpine'; label 'docker' } }, вторая на любом label.
- В глобальный environment положи APP_NAME и BUILD_TAG, собранный из ${env.BUILD_NUMBER}.
- В одной стадии выведи env.WORKSPACE через echo (Groovy) и через sh с $WORKSPACE - убедись, что значения совпадают.
- Заведи в Jenkins тестовый credential типа secret text и достань его через credentials(). Проверь, что в логе он замаскирован звездочками.
- Бонус: перепиши docker-стадию на agent { kubernetes { yaml ... } }, если есть доступ к кластеру, и оберни шаг в container().
- Чем отличается обращение к переменной как ${env.NAME} от ${NAME}, и в каком контексте работает каждое?
- Сколько переменных и с какими суффиксами создаст credentials() для секрета типа "username with password"?
- Что произойдет, если на уровне pipeline стоит agent none, а внутри pipeline остался блок steps?
- Зачем в kubernetes-агенте нужен шаг container() и что будет, если его не указать?
agent отвечает за "где" (узел по метке, контейнер docker, эфемерный pod в Kubernetes), environment - за "с чем" (переменные, встроенные jenkins env и секреты через credentials). Держи в голове разницу между Groovy- и shell-интерполяцией, не хардкодь пароли и помни, что локальный agent перекрывает глобальный. Этих двух директив хватает, чтобы пайплайн стал предсказуемым и переносимым, а не магией, которая работает только на одной машине.