声明式Pipeline语法:核心五要素
声明式Pipeline,说白了就是一套结构化的流水线写法。我刚开始学的时候,觉得它比脚本式啰嗦,但后来发现——嗯,这玩意儿才是团队协作的基石。你想想看,代码写清楚了结构,别人一看就懂,维护起来省心多了。
这一节咱们拆开看看:pipeline块、agent、stages、steps、post。这五个东西,几乎能搞定你90%的持续集成场景。
1. pipeline块:一切从这里开始
pipeline块是声明式流水线的根。任何东西都要包在它里面。说白了,文件的最外层必须是pipeline,不能多也不能少。
pipeline {
// 剩下的都写在这里
}
⚠️ 注意:pipeline块外面不能有别的代码块,比如node、stage。我见过有人把pipeline包在node里面,结果Jenkins直接报错。声明式就是声明式,别混用。
我在项目中遇到过一个问题:有些同事喜欢把变量定义放在pipeline外面,认为“这样全局能用”。其实不对,声明式Pipeline的变量最好用environment块或者script块里定义,否则容易出作用域问题。
2. agent:告诉Jenkins去哪儿跑
agent指定流水线在哪个节点上执行。常用的有这些:
| 类型 | 写法 | 说明 |
|---|---|---|
| any | agent any |
任何可用的节点 |
| label | agent { label 'linux' } |
匹配标签的节点 |
| docker | agent { docker 'maven:3.8' } |
在容器里跑,省环境配置 |
| none | agent none |
全局不分配,stage单独指定 |
我个人习惯在顶层用agent none,然后在每个stage里单独指定agent。这样不同的阶段可以用不同的环境,比如编译用JDK镜像,部署用kubectl镜像。不过,有一个坑:如果你在某个stage里忘了写agent,那这个stage就没地方运行,流水线会卡住。
💡 我建议:如果所有stage都在同一个节点上,直接写agent any或者agent { label 'master' }就行,省事。
曾经有一个项目,我用了agent docker,但镜像太大,每次拉取要5分钟。后来我发现可以用dockerfile参数直接构建自己的镜像,但要注意缓存问题。嗯,这又是另一个故事了。
3. stages & stage:流水线的骨架
stages块里包含多个stage,每个stage代表一个逻辑阶段。比如:编译、单元测试、打包、部署。每个stage里必须有一个steps。
stages {
stage('编译') {
steps {
echo '开始编译...'
sh 'mvn compile'
}
}
stage('测试') {
steps {
sh 'mvn test'
}
}
}
这里要注意几点:
- stage名字要有意义:别用stage1、stage2,蓝海里看到全是英文名,运维会骂人的。
- 顺序执行:默认stage按书写顺序一个个跑,除非你用parallel做并行。
- 可以嵌套:stage里还可以用parallel或者matrix,但别太深,不然可读性下降。
我见过有人把几十个步骤写在一个stage里,结果失败时根本看不清是哪一步出的问题。所以,尽量按职责拆分stage,每个stage只做一件事。
4. steps:具体干活的地方
steps就是stage里执行的具体操作。每个stage必须有且只能有一个steps块(里面可以有多条指令)。
常用的steps命令:
sh:执行Shell命令echo:打印信息checkout scm:拉取代码script:嵌入脚本式代码(必要时才用)sleep:等待
⚠️ 别把steps写成复数!我一直看到有人写成steps { ... }但内部写成了step { ... },少了一个s,语法错误。
我记得有一次排查问题,发现某个stage的steps里写了sh 'npm install',但没给node镜像。结果agent用的是jdk镜像,根本没有npm。所以steps的执行环境取决于agent。
5. post:无论成败,都得收拾
post块用于定义流水线或stage执行后的条件处理。它必须放在stages之后(如果是stage级别,则放在steps之后)。
post {
always {
echo '无论结果如何,我都会执行'
}
success {
echo '成功了!'
}
failure {
echo '失败了,赶紧查日志'
}
unstable {
echo '不太稳定,但没完全失败'
}
changed {
echo '状态变了,比如从失败变成成功'
}
}
我个人习惯在post的always里加一个清理步骤,比如删除临时文件,或者发送通知。工作中经常用failure发邮件给开发,success发给QA。
💡 小技巧:post可以放在pipeline顶层(全局),也可以放在stage内部(局部)。全局post会在整个流水线结束后运行,局部post只在那个stage结束后运行。
曾经有一个坑:我在stage里用了post的failure,想发钉钉通知,但忘了在全局agent none的情况下,stage的post也会继承agent。结果那个stage的agent是none,post根本没地方跑。所以,如果你在stage里用post,最好显式指定agent。
知识点总结
为了方便回顾,我把这五要素整理成一个速查表:
| 要素 | 用途 | 常见坑 |
|---|---|---|
| pipeline | 根容器 | 别在外部包别的块 |
| agent | 指定运行节点 | 忘记写agent导致无节点 |
| stages/stage | 流程阶段 | stage内必须有steps |
| steps | 具体操作 | 单数写法错误 |
| post | 结束后的条件处理 | 局部post需考虑agent |
整体结构图
下面这张图可以帮你理解声明式Pipeline的层级关系: