В книге Ластера 2018 года этой темы почти нет - тогда подход только-только набирал силу. А в 2026-м декларативная настройка контроллера через jenkins configuration as code - это уже не экзотика, а индустриальный стандарт. Разберёмся, как описать весь Jenkins одним YAML-файлом и забыть про мышку.
Что такое jenkins configuration as code и зачем он нужен
JCasC (Jenkins Configuration as Code) - это плагин, который позволяет описать конфигурацию контроллера в человекочитаемом YAML-файле вместо того, чтобы тыкать по UI. Идея простая: всё, что ты обычно настраиваешь руками в "Manage Jenkins" - системные параметры, агенты и облака, учётные данные, политика безопасности, список плагинов, настройки инструментов - выносится в один файл jenkins.yaml. Этот файл лежит в Git, ревьюится через pull request и применяется автоматически при старте.
Подход jenkins как код даёт три вещи, ради которых всё затевается:
- Воспроизводимость. Поднял контейнер - получил полностью настроенный Jenkins. Снёс - поднял заново, конфигурация идентична до последней галочки.
- Версионирование. Любое изменение настроек видно в истории Git: кто, когда, зачем поменял матрицу прав или добавил агента.
- Прозрачность. Новый человек в команде открывает один файл и видит весь контроллер целиком, а не лазает по сорока экранам настроек.

