Jenkins Pipeline 核心实践
概述
Jenkins 是老牌 CI/CD 工具,凭借 1800+ 插件和灵活的 Pipeline DSL(Groovy)覆盖几乎所有构建场景。对已有 Jenkins 遗产的企业团队来说,掌握 Pipeline as Code 和 Shared Library 是提效关键。
Declarative Pipeline 标准结构
pipeline {
agent any // 或 agent { label 'linux-docker' }
// ── 环境变量 ──
environment {
GO_VERSION = '1.23'
REGISTRY = 'harbor.example.com'
}
// ── 构建参数 ──
parameters {
choice(name: 'ENV', choices: ['dev', 'staging', 'prod'], description: '部署环境')
booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: '跳过测试')
}
// ── 全局触发器 ──
triggers {
pollSCM('H/5 * * * *') // 每 5 分钟检测 Git 变更
cron('0 2 * * 0') // 每周日 2AM 构建
}
// ── 阶段定义 ──
stages {
stage('Checkout') {
steps {
checkout scm // 拉取代码
}
}
stage('Lint') {
steps {
sh 'golangci-lint run ./...'
}
}
stage('Test') {
when { expression { !params.SKIP_TESTS } } // 条件执行
steps {
sh 'go test -v -race -coverprofile=coverage.out ./...'
}
post {
always {
junit 'reports/*.xml' // 渲染测试报告
publishHTML target: [ // 发布覆盖率报告
reportDir: 'coverage',
reportFiles: 'index.html'
]
}
}
}
stage('Build & Push') {
when { branch 'main' }
steps {
script {
def imageTag = "${REGISTRY}/${env.JOB_NAME}:${env.BUILD_NUMBER}"
sh "docker build -t ${imageTag} ."
sh "docker push ${imageTag}"
}
}
}
stage('Deploy') {
when { branch 'main' }
steps {
script {
sh "helm upgrade --install my-app ./helm/chart --set image.tag=${env.BUILD_NUMBER} -n ${params.ENV}"
}
}
}
}
// ── 构建后操作 ──
post {
success {
emailext(
to: 'team@example.com',
subject: "✅ Build ${env.BUILD_NUMBER} passed",
body: "See ${env.BUILD_URL}"
)
}
failure {
emailext(
to: 'team@example.com',
subject: "❌ Build ${env.BUILD_NUMBER} failed",
body: "Check ${env.BUILD_URL}/console"
)
}
always {
cleanWs() // 清理工作空间
}
}
}
Jenkinsfile 关键语法
Agent 选择
// 固定标签
agent { label 'linux-docker' }
// Docker Agent(每次构建启动新容器)
agent {
docker {
image 'golang:1.23'
args '-v /tmp:/tmp'
}
}
// K8s Pod Agent(云原生推荐)
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: golang
image: golang:1.23
command: ['sleep', 'infinity']
- name: docker
image: docker:dind
securityContext: { privileged: true }
'''
}
}
阶段条件控制
// 分支判断
when { branch 'main' }
when { branch pattern: 'release/*', comparator: 'GLOB' }
// 环境判断
when { environment name: 'ENV', value: 'prod' }
// 表达式
when { expression { return params.SKIP_TESTS == false } }
// 任一条件满足
when { anyOf { branch 'main'; branch 'develop' } }
并行执行
stage('Parallel Tests') {
parallel {
stage('Unit Test') {
steps { sh 'go test -short ./...' }
}
stage('Integration Test') {
steps { sh 'go test -tags=integration ./...' }
}
stage('E2E Test') {
steps { sh 'npm run test:e2e' }
}
}
}
Shared Library(共享库)—— 企业级最佳实践
为什么需要 Shared Library
- 避免每个项目复制粘贴 Jenkinsfile
- 统一构建标准(代码检查、依赖扫描、镜像构建)
- 集中管理 Credentials / 工具版本 / 安全策略
目录结构
jenkins-shared-library/
├── vars/ # 全局变量(可在 Pipeline 中直接调用)
│ ├── golangCI.groovy
│ ├── dockerBuild.groovy
│ └── notify.groovy
├── src/ # Groovy 类(复杂逻辑)
│ └── com/example/
│ ├── DockerUtils.groovy
│ └── Notifier.groovy
└── resources/ # 静态资源(JSON 配置等)
└── templates/
示例:golangCI.groovy
// vars/golangCI.groovy
def call(Map config = [:]) {
def goVersion = config.goVersion ?: '1.23'
pipeline {
agent { label 'linux-docker' }
environment { GO_VERSION = goVersion }
stages {
stage('Checkout') {
steps { checkout scm }
}
stage('Lint') {
steps { sh 'golangci-lint run ./...' }
}
stage('Test') {
steps { sh 'go test -v -race ./...' }
}
stage('Build') {
steps {
script {
dockerBuild(
registry: config.registry,
imageName: config.imageName
)
}
}
}
}
post {
failure { notify.slack(channel: '#ci-alerts') }
}
}
}
// 项目 Jenkinsfile 只需两行
@Library('shared-library@main') _
golangCI(
goVersion: '1.22',
registry: 'harbor.example.com',
imageName: 'my-service'
)
常用插件推荐
| 插件 | 用途 |
|---|
| Pipeline | 声明式/脚本化 Pipeline |
| Blue Ocean | 现代化 Pipeline UI |
| Docker Pipeline | Docker 构建/推送 |
| Kubernetes | 动态 Pod Agent |
| Git Parameter | Git 分支/标签选择参数 |
| JUnit | 测试报告渲染 |
| HTML Publisher | 覆盖率报告展示 |
| Email Extension | 邮件通知 |
| Slack Notification | Slack/飞书通知 |
| Credentials Binding | 安全绑定凭据 |
| SonarQube Scanner | 代码质量扫描 |
| Job DSL | 流水线即代码(批量创建 Job) |
性能与可靠性
Master-Agent 架构
Jenkins Master(调度 + Web UI)
├── Agent 1(Linux, label: linux-docker, 4 executors)
├── Agent 2(macOS, label: macos-build, 2 executors)
└── Agent 3(K8s Pod 动态分配)
避免 Master 负载过重
- Master 不要执行构建任务(executors = 0)
- 定期清理旧构建(
Discard old builds → 保留最近 30 天 / 50 次)
- Jenkins Home 目录用 SSD
JENKINS_HOME 备份(Jobs/Config/Credentials 的 XML)
Pipeline 优化
// ❌ 避免
node { ... } // 在同一个节点执行所有阶段
// ✅ 推荐
pipeline {
agent none // 不在全局声明 agent
stages {
stage('Lint') { agent { label 'linux' }; steps { ... } }
stage('Build') { agent { label 'linux-docker' }; steps { ... } }
}
}
常见问题 / 坑点
| 问题 | 原因 | 解决方案 |
|---|
| Jenkinsfile Groovy 语法报错 | @ 转义等问题 | 用 Declarative Pipeline 代替 Scripted |
| Agent 离线 | 磁盘满/JDK 版本不一致 | 监控 Agent 磁盘 + 统一 JDK 版本 |
| Credentials 泄露 | Groovy println 打印了 Secret | 用 withCredentials 绑定,禁止打印 |
| Pipeline 越来越慢 | Workspace 膨胀 | 加 cleanWs() + shallow clone |
| 插件升级导致兼容问题 | 大版本 API 变更 | 先在 Staging Jenkins 验证 |
| Docker in Docker (DinD) 问题 | 权限/挂载错误 | 用 Kaniko 代替 DinD 或配置正确 SecurityContext |
关联知识
参考资源
学习时间
| 阶段 | 时间 | 备注 |
|---|
| 初次学习 | 2026-07-14 | Pipeline 语法 + Shared Library |
状态: 📖 已掌握
下次复习日期: 2026-08-14