概述
用 Jenkins 的人分两派:一派在网页上填表单配任务,点几下就跑起来了,简单粗暴;另一派在代码仓库里写 Jenkinsfile,把构建流程变成代码,版本控制、评审、回滚一条龙。
前者叫 Freestyle Project(自由风格项目),后者叫 Pipeline Project(流水线项目)。
这两种风格不是"谁取代谁"的关系。很多团队两个都在用——简单的脚本任务用 Freestyle,复杂的多阶段发布用 Pipeline。但如果你刚开始搞 CI/CD,或者在犹豫要不要从 Freestyle 迁到 Pipeline,这篇文章帮你把两种风格的配置流程、核心差异、选型策略一次性捋清楚。
我先说结论:能用 Pipeline 就用 Pipeline。但别急着全盘迁移,得看场景。下面从零开始配。
Jenkins 项目到底是个什么东西
Jenkins 本身是个任务执行引擎。你给它一组指令——去哪拉代码、怎么编译、往哪部署——它照着干。这组指令的载体就是 Jenkins 项目(Jenkins 内部叫 Job)。
打个比方,Jenkins 是厨师,项目就是菜谱。菜谱写了先切菜、再炒、最后装盘,厨师按步骤做。Freestyle 和 Pipeline 就是两种不同格式的菜谱——一个是填表式,一个是代码式。
Jenkins 原生支持好几种项目类型,实际干活最常用的就两种:
| 项目类型 | 怎么配 | 适合谁 |
|---|---|---|
| Freestyle Project | 在网页上填表单 | 刚接触 Jenkins 的团队,简单构建场景 |
| Pipeline Project | 写 Jenkinsfile 代码 | 需要多阶段编排、长期维护的团队 |
还有 Maven 项目(Java Maven 专用的 Freestyle 特化版)和多配置项目(同一任务跑多套参数),用得越来越少,这里不展开。
两种风格的本质区别
先看一个直观的例子。假设要配一个"拉代码 → 编译 → 部署"的任务。
Freestyle 版本:打开 Jenkins 网页,在源码管理填 Git 地址,构建步骤填 mvn clean package,构建后操作填部署脚本。保存,点构建,跑起来了。
Pipeline 版本:在项目根目录放一个 Jenkinsfile 文本文件:
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'mvn clean package'
}
}
stage('Deploy') {
steps {
sh './deploy.sh'
}
}
}
}
Jenkins 从 Git 仓库读这个文件,按步骤执行。看起来多了几行代码,但好处后面会讲到。
九个维度的差异
| 维度 | Freestyle | Pipeline |
|---|---|---|
| 配置方式 | 网页表单 | 代码(Jenkinsfile) |
| 版本控制 | 做不到 | Jenkinsfile 在 Git 里,有提交记录 |
| 复杂流程 | 多步骤能配,但逻辑全混在 shell 里 | stages 分阶段,清晰可读 |
| 条件判断 | 很弱,只能靠 shell 脚本 if/else | when 指令原生支持 |
| 并行执行 | 不支持 | parallel 块直接支持 |
| 可视化 | 只有构建历史 | Stage View 看每个阶段耗时和状态 |
| 可复用性 | 克隆 Job,改改参数 | Jenkinsfile 共享库,跨项目复用 |
| 代码评审 | 做不到 | 改 Jenkinsfile 提 PR,团队评审 |
| 灾难恢复 | Job 配置在 Jenkins 内部,迁移要搬 XML | Jenkinsfile 在 Git,恢复就是重新拉 |
Freestyle 最致命的短板是版本控制。你改了 Job 配置,没有 commit 记录,谁改的、改了什么、什么时候改的一概查不到。线上炸了想回滚?只能靠记忆或者翻 Jenkins 操作日志。Pipeline 把构建流程变成代码,这些全不是问题了。
Freestyle 还值得用的四个场景
话虽如此,Freestyle 没死。下面四种情况用它更合适:
- 一次性任务:跑个数据迁移、清理个日志,配完就不管了
- 极简构建:就拉代码跑个
mvn package,没有多阶段编排的需求 - 团队刚开始搞 CI/CD:先让流水线跑起来建立信心,后面再迁移
- 不想学 Groovy 语法:Pipeline 虽然声明式语法很简单,但有些团队就是不想碰代码
自由风格项目配置详解
创建项目
Jenkins 首页 → New Item → 输入名称 → 选 Freestyle project → OK。
名称用英文和连字符,别用中文和空格,后面写脚本时好引用。
General:基本设置
| 配置项 | 干嘛的 | 建议值 |
|---|---|---|
| 描述 | 写清楚任务干嘛的、谁负责 | 订单服务生产环境部署 - 负责人:张三 |
| 丢弃旧的构建 | 防止构建记录撑爆磁盘 | 勾选,保留 7 天或 20 次 |
| 禁止并发构建 | 同一任务不要同时跑 | 勾选,避免部署冲突 |
| 参数化构建过程 | 构建时让用户选参数 | 按需勾选 |
| 限制运行节点 | 指定在哪个 agent 上跑 | 集群环境下用标签控制 |
参数化构建是 Freestyle 的常用功能,勾选后能添加多种参数类型:
| 参数类型 | 用途 | 示例 |
|---|---|---|
| String Parameter | 字符串输入 | 版本号:v1.2.3 |
| Choice Parameter | 下拉选择 | 环境:dev / test / prod |
| Boolean Parameter | 勾选框 | 是否跳过测试:false |
| Git Parameter | 自动拉取 Git 分支/Tag 列表 | 选择发布分支 |
| Credentials Parameter | 选择凭据 | 选择部署用的 SSH Key |
比如配一个环境选择参数:
名称:DEPLOY_ENV
选项:dev
test
gray
prod
描述:选择部署目标环境
构建时 Jenkins 弹一个下拉框让你选,选完点构建,参数注入到环境变量里,后续 shell 脚本用 $DEPLOY_ENV 就能拿到。
源码管理
告诉 Jenkins 去哪拉代码。选 Git,填三样东西:
| 配置项 | 说明 | 示例 |
|---|---|---|
| Repository URL | 代码仓库地址 | git@gitlab.example.com:team/order-api.git |
| Credentials | 访问凭据 | 选提前配好的 SSH Key 或用户名密码 |
| Branch Specifier | 拉哪个分支 | */main 或 */refs/tags/$RELEASE_TAG |
凭据配置容易卡住。SSH 方式拉代码的步骤:
- Jenkins 服务器上生成密钥对:
ssh-keygen -t ed25519 -f ~/.ssh/jenkins_deploy - 公钥(
.pub文件内容)添加到 Git 仓库的 Deploy Keys - Jenkins → Manage Jenkins → Credentials → System → Global credentials → 添加 SSH Username with private key,把私钥贴进去
- 源码管理的 Credentials 下拉选这个凭据
HTTPS 方式则选 Username with password,填 Git 账号密码或 Access Token。
高级选项里有几个实用的配置。比如 Additional Behaviours → Check out to a sub-directory,把代码拉到子目录而不是工作区根目录,多个项目共用一个 Job 时有用。还有 Sparse Checkout,大仓库只拉需要的目录,能省不少时间。
构建触发器
控制什么时候自动触发构建。常用的几种:
| 触发器 | 说明 | 典型场景 |
|---|---|---|
| Build periodically | 定时触发,用 cron 表达式 | 每天凌晨 2 点跑全量构建 |
| Poll SCM | 定时检查 Git 有没有新提交 | 每 5 分钟看一次,有新代码就构建 |
| GitHub hook trigger | Git push 时自动触发 | 配合 Webhook,推送即构建 |
| Build after other projects | 上游 Job 构建完触发 | 模块间依赖构建 |
cron 表达式格式是 分 时 日 月 周。比如 H 2 * * 1-5 表示工作日凌晨 2 点执行。H 是 Jenkins 的哈希值,避免所有定时任务都在同一秒触发。
Poll SCM 建议用 Webhook 替代。Poll 本质是轮询,Git 仓库没更新也白查,浪费资源。Webhook 是推送触发,有新提交才通知 Jenkins。
构建步骤
这里是核心,告诉 Jenkins 要执行什么命令。
常用的构建步骤:
| 步骤类型 | 用途 |
|---|---|
| Execute shell | 执行 shell 脚本(最常用) |
| Execute Windows batch command | 执行 Windows 批处理命令 |
| Invoke top-level Maven targets | 执行 Maven 命令 |
| Execute Gradle script | 执行 Gradle 构建 |
| Use builder plugin | 其他插件提供的构建步骤 |
一个典型的 Java 项目构建脚本:
#!/bin/bash
set -e
echo "=== 拉取代码完成 ==="
echo "当前分支: $GIT_BRANCH"
echo "工作目录: $WORKSPACE"
# 切换到项目目录
cd $WORKSPACE
# Maven 编译打包
echo "=== 开始构建 ==="
mvn clean package -DskipTests -B -q
# 检查构建产物
if [ ! -f target/order-api.jar ]; then
echo "构建失败:未找到 target/order-api.jar"
exit 1
fi
echo "构建产物大小: $(du -sh target/order-api.jar)"
echo "=== 构建完成 ==="
几个注意点:
set -e 一定要加。不加的话,某条命令失败了 shell 会继续往下跑,构建"成功"了但产物是旧的。加上了,任何命令非零返回就立即停。
Jenkins 的 shell 默认不是交互式的,~/.bashrc 里配的别名和环境变量不会自动加载。需要的话在脚本开头 source ~/.bashrc 或直接 export PATH=...。
$WORKSPACE 是 Jenkins 注入的环境变量,指向当前 Job 的工作目录。构建产物一般在这里面。
多步骤之间传变量是个坑。如果配了多个 Execute shell 步骤,每个步骤是独立的 shell 进程,上一步 export 的变量下一步拿不到。解决方案是用文件中转(写文件再读文件),或者装 EnvInject 插件。
构建后操作
构建跑完后要做的事。
| 操作 | 说明 | 示例 |
|---|---|---|
| Archive the artifacts | 归档构建产物 | target/*.jar |
| Publish JUnit test result report | 展示单元测试报告 | target/surefire-reports/*.xml |
| Publish HTML reports | 展示 HTML 报告 | JaCoCo 覆盖率报告 |
| Send build artifacts over SSH | 通过 SSH 传文件到远程服务器 | 部署到生产/测试服务器 |
| Editable Email Notification | 构建结果邮件通知 | 失败时发邮件给负责人 |
| Build other projects | 触发下游任务 | 构建完触发部署任务 |
| Delete workspace when build is done | 构建完清工作区 | 省磁盘 |
归档产物一定要配。配了之后,每次构建的产物都会保存下来,后面随时从 Jenkins 界面下载某次构建的 jar 包,不用重新构建。
邮件通知建议只配失败通知。每次成功都发,久了大家当噪音忽略。只在失败和恢复时发,收到的人才会重视。
流水线项目配置详解
两种语法:声明式 vs 脚本式
Pipeline 支持两种写法:
| 对比 | 声明式(Declarative) | 脚本式(Scripted) |
|---|---|---|
| 语法风格 | 结构化,像写 YAML | 自由,像写 Groovy 代码 |
| 外层关键字 | pipeline {} | node {} |
| 学习难度 | 低,语法固定好理解 | 高,需要懂 Groovy |
| 灵活性 | 中等,复杂逻辑用 script {} 块嵌入 | 高,随便写 |
| 官方推荐 | 是 | 否(老项目很多在用) |
日常用声明式就够了。脚本式是 Pipeline 早期的写法,新项目没必要用。下面讲的都是声明式。
一个完整的 Jenkinsfile 长什么样
pipeline {
agent any
environment {
APP_NAME = 'order-api'
VERSION = '1.0.0'
}
parameters {
choice(name: 'DEPLOY_ENV', choices: ['dev', 'test', 'prod'], description: '部署环境')
string(name: 'BRANCH', defaultValue: 'main', description: '构建分支')
booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: '是否跳过测试')
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
sh 'mvn clean package -DskipTests'
}
}
stage('Test') {
when {
expression { !params.SKIP_TESTS }
}
steps {
sh 'mvn test'
}
}
stage('Deploy') {
steps {
sh "./deploy.sh ${params.DEPLOY_ENV}"
}
}
}
post {
always {
junit 'target/surefire-reports/*.xml'
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
cleanWs()
}
success {
echo '构建成功'
}
failure {
emailext subject: '构建失败: ${env.JOB_NAME}',
body: '详情查看: ${env.BUILD_URL}',
to: 'ops-team@example.com'
}
}
}
下面拆开讲每个部分。
agent:在哪执行
agent 告诉 Jenkins 这个 Pipeline 在哪个节点上跑。
// 任意可用节点
agent any
// 不分配全局节点,每个 stage 单独指定
agent none
// 指定标签的节点
agent { label 'linux && docker' }
// 在 Docker 容器中执行
agent {
docker {
image 'maven:3.9-eclipse-temurin-17'
args '-v /root/.m2:/root/.m2 -v /var/run/docker.sock:/var/run/docker.sock'
}
}
Docker agent 是个好东西。比如 Java 项目需要 Maven 3.9 + JDK 17 的环境,不用在 Jenkins 服务器上装这些,直接用 Docker 镜像跑。构建环境跟着镜像走,换机器也不怕。
args 里的 -v 挂载很关键:挂 .m2 目录是为了复用 Maven 本地缓存(不然每次构建都重新下载依赖,慢到怀疑人生);挂 docker.sock 是让容器内能执行 docker 命令。
environment:环境变量
environment {
DEPLOY_ENV = 'prod'
BUILD_TAG = "order-api-${env.BUILD_NUMBER}"
DB_PASSWORD = credentials('mysql-prod-password')
SSH_CREDS = credentials('deploy-ssh-key')
// SSH_CREDS_USR → 用户名
// SSH_CREDS_PSW → 私钥内容
}
credentials() 函数会在运行时从 Jenkins 凭据库取值,日志里显示为 ****,不会泄露。这是 Pipeline 比 Freestyle 安全的地方——Freestyle 的 shell 脚本里写密码,构建日志里可能暴露。
stages 和 stage:阶段
这是 Pipeline 的核心。整个构建流程拆成多个 stage,每个 stage 里有若干 steps。
stages {
stage('Prepare') {
steps {
echo '准备构建环境...'
sh 'java -version'
sh 'mvn -version'
}
}
stage('Build') {
steps {
sh 'mvn clean package -DskipTests'
}
}
// 并行执行
stage('Parallel Tests') {
parallel {
stage('Unit Test') {
steps {
sh 'mvn test'
}
}
stage('Integration Test') {
steps {
sh 'mvn verify -Pintegration'
}
}
}
}
}
并行执行是 Pipeline 的杀手锏。单元测试和集成测试同时跑,构建时间直接砍半。Freestyle 里做不到。
when:条件判断
stage('Deploy to Prod') {
when {
branch 'main'
expression { params.DEPLOY_ENV == 'prod' }
}
steps {
sh './deploy-prod.sh'
}
}
when 指令控制 stage 是否执行。支持的判断条件:
| 条件 | 说明 |
|---|---|
branch 'main' | 分支是 main 时执行 |
expression { ... } | Groovy 表达式为 true 时执行 |
environment name: 'DEPLOY_ENV', value: 'prod' | 环境变量匹配时执行 |
changelog '.*fix.*' | 提交信息匹配时执行 |
buildingTag() | 构建的是 Git Tag 时执行 |
post:构建后操作
post {
always {
junit 'target/surefire-reports/*.xml'
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
}
success {
echo '构建成功'
sh './scripts/notify-dingtalk.sh SUCCESS'
}
failure {
echo '构建失败'
sh './scripts/notify-dingtalk.sh FAILURE'
emailext subject: '构建失败: ${env.JOB_NAME}',
body: '详情查看: ${env.BUILD_URL}',
to: 'ops-team@example.com'
}
cleanup {
cleanWs()
}
}
always 在所有情况都执行,适合放归档和清理。success 和 failure 分别在成功和失败时触发。cleanup 在所有其他 post 条件之后执行,适合做最后的清扫。
Jenkinsfile 放哪
推荐方式:Pipeline script from SCM。把 Jenkinsfile 放在项目代码仓库根目录,Jenkins 从 Git 读取执行。好处是 Jenkinsfile 跟着代码走,改流程提 PR 评审,审计追踪都有。
备选方式:Pipeline script,直接在 Jenkins 网页里粘贴 Groovy 脚本。适合快速验证和临时测试,不推荐长期使用。
多分支流水线
多分支流水线是 Pipeline 的进阶用法,能自动发现 Git 仓库中的所有分支,为每个分支创建一个 Pipeline 任务。
配置步骤:
- 新建任务 → 选 Multibranch Pipeline
- Branch Sources → 添加 Git 仓库
- 配置分支发现策略(发现所有分支,或只发现
feature/*、bugfix/*等) - 配置扫描间隔(比如每小时扫一次新分支)
- 保存
效果:代码仓库有新分支,自动创建对应的 Pipeline 任务;分支删除,对应任务自动清理;每个 PR 自动创建构建任务,PR 合入前验证代码质量。
多分支流水线是 GitOps 工作流的基础。团队用 Git Flow 或 GitHub Flow 的话,强烈建议上。
实战示例:Java 项目发布流程
场景
一个 Spring Boot 项目,代码在 GitLab,要求:
- 支持手动选择环境(dev / test / gray / prod)
- 自动拉代码、编译打包
- 单元测试结果展示
- 部署到对应环境的服务器
- 部署后健康检查
- 失败发邮件通知
Pipeline 版 Jenkinsfile
pipeline {
agent {
docker {
image 'maven:3.9-eclipse-temurin-17'
args '-v /root/.m2:/root/.m2'
}
}
environment {
APP_NAME = 'order-api'
JAR_NAME = 'order-api.jar'
}
parameters {
choice(name: 'DEPLOY_ENV', choices: ['dev', 'test', 'gray', 'prod'],
description: '部署环境')
string(name: 'BRANCH', defaultValue: 'main', description: '构建分支')
booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: '跳过测试')
}
stages {
stage('Checkout') {
steps {
checkout scm
echo "分支: ${params.BRANCH} 环境: ${params.DEPLOY_ENV}"
}
}
stage('Build') {
steps {
sh 'mvn clean package -DskipTests -B -q'
}
}
stage('Test') {
when {
expression { !params.SKIP_TESTS }
}
steps {
sh 'mvn test'
}
post {
always {
junit 'target/surefire-reports/*.xml'
}
}
}
stage('Deploy') {
steps {
script {
def servers = [
dev: '192.168.1.10',
test: '192.168.1.20',
gray: '172.18.247.1',
prod: '172.18.251.114'
]
def target = servers[params.DEPLOY_ENV]
sshagent(['deploy-ssh-key']) {
sh """
scp target/${env.JAR_NAME} deploy@${target}:/opt/app/
ssh deploy@${target} 'systemctl restart ${env.APP_NAME}'
"""
}
}
}
}
stage('Health Check') {
steps {
script {
def servers = [
dev: '192.168.1.10',
test: '192.168.1.20',
gray: '172.18.247.1',
prod: '172.18.251.114'
]
def target = servers[params.DEPLOY_ENV]
retry(6) {
sleep time: 10, unit: 'SECONDS'
def status = sh(script: "curl -s -o /dev/null -w '%{http_code}' http://${target}:8080/actuator/health", returnStdout: true).trim()
if (status != '200') {
error "服务启动失败,健康检查未通过!HTTP状态: ${status}"
}
}
}
}
}
}
post {
success {
echo "部署成功!环境: ${params.DEPLOY_ENV} 分支: ${params.BRANCH}"
}
failure {
echo "部署失败!环境: ${params.DEPLOY_ENV} 分支: ${params.BRANCH}"
emailext subject: '部署失败: ${env.APP_NAME}',
body: '详情查看: ${env.BUILD_URL}',
to: 'ops-team@example.com'
}
always {
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
cleanWs()
}
}
}
Pipeline 做了哪些 Freestyle 做不到的事
- 健康检查阶段:部署后自动检查服务是否启动成功,失败重试 6 次。Freestyle 里也能写 shell 做,但逻辑全混在一个大脚本里,不直观
- 环境变量映射:用
script块根据参数动态选择目标服务器 IP,代码清晰 - Stage View 可视化:Jenkins 界面上看到每个阶段的执行状态和耗时,哪个阶段慢了一目了然
- 版本控制:Jenkinsfile 在 Git 里,改了部署流程有 commit 记录,谁改的、改了什么、什么时候改的都查得到
选型建议
决策树
你的项目有复杂的构建流程吗(多阶段、并行、条件判断)?
├── 是 → Pipeline,没商量
└── 否 → 你的团队有人会写 Groovy 吗?
├── 会 → Pipeline,趁早迁移
└── 不会 → 项目会长期维护吗?
├── 是 → 学一下 Pipeline,值得投入
└── 否 → Freestyle,快速跑起来就行
场景对照
| 场景 | 推荐 | 理由 |
|---|---|---|
| 简单脚本执行 | Freestyle | 杀鸡不用牛刀 |
| Java 项目 CI/CD | Pipeline | 需要编译→测试→部署多阶段 |
| 前端项目构建发布 | Pipeline | 构建→部署→CDN 刷新,有明确阶段 |
| 多环境发布 | Pipeline | 参数化 + 条件部署更灵活 |
| 微服务多服务编排 | Pipeline | 服务间依赖编排,Pipeline 天然支持 |
| 临时数据迁移脚本 | Freestyle | 一次性任务,不值得写 Jenkinsfile |
| 团队刚开始 CI/CD | Freestyle → Pipeline | 先用 Freestyle 跑起来,再逐步迁移 |
从 Freestyle 迁移到 Pipeline
不要一刀切全迁移。按这个步骤来:
- 先拿一个新项目试水:用 Pipeline 从头配,团队熟悉语法和流程
- 挑一个简单的老项目迁移:选构建步骤少的(比如就拉代码跑个
mvn package),迁移风险低 - 逐步迁移复杂项目:把 Freestyle 的 shell 脚本拆成 Pipeline 的 stages
- 用 Blue Ocean 辅助:可视化编辑器能帮你生成 Pipeline 语法
迁移过程中有个实用技巧:Jenkins 内置了 Pipeline Syntax 生成器(Job 配置页左侧菜单 → Pipeline Syntax)。你不知道某个操作用 Pipeline 语法怎么写时,在这个页面选步骤、填参数,点生成,它直接给你吐出 Groovy 代码片段。拉代码、ssh 远程执行、文件传输这些常用操作都能自动生成。
常见坑和排障
构建权限问题
症状:构建报 permission denied,或 shell 脚本里执行某些命令报错。
原因:Jenkins 以 jenkins 用户运行,没有执行某些命令的权限。
# 给 jenkins 用户加 sudo 权限(白名单方式)
sudo visudo -f /etc/sudoers.d/jenkins
# 内容:允许 jenkins 免密执行 systemctl
jenkins ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart order-api, /usr/bin/systemctl stop order-api
不要给 jenkins 用户 ALL 权限——那等于 root 后门。
Git 拉代码超时
症状:构建卡在拉代码阶段,报 git fetch 超时。
排查:
- Jenkins 服务器上手动
git clone试一下,看能不能拉下来 - 检查 SSH 密钥权限:
~/.ssh目录 700,私钥文件 600 - 检查 GitLab 服务器网络是否可达:
curl -v gitlab.example.com - 如果是大仓库,在源码管理的 Additional Behaviours 里加
Sparse Checkout只拉需要的目录
Pipeline 语法错误
症状:Pipeline 执行直接报 org.codehaus.groovy.control.MultipleCompilationErrorsException。
原因:Groovy 语法错误,括号不匹配、引号没闭合之类的。
解决:
- 看错误行号,定位到 Jenkinsfile 的具体行
- 检查大括号
{}和小括号()是否匹配 - 用 Jenkins 的 Replay 功能在线改 Jenkinsfile 试跑,不用提交 Git。Pipeline 运行历史页 → Replay → 修改脚本 → 运行。改对了再提交到 Git
构建产物撑爆磁盘
症状:Jenkins 服务器磁盘满了,构建全部失败。
排查:
# 查看 Jenkins 工作目录大小
du -sh /var/lib/jenkins/
# 找占空间最多的 Job
du -sh /var/lib/jenkins/jobs/*/workspace | sort -rh | head -10
# 找旧构建记录
find /var/lib/jenkins/jobs -name "builds" -type d -exec du -sh {} \; | sort -rh | head -10
解决:
- 在 Job 配置里勾选"丢弃旧的构建",保留 7 天或 20 次
- 构建后操作加"Delete workspace when build is done"
- Pipeline 里加
cleanWs()清理工作区 - 定期跑清理脚本
并发构建冲突
症状:两个构建同时跑,互相覆盖工作区文件。
Freestyle 在 General 里勾选"禁止并发构建"。Pipeline 在 options 里加 disableConcurrentBuilds():
options {
disableConcurrentBuilds()
}
环境变量丢失
症状:Execute shell 里 export 的变量,下一步拿不到。
原因:每个 Execute shell 是独立的 shell 进程,变量不传递。
解决:
- 用文件中转:
echo "VAR=value" > .env,下一步source .env - 装 EnvInject 插件
- 迁移到 Pipeline,用
environment {}块定义全局变量
凭据泄露
症状:构建日志里出现了密码或密钥明文。
Freestyle 的 shell 脚本里直接写密码,构建日志会暴露。解决方案:
- 凭据存到 Jenkins Credentials(Manage Jenkins → Credentials)
- Freestyle 里用 Credentials Binding 插件把凭据注入为环境变量
- Pipeline 里用
credentials()函数引用,日志自动脱敏
附录:常用插件清单
| 插件 | 用途 |
|---|---|
| Git | Git 源码管理 |
| Pipeline | 流水线核心 |
| Blue Ocean | Pipeline 可视化界面 |
| Credentials Binding | 凭据绑定到环境变量 |
| SSH Agent | Pipeline 中使用 SSH 密钥 |
| Docker Pipeline | Pipeline 中使用 Docker |
| Build Timeout | 构建超时自动中断 |
| ANSI Color | 控制台日志彩色输出 |
| Timestamper | 日志加时间戳 |
| Workspace Cleanup | 清理工作区 |
总结
Freestyle 和 Pipeline 不是对立关系,是互补的。简单任务用 Freestyle,复杂流程用 Pipeline,这是最务实的搭配。
从实际经验来看,Pipeline 的学习成本没有想象中那么高。声明式语法的结构就是 pipeline → agent → stages → stage → steps,写一两个 Jenkinsfile 就上手了。而它带来的收益是实打实的:版本控制、可视化、并行执行、条件判断、代码评审——这些在 Freestyle 里要么做不到,要么做起来很别扭。
如果是新项目,直接上 Pipeline。如果已有大量 Freestyle Job,别急着迁移,先拿一个新项目试水,团队熟悉了再逐步推进。
最后提醒一点:不管用哪种风格,凭据管理一定要走 Jenkins Credentials,不要在脚本里硬编码密码。构建日志的泄露风险远比你想的大。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- Working with projects — Jenkins 官方文档,项目类型说明与 Pipeline/Freestyle 对比
- Getting started with Pipeline — Jenkins 官方文档,Pipeline 基础概念与创建方式
- 流水线语法 — Jenkins 官方中文文档,声明式与脚本式语法详解
- 使用 Jenkinsfile — Jenkins 官方中文文档,Jenkinsfile 最佳实践
- 执行多个步骤 — Jenkins 官方中文文档,Pipeline steps 与 timeout/retry 用法
- Blue Ocean — Jenkins 官方文档,Blue Ocean 可视化与 Pipeline 编辑器
- Jenkins 三种常用项目构建类型 — 博客园,Freestyle/Maven/Pipeline 三种类型对比实操