Установка плагина и переменная CASC_JENKINS_CONFIG
Плагин называется "Configuration as Code" (configuration-as-code), ставится как обычный плагин через Manage Jenkins -> Plugins либо декларативно (об этом ниже). После установки появляется раздел Manage Jenkins -> Configuration as Code.
Главный вопрос - откуда плагин берёт YAML. Тут работает переменная окружения CASC_JENKINS_CONFIG. Если её не задать, плагин по умолчанию ищет файл jenkins.yaml прямо в $JENKINS_HOME. Но в проде так не делают - конфиг кладут отдельно и указывают путь явно:
Код: Выделить всё
# один файл
export CASC_JENKINS_CONFIG=/var/jenkins_home/casc/jenkins.yaml
# целая папка - плагин рекурсивно подхватит все .yaml и .yml
export CASC_JENKINS_CONFIG=/var/jenkins_home/casc/
# можно даже URL
export CASC_JENKINS_CONFIG=https://config.example.com/jenkins.yaml
Вот минимальный, но рабочий jenkins.yaml, который настраивает системные параметры, локального агента и матрицу безопасности:
Код: Выделить всё
jenkins:
systemMessage: "Контроллер Cyberlake CI. Настроен через JCasC, руками не трогать."
numExecutors: 0 # на контроллере не собираем, только оркеструем
mode: EXCLUSIVE
labelString: "controller"
securityRealm:
local:
allowsSignup: false
users:
- id: "admin"
password: "${ADMIN_PASSWORD}"
authorizationStrategy:
globalMatrix:
entries:
- user:
name: "admin"
permissions:
- "Overall/Administer"
- group:
name: "authenticated"
permissions:
- "Overall/Read"
- "Job/Read"
nodes:
- permanent:
name: "build-node-1"
remoteFS: "/home/jenkins/agent"
launcher:
inbound:
workDirSettings:
disabled: false
unclassified:
location:
url: "https://ci.cyberlake.ru/"
adminAddress: "devops@cyberlake.ru"
Экспорт текущей конфигурации и горячая перезагрузка
Писать jenkins.yaml с нуля по документации - больно, потому что не всегда понятно, как называется нужный ключ. Поэтому правильная стратегия: настроил что-то руками в UI -> экспортировал в YAML -> перенёс в свой конфиг.
Экспорт делается в разделе Manage Jenkins -> Configuration as Code, кнопка "Download Configuration" (под капотом дёргается эндпойнт /manage/configuration-as-code/). Ты получаешь полный YAML текущего состояния. Дальше вычищаешь лишнее (плагин экспортирует ОЧЕНЬ много, включая дефолты) и оставляешь только то, что реально хочешь зафиксировать.
После правки конфига не нужно перезапускать весь Jenkins. В том же разделе есть кнопка "Reload existing configuration" - она применяет YAML на лету. Перед применением можно прогнать "View Configuration" и валидацию, чтобы не уронить контроллер кривым синтаксисом. На бою перезагрузку конфига обычно вешают на вебхук из Git: смержили PR в jenkins.yaml - конфиг подтянулся.
Ещё пара полезных приёмов синтаксиса. JCasC умеет YAML-якоря для борьбы с повторами, но из-за особенностей обработки корневых элементов ключ якоря нужно префиксовать через x-, иначе плагин примет его за реальную секцию и ругнётся:
Код: Выделить всё
x-shared: &commonPerms
- "Job/Read"
- "Job/Build"
Главное правило: в jenkins.yaml НЕ должно быть ни одного пароля или токена в открытом виде. Файл лежит в Git, а Git помнит всё. Вместо значений подставляй переменные окружения через ${VAR} или ${VAR:-default} с дефолтом.
Сами секреты прокидываются в контейнер снаружи: из Docker secrets, из Kubernetes Secret, из HashiCorp Vault через sidecar. Пример секции с кредами, где значения берутся из окружения:
Код: Выделить всё
credentials:
system:
domainCredentials:
- credentials:
- usernamePassword:
scope: GLOBAL
id: "docker-registry"
username: "ci-bot"
password: "${DOCKER_REGISTRY_PASS}"
- string:
scope: GLOBAL
id: "github-token"
secret: "${GITHUB_TOKEN}"
Код: Выделить всё
docker run -d --name jenkins \
-p 8080:8080 \
-v jenkins_home:/var/jenkins_home \
-v $(pwd)/casc:/var/jenkins_home/casc:ro \
-e CASC_JENKINS_CONFIG=/var/jenkins_home/casc/jenkins.yaml \
-e ADMIN_PASSWORD="$ADMIN_PASSWORD" \
-e DOCKER_REGISTRY_PASS="$DOCKER_REGISTRY_PASS" \
-e GITHUB_TOKEN="$GITHUB_TOKEN" \
jenkins/jenkins:lts-jdk21
JCasC настраивает Jenkins, но не ставит плагины и не выбирает версию ядра. Чтобы получить ПОЛНОСТЬЮ декларативный контроллер, jenkins casc сочетают с тремя вещами.
1. Версия ядра. Фиксируешь образ. Актуальная LTS на момент написания - линейка 2.541.x (релиз 2.541.1 вышел в январе 2026). Это последняя LTS с поддержкой Java 11; новые ветки требуют Java 17, а weekly-сборки уже двигаются к Java 21. Поэтому бери образ с современной JDK, например jenkins/jenkins:lts-jdk21, и не тяни старую Java.
2. Список плагинов. Кладёшь рядом plugins.txt и доустанавливаешь штатной утилитой jenkins-plugin-cli в своём Dockerfile:
Код: Выделить всё
FROM jenkins/jenkins:lts-jdk21
COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
RUN jenkins-plugin-cli --plugin-file /usr/share/jenkins/ref/plugins.txt
COPY casc/ /var/jenkins_home/casc/
ENV CASC_JENKINS_CONFIG=/var/jenkins_home/casc/jenkins.yaml
# отключаем мастер настройки - конфигурацию задаёт JCasC
ENV JAVA_OPTS="-Djenkins.install.runSetupWizard=false"
3. Kubernetes и эфемерные агенты. В 2026-м контроллер чаще всего живёт в Kubernetes (через официальный Helm-чарт), а агенты поднимаются как одноразовые поды под каждую сборку и удаляются после неё. Это и облако настраивается тем же JCasC - секцией про Kubernetes-cloud:
Код: Выделить всё
jenkins:
clouds:
- kubernetes:
name: "k8s"
namespace: "jenkins"
jenkinsUrl: "http://jenkins.jenkins.svc.cluster.local:8080"
containerCapStr: "10"
templates:
- name: "build-pod"
label: "k8s-build"
containers:
- name: "jnlp"
image: "jenkins/inbound-agent:latest-jdk21"
- name: "docker"
image: "docker:27-cli"
command: "sleep"
args: "infinity"
Кстати, для сравнения: у GitLab CI (.gitlab-ci.yml) и GitHub Actions (.github/workflows) конфигурация изначально лежит в репозитории как код - им JCasC не нужен, там это встроено в саму модель. Jenkins исторически был UI-центричным, и JCasC - это его ответ на запрос "хочу всё как код". А вот Blue Ocean, на который многие смотрели в 2018-м, развивать перестали - он в режиме поддержки, новый UI на нём не строй, ставка везде на declarative pipeline и JCasC.
Типичные грабли
- Пароль в открытом виде в jenkins.yaml. Закоммитил - считай, утёк. Всегда ${VAR}, секреты только снаружи.
- Reload не применяет всё. Некоторые настройки плагинов цепляются только при старте. Если после "Reload" что-то не подхватилось - перезапусти контроллер.
- Конфликт UI и YAML. Поменял руками в UI то, что задано в jenkins.yaml, - при следующем reload правка затрётся. JCasC всегда побеждает. Решение: вообще не трогай UI для того, что под JCasC.
- Несоответствие ключей версии плагина. После обновления плагина имя ключа в YAML может смениться, и конфиг упадёт. Поэтому фиксируй версии плагинов в plugins.txt и обновляй осознанно.
- Забыл выключить мастер настройки. Без -Djenkins.install.runSetupWizard=false контейнер встретит тебя визардом, а не готовым Jenkins.
Сделай руками, чтобы уложилось:
- Подними Jenkins в Docker из образа jenkins/jenkins:lts-jdk21, смонтируй папку casc/ и задай CASC_JENKINS_CONFIG.
- Положи минимальный jenkins.yaml с systemMessage, одним admin-пользователем и globalMatrix. Пароль вынеси в ${ADMIN_PASSWORD}.
- Запусти, проверь, что Jenkins стартовал уже настроенным, без визарда.
- Поменяй systemMessage в YAML и примени через "Reload existing configuration" - убедись, что изменилось без перезапуска.
- Зайди в "Download Configuration", выгрузи полный YAML и сравни со своим - посмотри, сколько всего плагин знает про твой контроллер.
- Чем отличается роль jenkins.yaml от Jenkinsfile - что настраивает каждый из них?
- Что произойдёт, если CASC_JENKINS_CONFIG не задана вообще?
- Почему пароли нельзя писать прямо в YAML и как их туда подставляют правильно?
- Что нужно добавить к JCasC, чтобы получить ПОЛНОСТЬЮ воспроизводимый контроллер, включая плагины и версию ядра?
JCasC превращает капризную ручную настройку Jenkins в обычный YAML-файл, который живёт в Git, ревьюится и применяется автоматически. Связка jenkins.yaml + plugins.txt + Docker-образ с фиксированной LTS даёт полностью воспроизводимый контроллер, а в Kubernetes к этому добавляются эфемерные агенты-поды через Helm. Секреты держим снаружи, через переменные окружения. Главная привычка, которую стоит выработать: всё, что хочешь сохранить, - описывай в YAML, а UI используй только чтобы подсмотреть нужный ключ через экспорт. Тогда любой Jenkins поднимется заново одной командой и будет идентичен оригиналу.