Из чего собирается pipeline: stages и stage
Базовый кирпич declarative-конвейера - блок stages, а внутри него отдельные stage. Каждый stage - это логически завершенный этап: сборка, тесты, упаковка, деплой. По умолчанию все jenkins stages внутри одного stages выполняются строго сверху вниз, последовательно. Это и хорошо (понятный поток), и плохо (медленно, если этапы независимы).
Вот скелет, к которому привыкает глаз в 2026 году. Declarative pipeline - стандарт по умолчанию, scripted трогаем только там, где нужна динамика.
Код: Выделить всё
pipeline {
agent any
stages {
stage('Build') {
steps {
sh './gradlew clean assemble'
}
}
stage('Test') {
steps {
sh './gradlew test'
}
}
stage('Package') {
steps {
sh './gradlew bootJar'
archiveArtifacts artifacts: 'build/libs/*.jar', fingerprint: true
}
}
}
}
Код: Выделить всё
stage('QA') {
agent { label 'linux' }
stages {
stage('Lint') {
steps { sh 'make lint' }
}
stage('Unit') {
steps { sh 'make test-unit' }
}
}
}

jenkins parallel: запускаем этапы одновременно
Самый честный способ ускорить конвейер - перестать ждать там, где ждать не надо. Если юнит-тесты на разных ОС не зависят друг от друга, нет смысла гонять их в очередь. Блок parallel говорит Jenkins запустить вложенные стейджи jenkins параллельно. Каждая ветка обычно уезжает на свой агент, и они крутятся одновременно.
Код: Выделить всё
stage('Cross-platform tests') {
parallel {
stage('Linux') {
agent { label 'linux' }
steps { sh './run-tests.sh' }
}
stage('Windows') {
agent { label 'windows' }
steps { bat 'run-tests.bat' }
}
stage('macOS') {
agent { label 'macos' }
steps { sh './run-tests.sh' }
}
}
}
Если же ты хочешь, чтобы при первом падении любой ветки остальные немедленно прервались (например, дорогие e2e-прогоны, и нет смысла жечь ресурсы при заведомо красной сборке), включи failFast:
Код: Выделить всё
stage('E2E') {
failFast true
parallel {
stage('Chrome') { steps { sh './e2e.sh chrome' } }
stage('Firefox') { steps { sh './e2e.sh firefox' } }
stage('WebKit') { steps { sh './e2e.sh webkit' } }
}
}
Код: Выделить всё
options { parallelsAlwaysFailFast() }jenkins matrix: декартово произведение осей одним блоком
Теперь представь, что тебе нужно протестировать продукт на трех ОС и трех версиях Java. Руками это 9 одинаковых стейджей. Писать их копипастой - путь к ошибкам. Для этого придумали matrix - он генерирует декартово произведение осей и сам разворачивает все комбинации (ячейки). Это одна из самых недооцененных современных возможностей: jenkins matrix экономит десятки строк и держит конфигурацию в одном месте.
Структура такая: внутри matrix обязательны секции axes (оси) и stages (что выполнять в каждой ячейке). Опционально - excludes (выкинуть невалидные сочетания).
Код: Выделить всё
stage('Matrix build') {
matrix {
axes {
axis {
name 'PLATFORM'
values 'linux', 'windows', 'mac'
}
axis {
name 'JAVA'
values '17', '21', '25'
}
}
excludes {
exclude {
axis { name 'PLATFORM'; values 'mac' }
axis { name 'JAVA'; values '17' }
}
}
agent { label "${PLATFORM}" }
stages {
stage('Test cell') {
steps {
sh 'echo "PLATFORM=$PLATFORM JAVA=$JAVA"'
sh './gradlew test -Pjava=$JAVA'
}
}
}
}
}
Типичные грабли
- Параллелят все подряд. Параллелизм имеет смысл только для независимых задач. Если ветки дерутся за один и тот же ресурс (общая БД, один порт, один артефакт-кэш) - получишь гонки и плавающие падения.
- Не хватает агентов. Объявил 9 ячеек matrix, а исполнителей всего 2 - параллельности не будет, очередь все равно растянется. Считай реальную емкость: число свободных executors и агентов с нужными метками.
- Используют stages внутри parallel неправильно. Внутри parallel идут именно stage-ветки, а уже внутри каждой ветки может быть свой блок stages. Путаница в уровнях вложенности - частая причина ошибок "Expected a stage".
- Забывают про общий post. При падении одной параллельной ветки удобно собрать отчеты во всех. Делай это в post внутри каждого stage, а не в одном месте снаружи.
- Ждут failFast там, где надо собрать всю картину. failFast хорош для дорогих прогонов, но для матрицы совместимости он вреден: ты хочешь видеть ВСЕ красные ячейки, а не первую попавшуюся. Не включай его бездумно.
- Думают, что Blue Ocean покажет красивый граф. Blue Ocean заброшен и держится как наследие. Параллельные ветки и matrix отлично визуализируются в штатном Pipeline-обзоре и в Stage View современных LTS - ветки рисуются столбиками рядом, ячейки матрицы тоже видны по отдельности.
Возьми любой свой проект (или болванку из Build-стейджа выше) и на Jenkins LTS из линейки 2.541.x или новее (2.555.x уже требует Java 21/25):
- Собери линейный конвейер из трех стейджей Build -> Test -> Package и засеки время сборки в Stage View.
- Раздели Test на три параллельные ветки (можно даже на одном агенте через разные и т.п.), запусти и сравни длительность с линейным вариантом.
Код: Выделить всё
sh 'sleep 5; echo linux' - Добавь , сломай намеренно одну ветку (
Код: Выделить всё
failFast true) и посмотри, как остальные обрываются.Код: Выделить всё
sh 'exit 1' - Перепиши параллельный блок через matrix с двумя осями (например PLATFORM и JAVA), добавь один exclude и убедись, что в обзоре ячеек стало на одну меньше.
Код: Выделить всё
parallel: matrix:Код: Выделить всё
strategy.matrixКод: Выделить всё
fail-fastКонтрольные вопросы
- Чем порядок выполнения стейджей внутри одного блока stages отличается от стейджей внутри parallel?
- Какие две секции обязательны внутри matrix и за что отвечает excludes?
- Что делает failFast и в каком случае его НЕ стоит включать?
- Сколько ячеек породит matrix с осями 3 ОС x 4 версии при двух excludes, и почему важно иметь достаточно агентов?
Стейджи задают структуру конвейера и читаются как оглавление сборки. parallel убирает лишнее ожидание и реально режет время там, где этапы независимы. matrix сворачивает десяток однотипных комбинаций в один компактный блок с осями и исключениями. Связка stages + parallel + matrix плюс аккуратный failFast - это и есть современный способ организовать этапы в jenkins pipeline так, чтобы сборка была быстрой, читаемой и предсказуемой.