自从在Go 1.11和高版本中引入了Go的新管理系统以来,GoLang开发人员已经接受了包版本控制解决方案。这样做的用户可以使用GoCenter存储库中的不可变公共Go 模块,并通过健壮、可靠的Go Pipeline获得快的构建速度。
但是,将现有的项目转换为使用Go Module并不总是很容易,如果该项目已经尝试过GoLang的其他包管理解决方案时。
为了帮助GoLang社区正确地使用Go Module,我们将使用开源的etcd项目(Kubernetes使用的键值数据存储)作为示例。这是一个实践的实际示例,因为它很复杂,可以展示一些常见的实践
Go 项目依赖管理痛点分析
传统GO项目进行第三方模块依赖时,往往是去下载第三方源码,这种方式将存在以下常见问题:
1.性能及稳定性:每次下载从各大VCS系统下载源码性能低,依赖网络环境,稳定性差
2. 一致性&可重复性:容易收到依赖源的影响,我们往往在感知不到模块提供方的改动时,就下载了新版的代码,两次依赖某模块得到的依赖不一致,往往造成前一秒还行,下一秒构建失败的情形,尤其在持续集成系统中
3. 协作:源码方式模块基本无版本概念,或不是语义类型,多团队协作困难
基于以上问题及痛点,建议转换为Go Module 模式管理Go 项目依赖。附Go Module 基于Go Proxy进行依赖下载的原理图:
应用Go Module方式后可以获得以下收益:
1. 可用性(标签tag可以从VCS中删除)
2. 不变性(可以在VCS中进行更改)
3. 快速:(没有git,没有计算元数据,调用更少,性能好)
4. 本地统一存储缓存($GOPATH/pkg /mod/cache)
Go模块转换实践
我们以ETCD项目为例进行转换,这个转换过程已通过测试用例的验证,可以到该项目中的Pull Request中查看
步骤一:准备go.mod文件
对于以前从未使用过模块的项目(没有go.mod文件),或者任何现在不推荐的依赖项管理解决方案,这个过程都非常简单。您只需要在项目的根目录中运行go mod tidy。这将生成一个新的、已填充好该项目依赖描述的go.mod文件。
但是,如果项目使用了那些较老的解决方案之一,比如dep、glide、govendor或godep,那么您将需要运行go mod init来生成填充的go.mod文件。该命令支持旧格式中依赖项描述。
etcd项目确实有一个go.mod文件,尽管它从未在项目的构建系统中启用。问题是模块名称没有正确的版本标识符,因为当前版本标记是v2+。由于语义化导入版本控制的影响,需要更改为v3。
其包括执行以下过程:
更新etcd的go.mod文件以修正模块名称,使其包含v3后缀。
但是,如果项目使用了那些较老的解决方案之一,比如dep、glide、govendor或godep,那么您将需要运行go mod init来生成填充的go.mod文件。该命令支持旧格式中依赖项描述。
etcd项目确实有一个go.mod文件,尽管它从未在项目的构建系统中启用。问题是模块名称没有正确的版本标识符,因为当前版本标记是v2+。由于语义化导入版本控制的影响,需要更改为v3。
其包括执行以下过程:
1. 更新etcd的go.mod文件以修正模块名称,使其包含v3后缀。
2. 更新所有代码中的Import以包含版本号。我们编写了一个脚本,以便更容易地修改所有引用。完成后,此更改如下:
步骤二 : 启用Go模块
要使go客户端能够使用go module,需要设置GO111MODULE=on
正如我们所指出的,etcd项目已经设置了go.mod文件,有人可能认为这已经完成了。但它没有,而该环境变量这种缺失证实了该项目还没有使用go module。
注意:从Go 1.13开始,这一步将不再需要,因为Go Module将在默认情况下启用
步骤三: 更新测试中的导入
在上面的过程中,我们对组成etcd主模块的go.mod文件进行了更新,以使用v3版本标记。现在主模块被标记为v3,我们还需要更新etcd项目的测试用例中的Import引用v3,以确保它们导入了主模块的正确版本。
步骤四: 其他更新
在这些更改之后,您可能希望保持良好的状态—毕竟,应用程序模块现在已经全部转换为使用go module,并使用正确的版本标记。
不过没那么快。一旦你开始运行测试,你会发现两个额外的场景需要处理:
1. etcd使用了诸如golint、gosimple、staticcheck、ineffassign等静态分析工具,但其中一些工具没有模块意识,无法识别模块路径,而无法通过必要的检查。在etcd的这种场景下,etcd-io/etcd下并没有v3文件夹,但是Import导入(或模块路径)包含v3,如etcd-io/etcd/v3。其他工具是模块感知的,但必须在新版本的Go 1.12中可用。如果构建系统在1.11之上,那么它们也需要迁移到1.12。
2. 如果使用了protobuf之类的代码生成器。更新.proto文件,以便使用正确版本的导入生成代码。
步骤五: 加入GoCenter
在构建过程中,您可能会注意到许多go get命令在etcd的不同阶段执行。
为了加快GoLang应用程序的构建时间,并确保etcd ppipeline中使用的Go Module版本的不可变性和可用性,使用GoCenter来构建etcd
只需设置GOPROXY=https://gocenter.io。(详细原理可看上文的Go Proxy 原理图)
总结
正如您所看到的,将Go项目转换为使用Go
Module方式非常简单,但是有一些细节可能会减慢您的速度。通过选择这个具有丰富场景的项目来演示这个过程,我们相信我们达到了大多数需要处理的场景,为您提供了一个很好的示例,覆盖了您可能面临的情况。
大家感兴趣可以测试一下旧的依赖管理方式和Go Module方式的性能对比,请参考:
https://jfrog.com/blog/build-times-matter-speed-is-everything/