概述

用 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 仓库读这个文件,按步骤执行。看起来多了几行代码,但好处后面会讲到。

九个维度的差异

维度FreestylePipeline
配置方式网页表单代码(Jenkinsfile)
版本控制做不到Jenkinsfile 在 Git 里,有提交记录
复杂流程多步骤能配,但逻辑全混在 shell 里stages 分阶段,清晰可读
条件判断很弱,只能靠 shell 脚本 if/elsewhen 指令原生支持
并行执行不支持parallel 块直接支持
可视化只有构建历史Stage View 看每个阶段耗时和状态
可复用性克隆 Job,改改参数Jenkinsfile 共享库,跨项目复用
代码评审做不到改 Jenkinsfile 提 PR,团队评审
灾难恢复Job 配置在 Jenkins 内部,迁移要搬 XMLJenkinsfile 在 Git,恢复就是重新拉

Freestyle 最致命的短板是版本控制。你改了 Job 配置,没有 commit 记录,谁改的、改了什么、什么时候改的一概查不到。线上炸了想回滚?只能靠记忆或者翻 Jenkins 操作日志。Pipeline 把构建流程变成代码,这些全不是问题了。

Freestyle 还值得用的四个场景

话虽如此,Freestyle 没死。下面四种情况用它更合适:

  1. 一次性任务:跑个数据迁移、清理个日志,配完就不管了
  2. 极简构建:就拉代码跑个 mvn package,没有多阶段编排的需求
  3. 团队刚开始搞 CI/CD:先让流水线跑起来建立信心,后面再迁移
  4. 不想学 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 方式拉代码的步骤:

  1. Jenkins 服务器上生成密钥对:ssh-keygen -t ed25519 -f ~/.ssh/jenkins_deploy
  2. 公钥(.pub 文件内容)添加到 Git 仓库的 Deploy Keys
  3. Jenkins → Manage Jenkins → Credentials → System → Global credentials → 添加 SSH Username with private key,把私钥贴进去
  4. 源码管理的 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 triggerGit 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 在所有情况都执行,适合放归档和清理。successfailure 分别在成功和失败时触发。cleanup 在所有其他 post 条件之后执行,适合做最后的清扫。

Jenkinsfile 放哪

推荐方式:Pipeline script from SCM。把 Jenkinsfile 放在项目代码仓库根目录,Jenkins 从 Git 读取执行。好处是 Jenkinsfile 跟着代码走,改流程提 PR 评审,审计追踪都有。

备选方式:Pipeline script,直接在 Jenkins 网页里粘贴 Groovy 脚本。适合快速验证和临时测试,不推荐长期使用。

多分支流水线

多分支流水线是 Pipeline 的进阶用法,能自动发现 Git 仓库中的所有分支,为每个分支创建一个 Pipeline 任务。

