发布工程(一):什么是发布——制品全生命周期的起点

很多团队搞 DevOps,流水线搭了、K8s 上了、监控配了,但说到“发布”,第一反应就是“打一个 Git Tag,然后部署上线”。这当然不算错,但如果你把发布仅仅理解为“打 Tag”,那你可能会忽略一个关键环节:从构建产物到可交付版本之间,还隔着一个制品管理的世界。

一、发布不是部署

在 DevOps 回环里,“发布”和“部署”是两个截然不同的环节。

发布(Release)要回答的问题是:我们交付出去的到底是什么?你可以把它理解为“制造一个确定的、可追溯的、有版本标识的交付物”。在这个环节,你要做的事情包括:给制品打上版本号、生成变更日志、归档到制品仓库。发布的结果,是一个“可以被部署的版本”。

部署(Deploy)要回答的问题是:怎么让这个版本在目标环境里跑起来?蓝绿发布、金丝雀发布、滚动更新——这些都是部署策略,不是发布的事情。部署的前提是:已经有一个明确的、可追溯的版本存在了。

如果把这个关系类比成工厂生产,发布是“质检合格、贴标签、入库”,部署是“从仓库取出、运到现场、安装调试”。没有前者,后者就不知道自己在部署什么。

二、什么是制品?

制品(Artifact)是构建过程产出的、可以直接部署的产物。常见的制品包括:

  • Java 的 JAR/WAR 包
  • Docker 镜像
  • 前端项目的 dist 目录(打包成 tar.gz)
  • npm 包(.tgz)
  • Python 的 wheel 包
  • Go 的二进制文件

一件制品至少需要具备两个属性才能被称为“可发布的制品”:

第一,不可变性(Immutability)。 同样的版本号,产出的制品必须在任何时间、任何环境里都是同一个。这意味着你不能在发布之后偷偷替换制品——如果需要修改,就发新版本。这也是为什么 Docker 镜像有 sha256 digest、Maven 会校验 pom 文件:都是为了确保你拿到的东西和你以为的东西是同一个。

第二,可追溯性(Traceability)。 看着一个制品,你应该能回答:它是由哪次提交构建的?在哪个流水线里跑的?用的是什么版本的依赖?这些信息,有的嵌入在制品的元数据里(比如 JAR 包的 MANIFEST.MF),有的则需要通过制品仓库来关联(比如 Git commit SHA 映射)。

三、快照版 vs 发行版

制品管理里有一个最基本也最容易混淆的概念:快照版(Snapshot)和发行版(Release)。

快照版是开发过程中的中间态产物。你每次提交代码,CI 流水线自动构建出一个新版本,覆盖掉旧的。快照版的特点是可以被反复覆盖、随时变化。在 Maven 里,1.0.0-SNAPSHOT 就代表“这个版本还没定,内容随时可能变”。你不能拿一个快照版去生产环境部署,因为它不是一个确定的版本——今天部署的和明天部署的,可能不是同一个东西。

发行版是经过确认的、不可变的最终版本。一旦发布,就不能再改。Maven 的 1.0.0(不带 SNAPSHOT 后缀)就是一个发行版。你推上去了,它就是它,任何人都可以随时用它来复现一个部署。

这个区分决定了制品仓库的存储策略。快照版可以定期清理(保留最近 N 个构建即可),发行版则应该永久保留(或者至少按合规要求保留若干年)。

四、为什么不能只靠文件系统?

很多小团队一开始的做法是:构建产物丢在 Jenkins workspace 里,或者 scp 到某台“发布服务器”上。这在小规模下跑得通,但会带来几个头疼的问题:

找不到历史版本。 磁盘满了,清一清,上个月的构建产物就没了。等到要回滚的时候,发现没有制品可用——只能重新拉代码重新构建,但谁保证重新构建出来的东西跟当初上线的一致?

版本管理混乱。 文件名叫 app-20240315.jarapp-latest.jarapp-fix.jar,三个月后谁也说不清哪个是哪个。

安全问题。 文件系统没有访问控制,谁都可以删除或覆盖制品。也没有元数据管理,不知道这个制品的扫描结果、依赖信息、发布时间。

制品仓库就是为了解决这些问题而生的。它的核心能力包括:

  • 存储与索引:按版本、坐标组织制品,支持按条件检索
  • 访问控制:谁能上传、谁能下载、谁能删除
  • 元数据管理:关联构建信息、漏洞扫描结果、过期标记
  • 代理与缓存:代理外部仓库(如 Maven Central),加速下载、避免外网依赖

五、发布在 DevOps 回环中的位置

让我们回到 DevOps 回环的整体视角:

需求 → 代码 → 构建 → 测试 → 发布 ─→ 部署 → 监控 → 反馈
                              ↑                       ↓
                              └───────────────────────┘

“发布”这个环节,承接的是测试通过之后的构建产物,输出的是一个“可部署的版本”。从左边看,它依赖 CI 流水线产出的制品;从右边看,它为部署环节提供明确的版本输入。

为什么很多团队跳过了这个环节?因为在小团队里,“构建完直接部署”似乎更快。但随着系统规模增长、环境数量增加、合规要求提高,缺少发布环节的代价会越来越大——出问题找不到是哪个版本引入的、回滚时没有信心的制品可用、多个环境之间的版本关系理不清楚。

发布工程的核心,就是把“版本管理”这件事情从“顺便做做”变成“系统性地做好”。

六、这个系列会聊什么?

本系列共七篇,覆盖发布工程的核心内容:

内容
一(本篇) 什么是发布——制品全生命周期的起点
版本号规范——SemVer 与 CalVer
变更日志与 Release Notes
Nexus——Java 私服与通用制品库
Harbor——容器镜像仓库
JFrog Artifactory——企业级制品管理
选型对比与制品清理策略

下一篇文章,我们聊聊版本号到底应该怎么打:SemVer 好还是 CalVer 好?什么时候用 1.2.3,什么时候用 2024.03.15

每天前进一小步,就是一个新的高度!