文章

Jenkins Pipeline 核心实践

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 PipelineDocker 构建/推送
Kubernetes动态 Pod Agent
Git ParameterGit 分支/标签选择参数
JUnit测试报告渲染
HTML Publisher覆盖率报告展示
Email Extension邮件通知
Slack NotificationSlack/飞书通知
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 打印了 SecretwithCredentials 绑定,禁止打印
Pipeline 越来越慢Workspace 膨胀cleanWs() + shallow clone
插件升级导致兼容问题大版本 API 变更先在 Staging Jenkins 验证
Docker in Docker (DinD) 问题权限/挂载错误用 Kaniko 代替 DinD 或配置正确 SecurityContext

关联知识

参考资源

学习时间

阶段时间备注
初次学习2026-07-14Pipeline 语法 + Shared Library

状态: 📖 已掌握 下次复习日期: 2026-08-14