配置步骤:

  1. 新建任务 → 选 Multibranch Pipeline
  2. Branch Sources → 添加 Git 仓库
  3. 配置分支发现策略(发现所有分支,或只发现 feature/*bugfix/* 等)
  4. 配置扫描间隔(比如每小时扫一次新分支)
  5. 保存

效果:代码仓库有新分支,自动创建对应的 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 做不到的事

  1. 健康检查阶段:部署后自动检查服务是否启动成功,失败重试 6 次。Freestyle 里也能写 shell 做,但逻辑全混在一个大脚本里,不直观
  2. 环境变量映射:用 script 块根据参数动态选择目标服务器 IP,代码清晰
  3. Stage View 可视化:Jenkins 界面上看到每个阶段的执行状态和耗时,哪个阶段慢了一目了然
  4. 版本控制:Jenkinsfile 在 Git 里,改了部署流程有 commit 记录,谁改的、改了什么、什么时候改的都查得到

选型建议

决策树

你的项目有复杂的构建流程吗(多阶段、并行、条件判断)?
├── 是 → Pipeline,没商量
└── 否 → 你的团队有人会写 Groovy 吗?
    ├── 会 → Pipeline,趁早迁移
    └── 不会 → 项目会长期维护吗?
        ├── 是 → 学一下 Pipeline,值得投入
        └── 否 → Freestyle,快速跑起来就行

场景对照

场景推荐理由
简单脚本执行Freestyle杀鸡不用牛刀
Java 项目 CI/CDPipeline需要编译→测试→部署多阶段
前端项目构建发布Pipeline构建→部署→CDN 刷新,有明确阶段
多环境发布Pipeline参数化 + 条件部署更灵活
微服务多服务编排Pipeline服务间依赖编排,Pipeline 天然支持
临时数据迁移脚本Freestyle一次性任务,不值得写 Jenkinsfile
团队刚开始 CI/CDFreestyle → Pipeline先用 Freestyle 跑起来,再逐步迁移

从 Freestyle 迁移到 Pipeline

不要一刀切全迁移。按这个步骤来:

  1. 先拿一个新项目试水:用 Pipeline 从头配,团队熟悉语法和流程
  2. 挑一个简单的老项目迁移:选构建步骤少的(比如就拉代码跑个 mvn package),迁移风险低
  3. 逐步迁移复杂项目:把 Freestyle 的 shell 脚本拆成 Pipeline 的 stages
  4. 用 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 超时。

排查:

  1. Jenkins 服务器上手动 git clone 试一下,看能不能拉下来
  2. 检查 SSH 密钥权限:~/.ssh 目录 700,私钥文件 600
  3. 检查 GitLab 服务器网络是否可达:curl -v gitlab.example.com
  4. 如果是大仓库,在源码管理的 Additional Behaviours 里加 Sparse Checkout 只拉需要的目录

Pipeline 语法错误

症状:Pipeline 执行直接报 org.codehaus.groovy.control.MultipleCompilationErrorsException

原因:Groovy 语法错误,括号不匹配、引号没闭合之类的。

解决:

  1. 看错误行号,定位到 Jenkinsfile 的具体行
  2. 检查大括号 {} 和小括号 () 是否匹配
  3. 用 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

解决:

  1. 在 Job 配置里勾选"丢弃旧的构建",保留 7 天或 20 次
  2. 构建后操作加"Delete workspace when build is done"
  3. Pipeline 里加 cleanWs() 清理工作区
  4. 定期跑清理脚本

并发构建冲突

症状:两个构建同时跑,互相覆盖工作区文件。

Freestyle 在 General 里勾选"禁止并发构建"。Pipeline 在 options 里加 disableConcurrentBuilds()

options {
    disableConcurrentBuilds()
}

环境变量丢失

症状:Execute shell 里 export 的变量,下一步拿不到。

原因:每个 Execute shell 是独立的 shell 进程,变量不传递。

解决:

  1. 用文件中转:echo "VAR=value" > .env,下一步 source .env
  2. 装 EnvInject 插件
  3. 迁移到 Pipeline,用 environment {} 块定义全局变量

凭据泄露

症状:构建日志里出现了密码或密钥明文。

Freestyle 的 shell 脚本里直接写密码,构建日志会暴露。解决方案:

  1. 凭据存到 Jenkins Credentials(Manage Jenkins → Credentials)
  2. Freestyle 里用 Credentials Binding 插件把凭据注入为环境变量
  3. Pipeline 里用 credentials() 函数引用,日志自动脱敏

附录:常用插件清单

插件用途
GitGit 源码管理
Pipeline流水线核心
Blue OceanPipeline 可视化界面
Credentials Binding凭据绑定到环境变量
SSH AgentPipeline 中使用 SSH 密钥
Docker PipelinePipeline 中使用 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,不要在脚本里硬编码密码。构建日志的泄露风险远比你想的大。

参考资料与致谢

本文在撰写过程中参考了以下资料,感谢原作者的贡献:

  1. Working with projects — Jenkins 官方文档,项目类型说明与 Pipeline/Freestyle 对比
  2. Getting started with Pipeline — Jenkins 官方文档,Pipeline 基础概念与创建方式
  3. 流水线语法 — Jenkins 官方中文文档,声明式与脚本式语法详解
  4. 使用 Jenkinsfile — Jenkins 官方中文文档,Jenkinsfile 最佳实践
  5. 执行多个步骤 — Jenkins 官方中文文档,Pipeline steps 与 timeout/retry 用法
  6. Blue Ocean — Jenkins 官方文档,Blue Ocean 可视化与 Pipeline 编辑器
  7. Jenkins 三种常用项目构建类型 — 博客园,Freestyle/Maven/Pipeline 三种类型对比实操