Модернизация Jenkins: перенос сборщиков в динамические поды Kubernetes
Во многих инжиниринговых компаниях и IT-департаментах сервера автоматизации сборки Jenkins остаются ключевым фундаментом конвейеров непрерывной интеграции и поставки ПО (CI/CD). Однако традиционная схема развертывания с постоянными выделенными агентами — когда для сборок выделяются статические виртуальные машины или физические сервера, работающие в режиме 24 часа в сутки и 7 дней в неделю — создаёт серьезные операционные и экономические проблемы.
В периоды высокой нагрузки и перед крупными релизами разработчики сталкиваются с длинными очередями запускных задач. В то же время по ночам и в выходные дни выделенная инфраструктура простаивает, продолжая потреблять ресурсы. Дополнительно возникают конфликты системных зависимостей, когда разным проектам требуются отличающиеся версии окружений (например, Java 11 и Java 17, либо различные версии Node.js), а также возникают серьезные сложности с точным распределением затрат на серверные мощности между продуктовыми командами.
Перевод исполнительных агентов Jenkins на динамические поды Kubernetes решает эти инфраструктурные ограничения. В такой архитектуре центральный управляющий узел (Jenkins Controller) создает изолированный под в кластере под конкретную задачу, выполняет шаги сборки внутри целевых контейнеров и полностью удаляет под сразу после завершения пайплайна.
Архитектура решения и взаимодействие с Kubernetes API
В динамической модели управляющий узел Jenkins подключается к API-серверу Kubernetes через специализированный плагин kubernetes-plugin. Когда в очередь задач поступает новая сборка, контроллер обращается к кластеру и запрашивает создание нового пода на основе описанного шаблона (Pod Template).
Каждый создаваемый под сборщика содержит служебный контейнер jnlp (Jenkins Network Launch Protocol) для поддержания связи с управляющим узлом, а также один или несколько рабочих контейнеров с целевыми инструментами разработки. Для обеспечения безопасности и контроля доступа взаимодействие между Jenkins и кластером происходит от имени служебной учетной записи с строго ограниченными правами.
Шаг 1. Настройка пространства имен и прав доступа RBAC
Развертывание начинается с создания изолированного пространства имен в Kubernetes и настройки прав авторизации через механизмы Role-Based Access Control (RBAC). Для этого создается служебная учетная запись jenkins-admin в пространстве имен jenkins:
apiVersion: v1
kind: Namespace
metadata:
name: jenkins
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins-admin
namespace: jenkins
Для управления динамическими подами данной учетной записи назначаются соответствующие привилегии через манифест ClusterRoleBinding:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: jenkins-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: jenkins-admin
namespace: jenkins
Шаг 2. Развертывание управляющего узла и агента подключения
Управляющий узел Jenkins разворачивается через манифест Deployment с подключением постоянного тома (PersistentVolumeClaim) для долгосрочного хранения конфигураций, настроек плагинов и истории сборок. Для связи динамических подов с контроллером настраивается внутренний сервис с секретным токеном аутентификации:
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins-master
namespace: jenkins
spec:
replicas: 1
selector:
matchLabels:
app: jenkins-master
template:
metadata:
labels:
app: jenkins-master
spec:
serviceAccountName: jenkins-admin
containers:
- name: jenkins
image: jenkins/jenkins:lts
ports:
- containerPort: 8080
- containerPort: 50000
Для надежного аутентифицированного соединения динамических подов секретный ключ токена сохраняется в объекте Kubernetes Secret и автоматически монтируется в каждый запускаемый под сборщика.
Шаг 3. Конфигурирование пайплайнов в Jenkinsfile
Описание рабочей среды переносится непосредственно в код пайплайна. Для стандартных задач используется декларативный шаблон с одним рабочим контейнером, например, для компиляции и тестирования Java-проектов:
pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: maven
image: maven:3.9-eclipse-temurin-17
command: ["sleep", "infinity"]
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1000m"
'''
defaultContainer 'maven'
}
}
stages {
stage('Build') {
steps {
sh 'mvn clean package'
}
}
}
}
Если проект требует использования одновременно фронтенд- и бэкенд-инструментов, под может содержать несколько контейнеров (например, Maven и Node.js), работающих параллельно в общем сетевом пространстве пода и обращающихся к единой рабочей директории.
Шаг 4. Управление кешем и приватными корпоративными репозиториями
Эфемерность подов означает, что после успешного завершения или отмены задачи под полностью удаляется, а локальный кеш зависимостей теряется. Для работы с приватными корпоративными реестрами (такими как Nexus или Artifactory) конфигурационный файл settings.xml с авторизационными данными сохраняется в Kubernetes Secret и монтируется в контейнер сборщика:
volumeMounts:
- name: maven-settings
mountPath: /root/.m2/settings.xml
subPath: settings.xml
volumes:
- name: maven-settings
secret:
secretName: maven-settings
Если для команды критически важно сохранять артефакты сборки или промежуточные файлы для отладки в веб-интерфейсе Jenkins, применяются гибридные схемы: компиляция происходит в динамических подах, а артефакты архивируются на статический агент с томом PVC.
Шаг 5. Распределение затрат и финансовый учет по продуктовым командам
Главным стратегическим преимуществом переноса сборщиков в Kubernetes становится возможность точного финансового учета и аллокации инфраструктурных расходов. Запуск подов каждой продуктовой команды в отдельном пространстве имен (например, team-alpha-builds и team-beta-builds) позволяет использовать стандартные биллинговые метрики облачных провайдеров для автоматического распределения затрат.
Для предотвращения ситуации, когда одна команда захватывает все узлы кластера в момент релиза, на каждое пространство имен накладываются жесткие лимиты через объект ResourceQuota:
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-alpha-quota
namespace: team-alpha-builds
spec:
hard:
requests.cpu: "10"
requests.memory: 20Gi
limits.cpu: "20"
limits.memory: 40Gi
pods: "50"
Практические результаты и эксплуатационные ограничения
Переход на динамические поды полностью устраняет простаивание серверных мощностей, предотвращает конфликты версий и гарантирует чистую предсказуемую среду для каждого запуска. В качестве эксплуатационных ограничений следует учитывать небольшие задержки при первичном скачивании базовых Docker-образов на новые ноды кластера. Эта проблема успешно решается предзагрузкой частых образов (image pre-pulling) или использованием локальных реестров контейнеров. В результате перенос агентов в Kubernetes дает эластичную современную инфраструктуру с прозрачной экономикой без необходимости переписывать накопленные пайплайны.

