Jenkins CI/CD 流水线
大约 7 分钟约 2021 字
Jenkins CI/CD 流水线
简介
Jenkins 是最流行的开源持续集成/持续部署(CI/CD)服务器,通过 Jenkinsfile 定义流水线(Pipeline),实现代码从提交到部署的全自动化。Jenkins 支持声明式和脚本式两种流水线语法,可与 Docker、Git、邮件/Slack 等工具深度集成,是构建自动化交付流程的核心平台。
特点
Jenkinsfile 基础
声明式流水线
// Jenkinsfile — 声明式流水线基本结构
pipeline {
agent any
environment {
APP_NAME = 'myapp'
DOCKER_REGISTRY = 'registry.example.com'
VERSION = "${env.BUILD_ID}"
}
stages {
stage('Checkout') {
steps {
checkout scm
sh 'git log --oneline -5'
}
}
stage('Build') {
steps {
sh 'dotnet publish src/MyApp.Api/MyApp.Api.csproj -c Release -o publish/'
}
}
stage('Test') {
steps {
sh 'dotnet test --logger "trx;LogFileName=test_results.trx"'
}
post {
always {
// 发布测试报告
mstest testResultsFile: '**/test_results.trx'
}
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
sh """
docker build -t ${DOCKER_REGISTRY}/${APP_NAME}:${VERSION} .
docker push ${DOCKER_REGISTRY}/${APP_NAME}:${VERSION}
"""
}
}
}
}流水线参数化构建
// Jenkinsfile — 参数化构建
pipeline {
agent any
parameters {
choice(name: 'ENVIRONMENT', choices: ['dev', 'staging', 'production'], description: '部署环境')
string(name: 'VERSION', defaultValue: 'latest', description: '镜像版本')
booleanParam(name: 'RUN_TESTS', defaultValue: true, description: '是否运行测试')
password(name: 'DEPLOY_TOKEN', defaultValue: '', description: '部署令牌')
}
stages {
stage('Info') {
steps {
echo "部署环境: ${params.ENVIRONMENT}"
echo "部署版本: ${params.VERSION}"
echo "运行测试: ${params.RUN_TESTS}"
}
}
stage('Test') {
when {
expression { return params.RUN_TESTS }
}
steps {
sh 'dotnet test -c Release'
}
}
stage('Deploy') {
steps {
sh """
echo "部署 ${params.VERSION} 到 ${params.ENVIRONMENT}..."
kubectl set image deployment/${APP_NAME} \
${APP_NAME}=${DOCKER_REGISTRY}/${APP_NAME}:${params.VERSION} \
-n ${params.ENVIRONMENT}
"""
}
}
}
}Pipeline 阶段设计
完整的 CI/CD 流水线
// Jenkinsfile — 完整 CI/CD 流水线
pipeline {
agent any
environment {
APP_NAME = 'myapp'
REGISTRY = 'registry.example.com'
IMAGE_TAG = "${REGISTRY}/${APP_NAME}:${GIT_COMMIT.take(8)}"
KUBE_CONFIG = credentials('kube-config')
}
stages {
stage('Prepare') {
steps {
sh 'dotnet --info'
sh 'docker --version'
echo "构建分支: ${env.GIT_BRANCH}"
echo "提交哈希: ${GIT_COMMIT.take(8)}"
}
}
stage('Restore & Build') {
steps {
sh 'dotnet restore'
sh 'dotnet build -c Release --no-restore'
}
}
stage('Unit Tests') {
steps {
sh '''
dotnet test -c Release \
--logger "trx;LogFileName=unit.trx" \
--results-directory test-results/ \
/p:CollectCoverage=true \
/p:CoverletOutputFormat=cobertura
'''
}
post {
always {
mstest testResultsFile: 'test-results/**/*.trx'
// 发布覆盖率报告(需 Coverage 插件)
publishHTML(target: [
reportDir: 'test-results',
reportFiles: 'index.html',
reportName: 'Coverage Report'
])
}
}
}
stage('Docker Build & Push') {
steps {
sh """
docker build -t ${IMAGE_TAG} .
docker push ${IMAGE_TAG}
"""
}
}
stage('Deploy to K8s') {
when {
anyOf {
branch 'main'
branch 'release/*'
}
}
steps {
sh """
kubectl --kubeconfig=\${KUBE_CONFIG} \
set image deployment/\${APP_NAME} \
\${APP_NAME}=\${IMAGE_TAG} \
-n production
"""
sh """
kubectl --kubeconfig=\${KUBE_CONFIG} \
rollout status deployment/\${APP_NAME} \
-n production --timeout=300s
"""
}
}
}
}并行阶段执行
// Jenkinsfile — 并行构建与测试
pipeline {
agent any
stages {
stage('Parallel Build') {
parallel {
stage('Build API') {
steps {
sh 'dotnet publish src/Api/Api.csproj -c Release -o publish/api/'
}
}
stage('Build Worker') {
steps {
sh 'dotnet publish src/Worker/Worker.csproj -c Release -o publish/worker/'
}
}
stage('Build Frontend') {
steps {
dir('frontend') {
sh 'npm ci && npm run build'
}
}
}
}
}
stage('Parallel Test') {
parallel {
stage('Unit Tests') {
steps {
sh 'dotnet test --filter "Category=Unit"'
}
}
stage('Integration Tests') {
steps {
sh 'dotnet test --filter "Category=Integration"'
}
}
stage('Security Scan') {
steps {
sh 'trivy fs --severity HIGH,CRITICAL .'
}
}
}
}
}
}Docker Agent 配置
使用 Docker 作为构建代理
// Jenkinsfile — 使用 Docker 代理
pipeline {
agent none
stages {
stage('Build with .NET') {
agent {
docker {
image 'mcr.microsoft.com/dotnet/sdk:8.0'
args '-v /var/run/docker.sock:/var/run/docker.sock'
}
}
steps {
sh 'dotnet restore'
sh 'dotnet build -c Release'
sh 'dotnet test -c Release'
}
}
stage('Build Docker Image') {
agent {
docker {
image 'docker:24-dind'
args '-v /var/run/docker.sock:/var/run/docker.sock'
}
}
steps {
sh 'docker build -t myapp:v1.0 .'
sh 'docker push registry.example.com/myapp:v1.0'
}
}
stage('Deploy') {
agent {
docker {
image 'bitnami/kubectl:latest'
}
}
steps {
sh 'kubectl apply -f k8s/'
}
}
}
}Jenkins Agent 集群配置
// Jenkinsfile — 指定节点标签
pipeline {
agent none
stages {
stage('Build') {
agent { label 'dotnet && linux' }
steps {
sh 'dotnet publish -c Release'
stash name: 'artifacts', includes: 'publish/**'
}
}
stage('Test') {
agent { label 'dotnet && linux' }
steps {
unstash 'artifacts'
sh 'dotnet test -c Release'
}
}
stage('Docker Build') {
agent { label 'docker' }
steps {
unstash 'artifacts'
sh 'docker build -t myapp:v1.0 .'
}
}
}
}通知与审批
构建通知配置
// Jenkinsfile — 通知与审批
pipeline {
agent any
stages {
stage('Build & Test') {
steps {
sh 'dotnet test -c Release'
}
}
stage('Deploy to Staging') {
steps {
sh 'kubectl apply -f k8s/staging/'
}
}
stage('Approval') {
when {
branch 'main'
}
steps {
// 等待人工审批(需 Pipeline: Input Step 插件)
input message: '确认部署到生产环境?',
ok: '确认部署',
submitter: 'admin,lead',
parameters: [
string(name: 'COMMENTS', defaultValue: '', description: '审批备注')
]
}
}
stage('Deploy to Production') {
steps {
sh 'kubectl apply -f k8s/production/'
}
}
}
post {
success {
emailext(
subject: "构建成功: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: """
构建成功!
项目: ${env.JOB_NAME}
构建: #${env.BUILD_NUMBER}
分支: ${env.GIT_BRANCH}
地址: ${env.BUILD_URL}
""",
to: 'team@example.com'
)
// Slack 通知
slackSend(
color: 'good',
message: "构建成功: ${env.JOB_NAME} #${env.BUILD_NUMBER} (${env.GIT_BRANCH})"
)
}
failure {
emailext(
subject: "构建失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "构建失败,请检查: ${env.BUILD_URL}",
to: 'team@example.com'
)
slackSend(
color: 'danger',
message: "构建失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
)
}
always {
// 清理工作空间
cleanWs()
}
}
}定时与 Webhook 触发
// Jenkinsfile — 触发器配置
pipeline {
agent any
triggers {
// 定时触发(Cron 表达式)
cron('H 2 * * *')
// 轮询 SCM 变更
pollSCM('H/5 * * * *')
// GitHub Webhook 触发(需 GitHub 插件)
githubPush()
}
stages {
stage('Build') {
steps {
sh 'dotnet build -c Release'
}
}
}
}优点
缺点
总结
Jenkins CI/CD 流水线通过 Jenkinsfile 将构建、测试、部署过程代码化,实现了从代码提交到生产部署的全自动化。通过合理设计 Pipeline 阶段、利用并行执行加速构建、配置 Docker Agent 实现环境隔离,以及设置审批门禁和通知机制,可以构建出稳定可靠的持续交付流水线。在实际使用中,建议采用声明式语法以提高可读性,结合共享库(Shared Libraries)复用公共逻辑,并定期审查和优化流水线以保持高效运行。
关键知识点
- DevOps 主题的核心是让交付更快、更稳、更可审计。
- 自动化不是把命令脚本化,而是把失败、回滚、权限和观测一起设计进去。
- 生产链路必须明确制品、环境、凭据、配置和责任边界。
项目落地视角
- 把流水线拆成构建、测试、制品、部署、验证和回滚几个阶段。
- 为关键步骤补齐日志、指标、通知和人工兜底点。
- 定期演练扩容、回滚、故障注入和灾备切换。
常见误区
- 只关注部署成功,不关注失败恢复和审计追踪。
- 把环境差异藏在临时脚本或人工操作里。
- 上线频率高了以后,没有标准化制品和配置管理。
进阶路线
- 继续补齐 GitOps、可观测性、平台工程和成本治理。
- 把主题和应用架构、安全、权限、备份恢复联动起来理解。
- 形成团队级平台能力,而不是每个项目重复造轮子。
适用场景
- 当你准备把《Jenkins CI/CD 流水线》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合构建自动化交付、基础设施治理、监控告警和生产发布体系。
- 当团队规模扩大、发布频率提升或环境变多时,这类主题会显著影响交付效率。
落地建议
- 所有自动化流程尽量做到幂等、可审计、可回滚。
- 把制品、变量、凭据和执行权限分层管理。
- 定期演练扩容、回滚、密钥轮换和灾备恢复。
排错清单
- 先定位失败发生在代码、构建、制品、环境还是权限层。
- 检查流水线变量、凭据、镜像标签和目标环境配置是否一致。
- 如果问题偶发,重点看并发发布、资源争抢和外部依赖抖动。
复盘问题
- 如果把《Jenkins CI/CD 流水线》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《Jenkins CI/CD 流水线》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《Jenkins CI/CD 流水线》最大的收益和代价分别是什么?
