动机
我们来思考下持续交付的原则。
- 每次构建的结果可能是一个潜在的发行版本
- 消除手动瓶颈
- 尽可能自动化
这三点正是我们想要实现的,但是在实现之前,我们先来看下在典型的Maven发布流程和经典方式版本号管理上的具体问题。
没有自动化
通常来说,一次提交会触发一个快照构建,然后生成一个快照构件(“8.1.2-SNAPSHOP”)。当开发者感觉软件到达稳定状态后,他会触发一次专用发布构建。因此,他预先分配版本号(“8.2.0”)
人员必须分配版本号然后触发发布构建
这种方式有什么缺点:
- 我们需要手动触发专用的发布工作流程
- 版本号需要人为手动分配
- 此外,仅看版本号“8.2.0”并不清晰,比如这个版本号的构件中包含了哪些提交(代码)。我们需要一个Git标签
任意快照
此外,快照引起了许多问题。
不清晰的内容变动
- 不可追踪。我们不能说,有哪些提交包含在了当前的快照构件中。
- 不可靠。快照构件可以很快速的修改。这样很容易引起问题
- 易出错。不过不注意,很容易覆盖一个快照(比如:当创建Git分支的时候,忘记修改POM文件中的版本号。从Git分支上构建会覆盖之前的快照)
让人生气的Maven发布插件
最后,Maven的发布插件也引起了许多问题:
- 开销。Maven发布插件运行3次完整的构件和测试周期,展开POM文件两次和创建三次Git修订(Git revisions)
- 不具有隔离性。当其他人在发布版本期间提交更改时,插件可能容易陷入混乱。
- 不具有原子性。如果最后一步出现问题(比如:在构件上传期间),突然发现紧急Bug。我们必须清理创建的Git标签,Git修订和修复错误版本
解决方案:利用Git提交哈希
我们使用Git提交哈希来作为构件的版本号
使用Git提交哈希作为版本号
- 每个构建都incident,有一个机器分配的唯一号码
- 因此,每个构件都是潜在可交付的。所以这里不需要一个专用的发布流程。
- => 交付管道大大简化和自动化
- 可追踪。这很明显,哪些提交被包含到构件中。
- 重复性。只需检出版本并再次进行构建构件(假设没有快照依赖)。
- 基本上,Git标签不再需要了。
实现
让我们在Maven构建中集成版本命名。下面的事例构建生成一个简单的Spring Boot服务作为Fat Jar。此外,我们把Fat Jar包装到Docker镜像中,并部署镜像。完整的源码可以在中查看。
为了增加人的可读性,我们把时间戳作为Git提交哈希的前缀。因此,我们的版本号如下所示:
format: yyyyMMdd-HHmmss.abbreviatedCommitHashexample: 20160702-152019.75c54f5
使用Git提交哈希作为版本号
这是最重要的一步,我们使用 。这个插件可以从.git目录读取Git提交哈希和提交时间戳,并可以在POM中可以使用Maven属性使用。
pl.project13.maven git-commit-id-plugin 2.2.1 validate revision yyyyMMdd-HHmmss ${project.basedir}/.git false
下一步,我们可以在Maven属性中添加包含了Git提交哈希和提交时间戳的版本号属性。
${git.commit.time}.${git.commit.id.abbrev}
现在,我们可以在version标签中使用这个属性。
de.philipphauer.blog versioning-continuous-delivery ${version.number}
使用方式:
> mvn package # creates the jar with the desired version number> java -jar target/versioning-continuous-delivery-20160702-180249.193a613.jar # starts the service
使用版本号创建、标记Docker镜像
我们使用 io.fabric8 的来创建镜像。重要的部分是标签。我们创建两个标签。一个标签以提交ID命名(“20160702-153902.7c12eb”),另一个以“latest”命名。latest标签将会简化本地测试。
io.fabric8 docker-maven-plugin 0.15.9 true phauer docker123 ${docker.repository.name}:${version.number} ${project.artifactId} anapsix/alpine-java:jre8 latest assembly.xml 8080 java -jar /maven/${project.artifactId}-${version.number}.jar build-docker-image package build push-docker-image-to-registry deploy push
不要忘记assembly.xml和如下的属性:
phauer/${project.artifactId}
用法:
> mvn package # creates a docker image and installs it to the local repository> docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEphauer/versioning-continuous-delivery 20160702-174649.28b5299 12d7a526c506 2 seconds ago 135.8 MBphauer/versioning-continuous-delivery latest 12d7a526c506 2 seconds ago 135.8 MB> docker-compose up # starts the local image with the tag "latest"> mvn deploy # pushes the image to the docker registry
完整的和可以在GitHub中查看。
开放问题和讨论
部署构件的Git标签
有时,在真正部署的时候,使用专用的Git标签来标记一个构件是非常方便的。你可以在部署后自由标记相应的Git修订版本。你可以手动执行此操作,也可以在最终的deploy流程中集成。后者使工作流程自动化。
构件泛滥问题的处理
每次构建都会带出一个新的构件,我们在我们的存储库中面临着大量的构件。因此,我们必须清理:
- 但是我们怎么知道已经部署了哪些版本?我们不想删除当前正在生产环境的构件。因此,我们必须维护已部署的版本列表,并在清理期间跳过他们。我们可以通过使用Git标签来实现此目的,即当构件部署的时候,我们创建一个Git标签。我们也可以在任意地方保存版本号列表。
- 说实话,删除曾经是生产中的构件没有问题。你曾回滚到一年前的构件过吗?通常来说,你只需要当前部署的构件和它之前几个。
检查快照依赖关系
当发现快照依赖时,maven-release-plugin会中止构建。这基本上没问题,因为快照依赖阻止了构建的可重复性。我们缺乏这种检查。但是,我认为这在实践中不是什么大问题。
- 这很少发生。通常开发人员在发布快照依赖之前会注意并删除快照依赖。
- 当我们对我们的库应用建议的版本号概念时,我们根本没有快照。
- 即使我们的构件包含快照,快照仍然捆绑在构件(fat jar或者Docker镜像)中。构件保存在注册表中,然后测试到最终发布。如果我们在我们的注册表中有构建的构件,我们真的需要重复构建吗?
使用构建时间戳替换Git提交哈希
有一种版本方式是使用构建的时间戳来替换提交哈希。因为下面的原因,我更倾向于提交哈希:
- 显示的跟踪版本号提交
- 基本上不需要Git标签
- 基于给定修订,可在现构件
Docker是此版本控制方法所必须的吗?
基本上不是。如果你的Maven构建只生成一个jar,也可以应用建议的方法。在这种情况下,我们还是使用Git提交哈希作为Jar的版本号。
但是,有一个问题,有git-commit-id-plugin插件创建的默认属性不会被maven-install-plugin和maven-deploy-plugin插件解析到。有一个解决方案。
此外,Docker让事情变得更简单。Maven只允许一个构件只能有一个版本号。Docker允许一个镜像有多个标签。你可以使用“7d1dcc”和“latest”共同标记一个镜像。latest对于本地测试非常有用。你可以一直使用latest镜像,而不是永远的提升版本号(比如在本地中)
----------------------------------------------------------------------------------------------------
原文:https://blog.philipphauer.de/version-numbers-continuous-delivery-maven-docker/