Jenkins 上下游 Job + Docker 镜像部署完整实战(避坑版)
目标读者:
- 正在使用 Jenkins 做 CI/CD 的开发 / 运维
- 已经踩过坑、或者马上就要踩坑的人
- 想把「能跑」升级成「专业、稳定、可扩展」的人
一、背景说明(为什么会写这篇)
在实际项目中,我们经常会遇到这样的场景:
-
有一个 上游 Job
- 负责:编译 → 构建 → 打包 → Docker build → Docker push
-
有一个或多个 下游 Job
- 负责:从镜像仓库拉镜像 → 部署到多台服务器 / 多实例
看起来很简单,但在 Jenkins 里:
99% 的问题,不是 Docker,不是代码,而是"触发方式 + 参数传递"
本篇文章,就是一次完整的「踩坑 → 定位 → 纠正 → 升级」全过程总结。
二、整体架构一图看懂(先有全局感)
text
┌────────────────────┐
│ 上游 Job │
│ luge.api.consume │
│ │
│ 1. 编译 │
│ 2. Docker build │
│ 3. Docker push │
│ 4. 生成 tag │
│ 5. 触发下游 │
└─────────┬──────────┘
│ 传参 (IMAGE_TAG)
▼
┌────────────────────────────┐
│ 下游 Job(部署流水线) │
│ │
│ - SSH 到服务器 │
│ - docker pull 镜像 │
│ - docker run 多实例 │
└────────────────────────────┘
记住一句话:
上游负责"产出事实",下游负责"执行事实"
三、准备工作:必备插件与环境说明
在开始之前,先把一个经常被忽略、但极其关键的前提说清楚:
Jenkins 的很多"神奇行为",本质都是插件是否安装、是否理解导致的。
3.1 必备 Jenkins 插件清单(缺一不可)
| 插件名称 | 作用 | 是否必须 |
|---|---|---|
| Pipeline | 支持 Jenkinsfile | ✅ |
| Pipeline: Groovy | 支持 Pipeline 语法 | ✅ |
| SSH Pipeline Steps / Publish Over SSH | 通过 SSH 执行远程命令 | ✅ |
| Parameterized Trigger | 上游向下游传参 | ✅ |
| Docker Pipeline(可选) | Docker 相关语法封装 | ⭕ |
⚠️ 重点说明:
Trigger parameterized build on other projects就来自 Parameterized Trigger 插件- 如果你看不到"传参"相关选项,99% 是插件没装或版本太低
3.2 插件检查路径
text
Jenkins → 系统管理 → 插件管理 → 已安装
确认以上插件存在后,再继续下面步骤。
四、上游 Job:只做一件事 ------ 产出「可用镜像」
3.1 上游 Job 的关键参数
上游是一个 Multi-configuration / 参数化 Job,核心参数如下:
| 参数名 | 说明 | 示例 |
|---|---|---|
| imageName | 镜像名 | luge.api.consume |
| tag | 镜像 tag | 202601291616 |
| port | 构建时用 | 6300 |
最终 push 到仓库的镜像为:
text
crpi-xxx.aliyuncs.com/tmios/luge.api.consume:202601291616
⚠️ 注意:
是否存在latest完全取决于你有没有 pushlatest
4.2 上游触发下游(唯一推荐方式)
在 上游 Job → 构建后操作 中:
选择:
✅ Trigger parameterized build on other projects
配置要点:
-
Projects to build:
text下游 Job 名 -
参数传递方式:
textPredefined parameters -
示例:
textIMAGE_TAG=${tag}
这一步的含义是:
上游明确告诉下游:
"我刚刚产出的镜像,用这个 tag 去部署"
五、参数是如何从上游"流"到下游的(核心原理)
这是整套体系里最容易出错、但也是最关键的一环。
5.1 参数传递的真实链路
text
上游 Job 参数
↓
上游构建产出(Docker push)
↓
构建后操作:Trigger parameterized build
↓
参数注入到下游 Job
↓
下游 Pipeline parameters {}
一句话总结:
参数不是"自动继承"的,而是"上游显式注入"的。
5.2 上游如何传参(配置示例)
在【上游 Job → 构建后操作】中:
选择:
text
Trigger parameterized build on other projects
关键配置:
-
Projects to build
text下游 Job 名 -
Add Parameters → Predefined parameters
textIMAGE_TAG=${tag}
这里的 ${tag}:
- 来自上游 Job 的参数
- 或构建过程中生成的环境变量
5.3 下游如何"接住"参数
在下游 Jenkinsfile 中:
groovy
parameters {
string(name: 'IMAGE_TAG', description: '上游传入的镜像 tag')
}
随后在 Pipeline 中即可直接使用:
groovy
env.IMAGE_FULL = "xxx/luge.api.consume:${IMAGE_TAG}"
⚠️ 如果 IMAGE_TAG 为空:
- 说明 上游没有传
- 或传参方式选错(没用 Predefined parameters)
六、下游 Job:只做部署,不关心构建
4.1 下游 Job 的正确触发认知
下游 Job 不需要监听任何上游
也就是说:
- ❌ 不要勾选:Build after other projects are built
- ❌ 不要在 Pipeline 里写
build job:
下游只接受一种触发:
✅ 被上游"点名调用"
4.2 下游 Pipeline 参数定义
groovy
parameters {
string(name: 'IMAGE_TAG', description: '上游传入的镜像 tag')
}
这是整个部署是否成功的 生命线参数。
4.3 下游部署核心流程
text
接收 IMAGE_TAG
↓
拼接完整镜像地址
↓
SSH 登录服务器
↓
停止旧容器
↓
拉取新镜像
↓
启动新容器(多实例)
五、为什么会出现「下游 Pending 两个任务」?
这是 Jenkins 的经典误区。
5.1 根因分析
当以下 两种触发方式同时存在 时:
- 上游:Trigger parameterized build on other projects
- 下游:Build after other projects are built
结果就是:
同一个构建,被触发了两次
Jenkins 表现为:
- Queue 里出现两个 Pending
- Build number 看起来一样
- executor 不够时尤为明显
5.2 解决原则(记住这句话)
一个下游 Job,只允许有一个触发源
最终推荐:
text
上游主动触发 ✔
下游不监听 ✔
六、为什么"我没勾监听,下游还是触发了"?
答案只有一个:
这是上游主动触发的,和监听无关
验证方式:
在下游 Build 页面,你一定能看到:
text
Started by upstream project luge.api.consume
看到这行字,说明配置是完全正确的。
七、最终推荐的稳定架构(生产级)
text
┌──────────────┐
│ 上游 CI Job │
│ │
│ Docker Build │
│ Docker Push │
│ │
│ Trigger 下游 │
└──────┬───────┘
│ IMAGE_TAG
▼
┌────────────────────┐
│ 下游 CD Pipeline │
│ │
│ SSH + Docker Deploy│
│ Multi Instance │
│ disableConcurrent │
└────────────────────┘
并在下游加一道保险:
groovy
options {
disableConcurrentBuilds()
}
八、写在最后:从"能跑"到"体系化"
如果你走到了这里,其实你已经超过了 80% 的 Jenkins 使用者。
再往前一步,就是:
- 蓝绿发布
- 灰度切流
- 失败自动回滚
- 镜像版本可追溯
- 发布审计
CI/CD 的本质不是 Jenkins,而是"工程化思维"
九、下一篇可以写什么?
- Jenkins + Docker 蓝绿发布实战
- 如何设计可回滚的镜像 Tag 体系
- Jenkins 参数治理与发布规范
- 从流水线到发布平台的演进
如果这篇文章帮你少踩一个坑,那它就值了。