Jenkins
Self-hosted сервер автоматизации: пайплайны как код в Jenkinsfile, которые выполняются на ваших агентах.
За 30 секунд
Jenkins — это open-source сервер автоматизации, который вы устанавливаете и эксплуатируете сами. Jenkinsfile в репозитории описывает пайплайн на языке на основе Groovy: стадии вроде build, test и deploy, которые выполняются на рабочих машинах — агентах. Огромная экосистема плагинов связывает его почти с любым инструментом, поэтому он до сих пор распространён в крупных компаниях, но обновления, безопасность и масштабирование — на вас.
Не в моём ежедневном стеке — я понимаю принципы и изучу детали, когда это понадобится проекту.
Почему это важно
- Он целиком работает на вашей инфраструктуре, поэтому подходит для изолированных сетей, регулируемых отраслей и сборок, которым нужно особое железо.
- Около двух тысяч плагинов интегрируют его почти с любой SCM, облаком, тестовым инструментом и чатом, так что он может оркестрировать необычные или legacy-процессы.
- Многие давно существующие компании годами гоняют пайплайны на Jenkins; понимать его часто нужно, чтобы их поддерживать или мигрировать.
- Поминутной оплаты нет: вы платите за машины, которые контролируете, и при очень тяжёлой постоянной нагрузке это может быть дешевле.
Как это работает
01Контроллер, агенты и executor-ы
У инсталляции Jenkins есть контроллер: веб-интерфейс, конфигурация, планирование job-ов и хост для плагинов. Работа должна выполняться на агентах — отдельных машинах или контейнерах, которые подключаются к контроллеру и запускают сборки. Каждый агент предоставляет несколько executor-ов — слотов, в каждом из которых может идти одна сборка. Метки вроде linux, docker или arm64 описывают агентов, и пайплайн запрашивает метку, а не конкретную машину. Агенты бывают постоянными (VM, подключённая по SSH или входящим соединением) или эфемерными: плагин Kubernetes запускает под на каждую сборку, а облачные плагины поднимают VM по запросу и потом удаляют их. Представьте контроллер как диспетчера в таксопарке, а агентов — как машины. Ключевое правило: выставьте число executor-ов на самом контроллере в ноль, чтобы недоверенный код сборки никогда не выполнялся на машине, где хранятся все credentials и настройки.
02Безопасная установка Jenkins
У Jenkins есть еженедельная линейка релизов и линейка Long-Term Support (LTS), которую раз в двенадцать недель выбирают из еженедельного релиза и патчат ради стабильности; в production должен работать LTS. Текущие LTS-релизы требуют Java 21 или Java 25. Официальный Docker-образ jenkins/jenkins — самый простой старт: зафиксируйте LTS-тег с суффиксом JDK, сохраните volume jenkins_home (там живут вся конфигурация и история) и поставьте его за reverse proxy с HTTPS. При первом запуске Jenkins печатает в лог начальный пароль администратора, а мастер настройки предлагает рекомендуемые плагины. Ставьте только то, что нужно: каждый плагин — это код со своей историей уязвимостей. Включите authentication realm (LDAP, SAML или OIDC через плагины) и matrix- или role-based авторизацию; никогда не оставляйте анонимным пользователям никаких прав на запись.
# compose.yaml - Jenkins controller, data in a named volume
services:
jenkins:
image: jenkins/jenkins:lts-jdk21
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080" # expose via a reverse proxy with TLS
- "50000:50000" # inbound agents
volumes:
- jenkins_home:/var/jenkins_home
volumes:
jenkins_home:
03Declarative pipeline: Jenkinsfile
Pipeline as code означает, что сборка описана в Jenkinsfile, который хранится вместе с кодом, проходит ревью в pull request-ах и версионируется как всё остальное. Declarative-синтаксис — рекомендуемый стиль: фиксированная структура из pipeline, agent, stages и steps плюс необязательные секции options, environment, when и post. Его проще читать и валидировать, чем свободный Groovy. Пример собирает и тестирует Node.js-приложение внутри Docker-контейнера на любом агенте с меткой docker, хранит только последние двадцать сборок, прерывается по таймауту через тридцать минут, публикует результаты тестов JUnit и всегда чистит workspace. Job типа Multibranch Pipeline сканирует репозиторий и создаёт пайплайн для каждой ветки и pull request-а с Jenkinsfile, так что новые ветки получают CI автоматически.
pipeline {
agent { docker { image 'node:24-alpine'; label 'docker' } }
options {
timeout(time: 30, unit: 'MINUTES')
buildDiscarder(logRotator(numToKeepStr: '20'))
disableConcurrentBuilds()
}
stages {
stage('Install') { steps { sh 'npm ci' } }
stage('Test') {
steps { sh 'npm test -- --reporter=junit --outputFile=reports/junit.xml' }
post { always { junit 'reports/*.xml' } }
}
stage('Build') { steps { sh 'npm run build' } }
}
post { always { cleanWs() } }
}
04Credentials и стадии деплоя
Jenkins хранит секреты в своём хранилище credentials, зашифрованными на диске, с глобальной областью видимости или на уровне папки, чтобы команды видели только свои. Пайплайны получают к ним доступ через withCredentials или хелпер credentials() в блоке environment, и Jenkins маскирует значения в выводе консоли. Предпочитайте credentials уровня папки, а ещё лучше — внешний менеджер секретов, например HashiCorp Vault или облачный secret manager через плагин. Стадия деплоя обычно запускается только на ветке main, ждёт человека через input и использует lock или milestone, чтобы пересекающиеся сборки не могли задеплоиться не по порядку. Обратите внимание на одинарные кавычки вокруг shell-скрипта ниже: тогда Groovy не подставляет секрет в строку команды, а shell читает его из окружения — так он не попадает в логи и списки процессов.
stage('Deploy') {
when { branch 'main' }
steps {
input message: 'Deploy to production?', ok: 'Deploy'
withCredentials([sshUserPrivateKey(
credentialsId: 'deploy-ssh-key',
keyFileVariable: 'SSH_KEY',
usernameVariable: 'SSH_USER')]) {
sh '''
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=yes \
"[email protected]" "./deploy.sh $GIT_COMMIT"
'''
}
}
}
05Плагины и shared libraries
Большинство возможностей Jenkins — это плагины: Git, Pipeline, credentials, Docker, Kubernetes-агенты, отчёты тестов. Эта гибкость — и главный эксплуатационный риск. Плагины зависят друг от друга и от версий ядра, и устаревший плагин — частый источник security advisories. Держите список коротким, регулярно обновляйтесь — сначала на staging-Jenkins — и читайте страницу security advisories. Для переиспользования между множеством репозиториев Shared Libraries хранят Groovy-код в отдельном Git-репозитории: функции из vars/ становятся шагами пайплайна, так что десятки Jenkinsfile могут вызывать один стандартный buildNodeApp() вместо копирования логики. Загружайте библиотеки с зафиксированной версией: @Library('[email protected]') _. Configuration as Code (плагин JCasC) описывает сам контроллер — включая безопасность, агентов и ссылки на credentials — в YAML, так что сломанный Jenkins можно пересобрать из Git, а не по памяти.
Когда применять
- Сборки должны полностью выполняться on-premises или в изолированных сетях без внешнего SaaS.
- У вас уже большой парк Jenkins, и его нужно поддерживать, укреплять или постепенно модернизировать.
- Необычная оркестрация: тесты hardware-in-the-loop, специальные сборочные машины или интеграции, которые есть только в плагинах Jenkins.
- Очень большой постоянный объём сборок, при котором владеть машинами дешевле поминутной оплаты.
Когда не стоит
- Новый проект на GitHub или GitLab со стандартными потребностями — встроенный CI проще эксплуатировать и защищать.
- Маленькая команда, где некому отвечать за обновления контроллера, плагинов и бэкапы.
- Вам нужна нулевая инфраструктура — Jenkins всегда что-то, что вы эксплуатируете сами.
Альтернативы
- GitHub Actions
Hosted CI, интегрированный с GitHub, без контроллера, который надо обслуживать.
- GitLab CI/CD
Встроен в GitLab, сильный self-managed вариант с раннерами в вашей сети.
- Buildkite
Hosted control plane с агентами на ваших машинах — частая замена Jenkins.
- Tekton / Argo Workflows
Kubernetes-native пайплайны для команд, которые строят собственную платформу.
Лучшие практики
- Используйте линейку LTS и проверяйте обновления ядра и плагинов сначала на staging-контроллере.
- Выставьте executor-ы контроллера в ноль и запускайте все сборки на агентах, лучше эфемерных.
- Используйте Declarative Pipeline в Jenkinsfile и Multibranch-job-ы вместо freestyle-job-ов, настроенных через UI.
- Управляйте конфигурацией контроллера через Configuration as Code и бэкапьте jenkins_home.
- Ограничивайте credentials папками, используйте shell-строки в одинарных кавычках для секретов и предпочитайте внешний менеджер секретов.
- Держите список плагинов минимальным и быстро реагируйте на security advisories Jenkins.
- Выносите переиспользуемую логику в версионированные Shared Libraries, оставляя Jenkinsfile короткими и декларативными.
Частые ошибки
- Запускать сборки на контроллере, давая любому пайплайну доступ ко всем секретам и конфигурации.
- Ставить десятки плагинов, о которых никто не помнит, а потом не иметь возможности обновить ядро из-за них.
- Подставлять секреты в Groovy-строки в двойных кавычках, из-за чего они могут утечь в логи и списки процессов.
- Выставлять Jenkins прямо в интернет без HTTPS, SSO и строгой авторизации.
- Настраивать всё кликами в UI без бэкапа, так что отказ диска означает восстановление по памяти.
Глубже
06Как пайплайны переживают перезапуски: CPS и durability
Код пайплайна выполняется не как обычный Groovy. Jenkins преобразует его в continuation-passing style (CPS), чтобы состояние работающего пайплайна можно было сохранить на диск и продолжить после перезапуска контроллера. Поэтому длинные сборки могут пережить обновление — и поэтому же часть обычного Groovy ломается: несериализуемые объекты, некоторые замыкания и итераторы вызывают NotSerializableException. Методы с пометкой @NonCPS выполняются как обычный Groovy, но не могут вызывать шаги пайплайна. Помните также, что Groovy в Jenkinsfile выполняется на контроллере, а не на агенте; на агентах выполняются только шаги вроде sh. Тяжёлый парсинг или циклы в Groovy нагружают контроллер, поэтому выносите такую работу в shell-скрипты или инструменты, которые запускаются на агентах. Настройка durability (например, PERFORMANCE_OPTIMIZED) жертвует гарантиями возобновления ради меньшего дискового I/O на загруженных контроллерах.
07Эфемерные агенты в Kubernetes
Плагин Kubernetes запускает под на каждую сборку из pod template и подключает его как агента; под удаляется по окончании сборки. Каждый контейнер в поде может содержать свой тулчейн, а container() переключает между ними внутри стадии. Плюсы: каждый раз чистый workspace, никаких уникальных «снежинок» среди сборочных машин и масштабирование вместе с кластером. Следите за деталями: задавайте requests по CPU и памяти, чтобы сборки не отнимали ресурсы друг у друга, запускайте сборки не от root и не монтируйте Docker-сокет хоста — это даёт сборке root на ноде; вместо этого используйте rootless-сборщики в стиле Kaniko или BuildKit. Кэши зависимостей исчезают вместе с подом, поэтому используйте persistent volume, прокси-репозиторий вроде Nexus или Artifactory или build cache на базе registry, чтобы сборки оставались быстрыми.
pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: node
image: node:24-alpine
command: [sleep]
args: [infinity]
resources:
requests: { cpu: "500m", memory: 1Gi }
'''
defaultContainer 'node'
}
}
stages { stage('Test') { steps { sh 'npm ci && npm test' } } }
}
08Защита контроллера Jenkins
У Jenkins длинный список исторических уязвимостей, в основном в плагинах, и атакующие активно сканируют открытые инстансы. Защита в несколько слоёв: обновляйте ядро и плагины по опубликованным security advisories; ставьте контроллер за reverse proxy с TLS и желательно за VPN или SSO; используйте matrix- или role-based авторизацию без анонимного доступа; отключите CLI через remoting и неиспользуемые протоколы; оставьте script console только администраторам — она выполняет произвольный Groovy с полными правами контроллера. Плагин Script Security изолирует Groovy пайплайнов в песочнице и требует одобрения администратора для опасных методов; не одобряйте сигнатуры бездумно. Контролируйте, какие репозитории могут определять пайплайны и какие credentials доступны каждой папке: вредоносное изменение Jenkinsfile в pull request может напечатать любой credential, доступный job-у. Настройте trusted sources, чтобы pull request-ы из форков не могли менять Jenkinsfile, используемый для их сборки.
Реальные ситуации
Обновление Jenkins блокируют старые плагины
Проблема
Контроллер на два года отстаёт по ядру и содержит 140 плагинов. Обновление ядра ломает несколько плагинов, и никто не знает, какие из них используются.
Как решить
Сделайте бэкап jenkins_home и разверните его копию как staging-контроллер. Выгрузите список плагинов и по данным об использовании (плагин Plugin Usage или grep по конфигам job-ов) найдите неиспользуемые; удалите их. Сначала обновите Java до 21, затем ядро — шагами по LTS-релизам, на каждом шаге обновляя плагины через plugin installation manager tool. Почините или замените заброшенные плагины. Зафиксируйте результат через Configuration as Code и plugins.txt в Git, затем соберите production-контроллер из этого образа. После этого запланируйте ежемесячные обновления LTS, чтобы отставание больше не росло.
Сборки случайно падают на общих агентах
Проблема
На одном и том же коммите сборки то проходят, то падают — в зависимости от того, какой постоянный агент их запускает. У агентов разные версии инструментов и оставшиеся файлы.
Как решить
Перенесите сборки в контейнеры: используйте agent { docker { image 'node:24-alpine' } } или pod templates Kubernetes, чтобы тулчейны были описаны в Jenkinsfile и везде одинаковы. Собирайте образы агентов в CI, с версиями и сканированием. Чистите workspace через cleanWs() в post и перестаньте полагаться на файлы вне workspace. Замените постоянных агентов эфемерными из плагина Kubernetes или облачного плагина. Оставьте один постоянный агент только там, где нужно особое железо, и управляйте его конфигурацией через Ansible, чтобы его можно было пересобрать.
Миграция с Jenkins на GitHub Actions
Проблема
Компания переносит код на GitHub и хочет отказаться от Jenkins, но у неё 80 пайплайнов с Shared Libraries и кастомными плагинами.
Как решить
Проведите инвентаризацию пайплайнов и сгруппируйте их по паттернам — большинство сведутся к нескольким формам. Переведите функции Shared Library в reusable workflows и composite actions. GitHub Actions Importer может сделать черновые воркфлоу из Jenkinsfile и ускорить механическую часть. Пересоздайте credentials как секреты environment или, лучше, как доверие через OIDC. Сначала мигрируйте пилотную группу и несколько недель держите обе системы параллельно, сравнивая результаты. Оставьте Jenkins только для job-ов, которым нужно on-premises железо, или подключите их через self-hosted раннеры, а затем выключите контроллер после финального бэкапа.
Шпаргалка
- Запустить Jenkins LTS в Docker
docker run -d -p 127.0.0.1:8080:8080 -v jenkins_home:/var/jenkins_home jenkins/jenkins:lts-jdk21- Начальный пароль администратора
docker exec <container> cat /var/jenkins_home/secrets/initialAdminPassword- Запуск только в main
when { branch 'main' }- Таймаут сборки
options { timeout(time: 30, unit: 'MINUTES') }- Хранить последние 20 сборок
options { buildDiscarder(logRotator(numToKeepStr: '20')) }- Использовать secret text
environment { API_TOKEN = credentials('api-token') }- Загрузить shared library
@Library('[email protected]') _- Параллельные стадии
parallel { stage('Unit') {...} stage('Lint') {...} }- Установить плагины в образ
jenkins-plugin-cli --plugin-file plugins.txt- Проверить Jenkinsfile
Pipeline Linter: POST $JENKINS_URL/pipeline-model-converter/validate
Вопросы и ответы
Q1Чем контроллер отличается от агента?
Контроллер — это мозг: он обслуживает веб-интерфейс, хранит конфигурацию, credentials и историю сборок, планирует сборки и запускает плагины. Агенты — это рабочие, которые подключаются к контроллеру и реально выполняют шаги сборки. У агента несколько executor-ов, каждый может выполнять одну сборку. Сборки выбирают агентов по метке, поэтому job, которому нужен Docker, просит docker, а не имя хоста. Сам контроллер не должен запускать сборки, потому что скрипты сборки — это код из репозиториев, и он может прочитать секреты контроллера или повредить его конфигурацию. Современные установки используют эфемерных агентов — под Kubernetes или облачную VM на каждую сборку, — которые каждый раз чистые и масштабируются по спросу.
Q2Declarative или scripted pipeline?
У declarative pipeline фиксированная структура: pipeline, agent, stages, steps, post, when, options. Jenkins может провалидировать его до запуска, представления в стиле Blue Ocean его понимают, и всей команде его проще читать. Scripted pipeline — это обычный Groovy внутри блоков node: максимум гибкости, но его легко превратить в нечитаемую программу с тонкими проблемами CPS-сериализации. Мой выбор по умолчанию — Declarative для каждого Jenkinsfile и блок script только для небольшой неизбежной логики. Когда нужна настоящая логика, ей место в Shared Library с тестами, которую вызывает короткий Declarative Jenkinsfile, а не во встроенном Groovy в каждом репозитории.
Q3Как работать с секретами в пайплайнах Jenkins?
Храните их в хранилище credentials Jenkins с областью видимости папки той команды, которой они нужны, или получайте из внешнего менеджера вроде Vault через плагин. В пайплайнах привязывайте их только там, где нужно, через withCredentials или credentials() в блоке environment — это заодно маскирует их в логах. Используйте sh-строки в одинарных кавычках, чтобы переменную раскрывал shell, а не Groovy; Jenkins даже предупреждает о небезопасной интерполяции. Никогда не выводите секреты через echo и не передавайте их аргументами команд, где они видны в списке процессов. Ограничьте, кто может настраивать job-ы: любой, кто может редактировать пайплайн, может напечатать доступный ему credential. И периодически проводите аудит использования credentials.
Q4Как годами держать инстанс Jenkins в поддерживаемом состоянии?
Относитесь к нему как к любому production-сервису. Используйте LTS и обновляйтесь по графику, репетируя на staging-контроллере, восстановленном из свежего бэкапа. Плагинов должно быть мало, и они должны быть актуальными; неиспользуемые удаляйте. Описывайте контроллер через Configuration as Code в Git, чтобы его можно было пересобрать; job-ы берутся из Jenkinsfile через Multibranch или organization folders, а не из UI. Бэкапьте jenkins_home или хотя бы те части, которые нельзя воспроизвести из Git. Используйте эфемерных агентов из образов, собранных в CI, чтобы инструменты агентов тоже были версионированы. Мониторьте место на диске, длину очереди и загрузку executor-ов и удаляйте старые сборки и артефакты через logRotator, чтобы диск не заполнился незаметно.
Q5Что такое Multibranch Pipeline?
Multibranch Pipeline — это тип job-а, который указывает на репозиторий и автоматически создаёт под-job для каждой ветки, а при желании и каждого pull request-а, в которых есть Jenkinsfile. Когда создаётся ветка, Jenkins обнаруживает её через webhook или периодическое сканирование и начинает сборку; когда ветку удаляют, под-job тоже удаляется. С условиями when { branch 'main' } или changeRequest() один Jenkinsfile может вести себя по-разному для feature-веток, pull request-ов и main. Organization Folder поднимается на уровень выше и сканирует целую организацию GitHub или группу GitLab, создавая Multibranch-job-ы для каждого репозитория с Jenkinsfile.
Вопросы уровня senior
S1Когда вы бы сегодня всё ещё выбрали Jenkins для новой платформы?
По умолчанию — редко, но честные случаи есть. Изолированные или строго регулируемые среды, где SaaS control plane не разрешён, а организация уже хорошо эксплуатирует Jenkins. Сложные процессы с железом, например embedded- или device-тестирование, которые зависят от конкретных плагинов или настроек агентов. Очень большие сборочные фермы, где опытная команда эксплуатирует Jenkins с Kubernetes-агентами, JCasC и Shared Libraries дешевле, чем hosted-минуты. Против: эксплуатационная нагрузка, риски безопасности плагинов, специфичные навыки Groovy и более слабый developer experience, чем у встроенного CI. Если код в GitLab или GitHub Enterprise Server, их CI с self-hosted раннерами обычно даёт тот же on-premises контроль при меньшей поддержке, так что Jenkins я бы выбрал только с ясной причиной и командой, которая будет им владеть.
S2Как масштабировать Jenkins на сотни команд?
Один гигантский контроллер становится единой точкой отказа и узким местом по производительности, потому что логика Groovy-пайплайнов, UI и плагины работают там. Распространённый паттерн — несколько контроллеров, разделённых по бизнес-подразделениям или уровню доверия, каждый управляется одинаково через Configuration as Code и выверенный образ с плагинами, возможно с CloudBees CI или похожими инструментами для централизованного управления. Агенты — эфемерные поды или VM с автомасштабированием. Shared Libraries дают стандартные шаги пайплайна, версионированные и протестированные. Команды получают папки с ограниченными credentials и правами. Мониторинг покрывает время в очереди, загрузку executor-ов, heap контроллера и диск. Компромисс — накладные расходы на управление парком против радиуса поражения: больше контроллеров — больше обновлений, которые надо оркестрировать, зато плохой плагин или вышедший из-под контроля job затрагивает только часть организации.
S3Pull request меняет Jenkinsfile так, чтобы напечатать credentials. Как это предотвратить?
Нужно сочетание нескольких мер. В конфигурации Multibranch настройте доверие так, чтобы pull request-ы из форков или от участников без прав на запись собирались с Jenkinsfile из целевой ветки, а не со своим изменённым. Ограничьте credentials папками и вообще не давайте сборкам pull request-ов production credentials; deploy credentials привязываются только в стадиях, которые выполняются в main. Маскирование помогает, но легко обходится кодированием, так что мерой контроля оно не является. Держите Groovy-песочницу включённой и не одобряйте рискованные сигнатуры скриптов. Требуйте код-ревью для изменений Jenkinsfile и Shared Library через branch protection. Для самых ценных секретов используйте короткоживущие credentials из Vault, чтобы даже утечка быстро истекала. Базовый принцип: любой, кто может менять код пайплайна, может использовать любой секрет, до которого этот пайплайн дотягивается.
Глоссарий
- Controller
- Центральный сервер Jenkins: UI, конфигурация, планирование и плагины. Раньше назывался master.
- Agent
- Машина или контейнер, который подключается к контроллеру и выполняет шаги сборки.
- Executor
- Слот на агенте, в котором одновременно выполняется одна сборка.
- Jenkinsfile
- Файл в репозитории, описывающий пайплайн как код.
- Declarative Pipeline
- Структурированный рекомендуемый синтаксис Jenkinsfile с блоками pipeline, agent, stages и steps.
- Shared Library
- Git-репозиторий переиспользуемых Groovy-шагов, который подгружают многие Jenkinsfile.
- JCasC
- Configuration as Code: описывает настройки контроллера в YAML, чтобы его можно было пересобрать из Git.
- LTS
- Линейка релизов Long-Term Support, выбирается раз в двенадцать недель и патчится ради стабильности.
- Multibranch Pipeline
- Job, создающий пайплайн для каждой ветки или pull request-а с Jenkinsfile.
Что изучить дальше
Есть идея продукта?Давайте создадим его.
Расскажите о своей идее, продукте или команде. Обычно отвечаю в течение одного рабочего дня.