前言
当团队规模扩大到几十个微服务、几十条流水线时,单个 Jenkins Agent 就成了瓶颈------构建排队、资源争抢、环境冲突。本篇讲清楚如何规划多节点构建集群,让构建任务高效分发到合适的 Agent 上。
一、为什么需要多节点
单节点的瓶颈
单节点场景:
10个流水线同时触发 → 排队 → 第1个跑完(15分钟) → 第2个开始...
→ 最后一个等了150分钟才开始
多节点场景:
10个流水线 → 分散到5个Agent → 每个节点跑2个 → 30分钟全部完成
多节点规划原则
| 原则 | 说明 |
|------|------|
| 按技术栈分组 | Java 节点、Go 节点、前端节点分离 |
| 按任务类型分组 | 构建节点、部署节点、测试节点分离 |
| 按环境隔离 | 生产部署节点与构建节点网络隔离 |
| 按负载弹性 | 基础节点常驻 + 弹性节点按需扩缩 |
二、Agent 标签策略
标签设计规范
命名规则: <技术栈>-<任务类型>-<序号>
示例标签:
java-build-01 # Java 构建
java-build-02
go-build-01 # Go 构建
frontend-build-01 # 前端构建
docker-build-01 # Docker 镜像构建
k8s-deploy-01 # K8s 部署(有 kubectl)
k8s-deploy-02
e2e-test-01 # E2E 测试(有浏览器)
Jenkins 中配置 Agent 标签
在 Manage Jenkins → Nodes → 节点配置中设置 Labels:
agent-java-build-01:
Labels: java-build,maven,gradle
# 多个标签用逗号分隔,Pipeline 中可以用任意一个匹配
agent-docker-build-01:
Labels: docker-build,docker
# 这个节点有 Docker daemon
agent-k8s-deploy-01:
Labels: k8s-deploy,kubectl
# 这个节点有 kubectl 和集群 kubeconfig
Pipeline 中使用标签
pipeline {
// 精确匹配:必须有 java-build 标签
agent { label 'java-build' }
stages {
stage('Build') {
steps { sh 'mvn clean package' }
}
stage('Docker Build') {
// 切换到有 docker 标签的节点
agent { label 'docker-build' }
steps {
sh 'docker build -t myapp:${BUILD_NUMBER} .'
sh 'docker push registry.com/myapp:${BUILD_NUMBER}'
}
}
stage('Deploy') {
// 切换到有 k8s-deploy 标签的节点
agent { label 'k8s-deploy' }
steps {
sh 'kubectl set image deployment/myapp app=registry.com/myapp:${BUILD_NUMBER} -n prod'
sh 'kubectl rollout status deployment/myapp -n prod'
}
}
}
}
**踩坑提示**:`stage` 级别的 `agent` 指令会导致工作空间切换。每个 stage 切换节点后,工作空间是新的,之前构建的产物不会自动带过去。需要用 `stash`/`unstash` 或共享存储来传递产物。
使用 stash 传递产物
pipeline {
agent { label 'java-build' }
stages {
stage('Build') {
steps {
sh 'mvn clean package -DskipTests'
// 打包产物
stash includes: 'target/*.jar', name: 'app-jar'
}
}
stage('Docker Build') {
agent { label 'docker-build' }
steps {
// 取回产物
unstash 'app-jar'
sh 'docker build -t myapp:${BUILD_NUMBER} .'
sh 'docker push registry.com/myapp:${BUILD_NUMBER}'
}
}
stage('Deploy') {
agent { label 'k8s-deploy' }
steps {
sh "kubectl set image deployment/myapp app=registry.com/myapp:${BUILD_NUMBER} -n prod"
}
}
}
}
三、Kubernetes 动态 Agent
为什么用动态 Agent
静态 Agent 的问题:
固定 5 个 Agent → 平时利用率 20% → 浪费资源
上线日不够用 → 构建排队
动态 Agent 的优势:
Pod 按需创建 → 高峰期自动扩容到 20 个
空闲期自动缩到 0
每次构建都是全新环境 → 无环境污染
安装 K8s 插件并配置
Manage Jenkins → System → Cloud → Kubernetes:
# 关键配置
Kubernetes URL: https://k8s-api:6443
Kubernetes Namespace: jenkins
Jenkins URL: http://jenkins-master:8080
Jenkins tunnel: jenkins-master:50000
# Pod Template 配置:
Name: maven-pod
Namespace: jenkins
Labels: k8s-maven
# Container Template:
Name: maven
Docker image: maven:3.9-eclipse-temurin-17
Command: cat
Working directory: /home/jenkins/agent
# 资源限制:
CPU request: 1
CPU limit: 2
Memory request: 2Gi
Memory limit: 4Gi
# Volume:
/root/.m2 → PVC(maven-cache)
Pipeline 使用 K8s 动态 Agent
pipeline {
agent {
kubernetes {
label 'maven-pod'
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: maven
image: maven:3.9-eclipse-temurin-17
command: ['cat']
tty: true
volumeMounts:
- name: maven-cache
mountPath: /root/.m2
- name: docker
image: docker:24
command: ['cat']
tty: true
securityContext:
privileged: true # 需要构建 Docker 镜像
volumes:
- name: maven-cache
persistentVolumeClaim:
claimName: maven-cache-pvc
- name: docker-sock
hostPath:
path: /var/run/docker.sock
'''
}
}
stages {
stage('Build') {
steps {
container('maven') {
sh 'mvn clean package -DskipTests'
stash includes: 'target/*.jar', name: 'app-jar'
}
}
}
stage('Docker Build') {
steps {
container('docker') {
unstash 'app-jar'
sh 'docker build -t myapp:${BUILD_NUMBER} .'
sh 'docker push registry.com/myapp:${BUILD_NUMBER}'
}
}
}
}
}
**踩坑提示**:K8s 动态 Agent 的 Pod 默认有 5 分钟空闲超时。如果 Pipeline 中有长时间等待(如等待人工审批),Pod 会被回收导致 Pipeline 失败。在 Pod Template 中设置 `idleMinutes: 30` 延长超时时间。
四、任务分发策略
1. 标签表达式
// AND 表达式:同时拥有两个标签
agent { label 'java && docker' }
// OR 表达式:任一标签
agent { label 'java-build || go-build' }
// 排除表达式
agent { label 'build && !e2e-test' }
2. 负载均衡
Jenkins 内置的负载均衡策略:
| 策略 | 说明 | 配置方式 |
|------|------|---------|
| Round Robin | 轮询分发 | 默认 |
| Load | 优先选负载低的 | Node 配置 → Usage → "Use this node as much as possible" |
| Specific | 指定节点 | agent { label 'agent-01' } |
3. 优先级队列
// 在 Job 配置中设置优先级
properties([
buildDiscarder(logRotator(numToKeepStr: '20')),
[$class: 'JiraProjectProperty'],
pipelineTriggers([cron('@daily')])
])
// 通过 Priority Sorter 插件设置优先级
// 安装 Priority Sorter Plugin 后:
// Manage Jenkins → System → Priority Sorter
// → Priority Strategy → Absolute Priority
// → Job Group:
// - 'deploy-*' → Priority 1 (最高)
// - 'build-*' → Priority 5
// - 'test-*' → Priority 10
4. 资源互斥
避免多个 Job 同时操作同一资源:
pipeline {
agent any
options {
// 同一资源组同时只允许一个构建
lock(resource: 'production-deploy', inversePrecedence: true)
}
stages {
stage('Deploy') {
steps {
sh 'kubectl apply -f deploy/ -n prod'
}
}
}
}
五、监控 Agent 健康状态
健康检查脚本
#!/bin/bash
# 定期检查所有 Agent 的磁盘空间
for agent in $(java -jar jenkins-cli.jar -s http://localhost:8080 list-nodes 2>/dev/null | awk '{print $1}'); do
DISK_USAGE=$(java -jar jenkins-cli.jar -s http://localhost:8080 node $agent run-script 'println "df -h".execute().text' 2>/dev/null | grep '/$' | awk '{print $5}' | tr -d '%')
if [ "$DISK_USAGE" -gt 80 ]; then
echo "WARNING: Agent $agent disk usage: ${DISK_USAGE}%"
# 推送到 Prometheus
echo "jenkins_agent_disk_usage{agent=\"$agent\"} $DISK_USAGE" | curl --data-binary @- http://pushgateway:9091/metrics/job/jenkins-agent
fi
done
Agent 自动清理
# 定时清理 Agent 上的旧构建产物
#!/bin/bash
# 在每个 Agent 上配置 crontab
find /home/jenkins/workspace -mindepth 1 -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \;
find /tmp -name "jenkins*" -mtime +1 -exec rm -f {} \;
docker system prune -f --filter "until=24h"
六、本篇要点回顾
-
Agent 按"技术栈+任务类型"规划标签,Pipeline 中用标签精确匹配
-
跨节点传递产物用
stash/unstash,不能依赖工作空间 -
K8s 动态 Agent 按需创建、用完即删,配合 PVC 缓存依赖
-
lock指令实现资源互斥,防止多个 Job 同时操作生产环境 -
监控 Agent 磁盘和状态,定期清理旧产物
下一篇预告:《插件生态:必装插件清单与安全加固》------盘点 Jenkins 生态中真正好用的插件,以及如何避免"插件地狱"。