【CI/CD·Jenkins篇】多节点构建:Agent 配置与任务分发策略

作者:袖梨 2026-08-25

【CI/CD·Jenkins篇】多节点构建:Agent 配置与任务分发策略

{"type":"doc","content":[{"type":"heading","attrs":{"level":2},"content":[{"type":"text","text":"前言"}]},{"type":"paragraph","content":[{"type":"text","text":"当团队规模扩大到几十个微服务、几十条流水线时,单个 Jenkins Agent 就成了瓶颈——构建排队、资源争抢、环境冲突。本篇讲清楚如何规划多节点构建集群,让构建任务高效分发到合适的 Agent 上。"}]},{"type":"horizontal_rule"},{"type":"heading","attrs":{"level":2},"content":[{"type":"text","text":"一、为什么需要多节点"}]},{"type":"heading","attrs":{"level":3},"content":[{"type":"text","text":"单节点的瓶颈"}]},{"type":"code_block","attrs":{"language":"text"},"content":[{"type":"text","text":"单节点场景:n10个流水线同时触发 → 排队 → 第1个跑完(15分钟) → 第2个开始...n→ 最后一个等了150分钟才开始nn多节点场景:n10个流水线 → 分散到5个Agent → 每个节点跑2个 → 30分钟全部完成"}]},{"type":"heading","attrs":{"level":3},"content":[{"type":"text","text":"多节点规划原则"}]},{"type":"paragraph","content":[{"type":"text","text":"| 原则 | 说明 |"}]},{"type":"paragraph","content":[{"type":"text","text":"|------|------|"}]},{"type":"paragraph","content":[{"type":"text","text":"| 按技术栈分组 | Java 节点、Go 节点、前端节点分离 |"}]},{"type":"paragraph","content":[{"type":"text","text":"| 按任务类型分组 | 构建节点、部署节点、测试节点分离 |"}]},{"type":"paragraph","content":[{"type":"text","text":"| 按环境隔离 | 生产部署节点与构建节点网络隔离 |"}]},{"type":"paragraph","content":[{"type":"text","text":"| 按负载弹性 | 基础节点常驻 弹性节点按需扩缩 |"}]},{"type":"horizontal_rule"},{"type":"heading","attrs":{"level":2},"content":[{"type":"text","text":"二、Agent 标签策略"}]},{"type":"heading","attrs":{"level":3},"content":[{"type":"text","text":"标签设计规范"}]},{"type":"code_block","attrs":{"language":"text"},"content":[{"type":"text","text":"命名规则: <技术栈>-<任务类型>-<序号>nn示例标签:njava-build-01 # Java 构建njava-build-02ngo-build-01 # Go 构建nfrontend-build-01 # 前端构建ndocker-build-01 # Docker 镜像构建nk8s-deploy-01 # K8s 部署(有 kubectl)nk8s-deploy-02ne2e-test-01 # E2E 测试(有浏览器)"}]},{"type":"heading","attrs":{"level":3},"content":[{"type":"text","text":"Jenkins 中配置 Agent 标签"}]},{"type":"paragraph","content":[{"type":"text","text":"在 Manage Jenkins → Nodes → 节点配置中设置 Labels:"}]},{"type":"code_block","attrs":{"language":"text"},"content":[{"type":"text","text":"agent-java-build-01:nLabels: java-build,maven,gradlen# 多个标签用逗号分隔,Pipeline 中可以用任意一个匹配nnagent-docker-build-01:nLabels: docker-build,dockern# 这个节点有 Docker daemonnnagent-k8s-deploy-01:nLabels: k8s-deploy,kubectln# 这个节点有 kubectl 和集群 kubeconfig"}]},{"type":"heading","attrs":{"level":3},"content":[{"type":"text","text":"Pipeline 中使用标签"}]},{"type":"code_block","attrs":{"language":"groovy"},"content":[{"type":"text","text":"pipeline {n// 精确匹配:必须有 java-build 标签nagent { label 'java-build' }nnstages {nstage('Build') {nsteps { sh 'mvn clean package' }n}nnstage('Docker Build') {n// 切换到有 docker 标签的节点nagent { label 'docker-build' }nsteps {nsh 'docker build -t myapp:${BUILD_NUMBER} .'nsh 'docker push registry.com/myapp:${BUILD_NUMBER}'n}n}nnstage('Deploy') {n// 切换到有 k8s-deploy 标签的节点nagent { label 'k8s-deploy' }nsteps {nsh 'kubectl set image deployment/myapp app=registry.com/myapp:${BUILD_NUMBER} -n prod'nsh 'kubectl rollout status deployment/myapp -n prod'n}n}n}n}"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"type":"text","marks":[{"type":"strong"}],"text":"踩坑提示"},{"type":"text","text":":"},{"type":"text","marks":[{"type":"code"}],"text":"stage"},{"type":"text","text":" 级别的 "},{"type":"text","marks":[{"type":"code"}],"text":"agent"},{"type":"text","text":" 指令会导致工作空间切换。每个 stage 切换节点后,工作空间是新的,之前构建的产物不会自动带过去。需要用 "},{"type":"text","marks":[{"type":"code"}],"text":"stash"},{"type":"text","text":"/"},{"type":"text","marks":[{"type":"code"}],"text":"unstash"},{"type":"text","text":" 或共享存储来传递产物。"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"type":"text","text":"使用 stash 传递产物"}]},{"type":"code_block","attrs":{"language":"groovy"},"content":[{"type":"text","text":"pipeline {nagent { label 'java-build' }nnstages {nstage('Build') {nsteps {nsh 'mvn clean package -DskipTests'n// 打包产物nstash includes: 'target/*.jar', name: 'app-jar'n}n}nnstage('Docker Build') {nagent { label 'docker-build' }nsteps {n// 取回产物nunstash 'app-jar'nsh 'docker build -t myapp:${BUILD_NUMBER} .'nsh 'docker push registry.com/myapp:${BUILD_NUMBER}'n}n}nnstage('Deploy') {nagent { label 'k8s-deploy' }nsteps {nsh "kubectl set image deployment/myapp app=registry.com/myapp:${BUILD_NUMBER} -n prod"n}n}n}n}"}]},{"type":"horizontal_rule"},{"type":"heading","attrs":{"level":2},"content":[{"type":"text","text":"三、Kubernetes 动态 Agent"}]},{"type":"heading","attrs":{"level":3},"content":[{"type":"text","text":"为什么用动态 Agent"}]},{"type":"paragraph","content":[{"type":"text","text":"静态 Agent 的问题:"}]},{"type":"code_block","attrs":{"language":"text"},"content":[{"type":"text","text":"固定 5 个 Agent → 平时利用率 20% → 浪费资源n上线日不够用 → 构建排队"}]},{"type":"paragraph","content":[{"type":"text","text":"动态 Agent 的优势:"}]},{"type":"code_block","attrs":{"language":"text"},"content":[{"type":"text","text":"Pod 按需创建 → 高峰期自动扩容到 20 个n空闲期自动缩到 0n每次构建都是全新环境 → 无环境污染"}]},{"type":"heading","attrs":{"level":3},"content":[{"type":"text","text":"安装 K8s 插件并配置"}]},{"type":"paragraph","content":[{"type":"text","text":"Manage Jenkins → System → Cloud → Kubernetes:"}]},{"type":"code_block","attrs":{"language":"yaml"},"content":[{"type":"text","text":"# 关键配置nKubernetes URL: https://k8s-api:6443nKubernetes Namespace: jenkinsnJenkins URL: http://jenkins-master:8080nJenkins tunnel: jenkins-master:50000nn# Pod Template 配置:nName: maven-podnNamespace: jenkinsnLabels: k8s-mavenn# Container Template:nName: mavennDocker image: maven:3.9-eclipse-temurin-17nCommand: catnWorking directory: /home/jenkins/agentn# 资源限制:nCPU request: 1nCPU limit: 2nMemory request: 2GinMemory limit: 4Gin# Volume:n/root/.m2 → PVC(maven-cache)"}]},{"type":"heading","attrs":{"level":3},"content":[{"type":"text","text":"Pipeline 使用 K8s 动态 Agent"}]},{"type":"code_block","attrs":{"language":"groovy"},"content":[{"type":"text","text":"pipeline {nagent {nkubernetes {nlabel 'maven-pod'nyaml '''napiVersion: v1nkind: Podnspec:ncontainers:n- name: mavennimage: maven:3.9-eclipse-temurin-17ncommand: ['cat']ntty: truenvolumeMounts:n- name: maven-cachenmountPath: /root/.m2n- name: dockernimage: docker:24ncommand: ['cat']ntty: truensecurityContext:nprivileged: true# 需要构建 Docker 镜像nvolumes:n- name: maven-cachenpersistentVolumeClaim:nclaimName: maven-cache-pvcn- name: docker-socknhostPath:npath: /var/run/docker.sockn'''n}n}nnstages {nstage('Build') {nsteps {ncontainer('maven') {nsh 'mvn clean package -DskipTests'nstash includes: 'target/*.jar', name: 'app-jar'n}n}n}nnstage('Docker Build') {nsteps {ncontainer('docker') {nunstash 'app-jar'nsh 'docker build -t myapp:${BUILD_NUMBER} .'nsh 'docker push registry.com/myapp:${BUILD_NUMBER}'n}n}n}n}n}"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"type":"text","marks":[{"type":"strong"}],"text":"踩坑提示"},{"type":"text","text":":K8s 动态 Agent 的 Pod 默认有 5 分钟空闲超时。如果 Pipeline 中有长时间等待(如等待人工审批),Pod 会被回收导致 Pipeline 失败。在 Pod Template 中设置 "},{"type":"text","marks":[{"type":"code"}],"text":"idleMinutes: 30"},{"type":"text","text":" 延长超时时间。"}]}]},{"type":"horizontal_rule"},{"type":"heading","attrs":{"level":2},"content":[{"type":"text","text":"四、任务分发策略"}]},{"type":"heading","attrs":{"level":3},"content":[{"type":"text","text":"1. 标签表达式"}]},{"type":"code_block","attrs":{"language":"groovy"},"content":[{"type":"text","text":"// AND 表达式:同时拥有两个标签nagent { label 'java && docker' }nn// OR 表达式:任一标签nagent { label 'java-build || go-build' }nn// 排除表达式nagent { label 'build && !e2e-test' }"}]},{"type":"heading","attrs":{"level":3},"content":[{"type":"text","text":"2. 负载均衡"}]},{"type":"paragraph","content":[{"type":"text","text":"Jenkins 内置的负载均衡策略:"}]},{"type":"paragraph","content":[{"type":"text","text":"| 策略 | 说明 | 配置方式 |"}]},{"type":"paragraph","content":[{"type":"text","text":"|------|------|---------|"}]},{"type":"paragraph","content":[{"type":"text","text":"| Round Robin | 轮询分发 | 默认 |"}]},{"type":"paragraph","content":[{"type":"text","text":"| Load | 优先选负载低的 | Node 配置 → Usage → "Use this node as much as possible" |"}]},{"type":"paragraph","content":[{"type":"text","text":"| Specific | 指定节点 | "},{"type":"text","marks":[{"type":"code"}],"text":"agent { label 'agent-01' }"},{"type":"text","text":" |"}]},{"type":"heading","attrs":{"level":3},"content":[{"type":"text","text":"3. 优先级队列"}]},{"type":"code_block","attrs":{"language":"groovy"},"content":[{"type":"text","text":"// 在 Job 配置中设置优先级nproperties([nbuildDiscarder(logRotator(numToKeepStr: '20')),n[$class: 'JiraProjectProperty'],npipelineTriggers([cron('@daily')])n])nn// 通过 Priority Sorter 插件设置优先级n// 安装 Priority Sorter Plugin 后:n// Manage Jenkins → System → Priority Sortern// → Priority Strategy → Absolute Priorityn// → Job Group: n// - 'deploy-*' → Priority 1 (最高)n// - 'build-*' → Priority 5n// - 'test-*' → Priority 10"}]},{"type":"heading","attrs":{"level":3},"content":[{"type":"text","text":"4. 资源互斥"}]},{"type":"paragraph","content":[{"type":"text","text":"避免多个 Job 同时操作同一资源:"}]},{"type":"code_block","attrs":{"language":"groovy"},"content":[{"type":"text","text":"pipeline {nagent anynoptions {n// 同一资源组同时只允许一个构建nlock(resource: 'production-deploy', inversePrecedence: true)n}nstages {nstage('Deploy') {nsteps {nsh 'kubectl apply -f deploy/ -n prod'n}n}n}n}"}]},{"type":"horizontal_rule"},{"type":"heading","attrs":{"level":2},"content":[{"type":"text","text":"五、监控 Agent 健康状态"}]},{"type":"heading","attrs":{"level":3},"content":[{"type":"text","text":"健康检查脚本"}]},{"type":"code_block","attrs":{"language":"bash"},"content":[{"type":"text","text":"#!/bin/bashn# 定期检查所有 Agent 的磁盘空间nfor agent in $(java -jar jenkins-cli.jar -s http://localhost:8080 list-nodes 2>/dev/null | awk '{print $1}'); donDISK_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 '%')nnif [ "$DISK_USAGE" -gt 80 ]; thennecho "WARNING: Agent $agent disk usage: ${DISK_USAGE}%"n# 推送到 Prometheusnecho "jenkins_agent_disk_usage{agent="$agent"} $DISK_USAGE" | curl --data-binary @- http://pushgateway:9091/metrics/job/jenkins-agentnfindone"}]},{"type":"heading","attrs":{"level":3},"content":[{"type":"text","text":"Agent 自动清理"}]},{"type":"code_block","attrs":{"language":"bash"},"content":[{"type":"text","text":"# 定时清理 Agent 上的旧构建产物n#!/bin/bashn# 在每个 Agent 上配置 crontabnfind /home/jenkins/workspace -mindepth 1 -maxdepth 1 -type d -mtime 7 -exec rm -rf {} ;nfind /tmp -name "jenkins*" -mtime 1 -exec rm -f {} ;ndocker system prune -f --filter "until=24h""}]},{"type":"horizontal_rule"},{"type":"heading","attrs":{"level":2},"content":[{"type":"text","text":"六、本篇要点回顾"}]},{"type":"ordered_list","attrs":{"order":1},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"Agent 按"技术栈 任务类型"规划标签,Pipeline 中用标签精确匹配"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"跨节点传递产物用 "},{"type":"text","marks":[{"type":"code"}],"text":"stash"},{"type":"text","text":"/"},{"type":"text","marks":[{"type":"code"}],"text":"unstash"},{"type":"text","text":",不能依赖工作空间"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"K8s 动态 Agent 按需创建、用完即删,配合 PVC 缓存依赖"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","marks":[{"type":"code"}],"text":"lock"},{"type":"text","text":" 指令实现资源互斥,防止多个 Job 同时操作生产环境"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"监控 Agent 磁盘和状态,定期清理旧产物"}]}]}]},{"type":"paragraph","content":[{"type":"text","marks":[{"type":"strong"}],"text":"下一篇预告"},{"type":"text","text":":《插件生态:必装插件清单与安全加固》——盘点 Jenkins 生态中真正好用的插件,以及如何避免"插件地狱"。"}]}]}","createTime":1786334625,"ext":{"closeTextLink":0,"comment_ban":0,"description":"","focusRead":0},"favNum":0,"html":"","isOriginal":0,"likeNum":0,

相关文章

精彩推荐