Go

五分钟学会使用 go modules(含在家办公使用技巧)


作者 | 孙健波(天元)  阿里巴巴技术专家

导读go modules 是 golang 1.11 新加的特性。如今 1.13 都已经发布了第 7 个小版本了,几乎所有大项目均已开始使用,这自然也包括 Kubernetes 生态中的众多项目。笔者在开发 OAM 相关项目的时候,却发现 modules 的各项功能看似简单,却并没有那么好用,于是便想给大家分享一下使用心得,希望大家也能在最短时间内学会 modules 的使用,避免踩坑。

modules 是什么?

简单说就是包管理,Golang 的包管理素来以混乱著称,以前是依赖 $GOPATH,只要你的代码放在指定路径下就好了,完全没有“包管理”的概念。被社区吐槽了很久以后开始搞 vendor 机制,简单来说就是代码不光是可以放到指定路径,还可以放在项目自身路径的 vendor 文件夹。这个解决的问题是:你引用的代码包上游变更不会直接影响你的项目,这显然是开始关心“包版本”了。遗憾的是依旧没有解决包管理的问题,比如不同的包依赖了同一个包的不同版本怎么办?版本间代码冲突怎么办?vendor 机制并没有解决,于是围绕 vendor/ 社区就出了几十个包管理工具,一时间百花齐放、百家争鸣、各有所长,导致 golang 的包管理生态变得有些混乱。对这段历史感兴趣的可以阅读下笔者曾经写的文章《Go 包管理的前世今生》

更有意思的是,在 go 官方社区看到包管理工具的乱象后,也做了个功能类似的工具 dep,原理与其他各类依靠vendor/ 机制的包管理工具类似,准备对包管理做统一。当大家对 dep 工具报以期望并纷纷开始切换到 dep 工具管理依赖包的时候,go 官方又发布了现在的 modules 机制,完全放弃了之前的 dep 工具与 vendor 机制。这样的操作在社区引起了巨大的争议,modules 与 go getgo build 等官方工具生态有很好的集成,官方的意图自然是希望抛开原有的历史包袱,通过全新的方式拯救世界。然而实际体验下来,却依旧不尽如人意。

总的来说大趋势已经是用 modules,go1.13 也对 modules 机制做了不少工作。

言归正传,本文的目标是希望能用 5~10 分钟时间带您学会使用 go modules,然后通过 QA 的形式,描述一些常见的问题。如果希望详细理解相关内容,也可以参考官方文档

初始化

modules 机制在 go 里面的子命令是 go mod

modules 机制是 go 1.11 才引入的,所以开始用之前先检查下自己的 go 版本 go version,建议使用最新的 1.13 版本,涵盖了 module 机制相关的较多更新和功能。

在保证 go 版本至少在 1.11 或以上之后,就要开启一下环境变量 GO111MODULE,启用 module 机制。

export GO111MODULE=on

或者设置为 auto 模式,这样在 GOPATH 路径下的项目可以不使用 module 机制。

export GO111MODULE=auto

建议加到 ~/.bashrc~/.zshrc  等配置中启动便自动生效。

如果您的项目之前已经用 modules 管理了,那么到此为止你本地的环境已经完成初始化了。

如果项目里之前没有使用 modules,切换过来也很简单,删除原先的 vendor 文件夹(保险起见可以移动到项目之外的地方),在项目里执行一下初始化命令即可。

go mod init [module名称]

包名跟以前一样,还是跟 go path 强关联的,比如我们的项目一般是在 http://github.com/oam-dev/oam-go-sdk,那么你的包名就是这个了。

go mod init github.com/oam-dev/oam-go-sdk

初始化完成后就会看见项目里有个 go.mod 文件。

然后通过  go mod download 就可以下载所有原先 vendor 中的依赖。

日常包管理

使用了 go module 以后,你的许多命令就会与包管理集成,比如 go getgo buildgo run 都会自动查找并在go.mod 里面更新依赖。

所以按照 Go 官方团队的意思,一般情况下,你根本不用关心包管理的问题了。( 当然,这真的纯粹只是官方在 YY )

所以到这里你就已经学会 go modules 了,没超过5分钟吧?

FAQ

实际因为 go 官方对包管理重视的太晚,各种包都没有版本的概念,随随便便就会出现各种冲突,而且由于 go modules 实在是太自动了,所以就算你学会了怎么用 modules,最后还是会比较头疼。

下面我们就以 FAQ 的形式回答项目中遇到各种问题怎么办。

有些包由于特定网络原因无法访问怎么办?

Go module 加入了代理的机制,只要设置一个代理地址,就可以提供代理访问。阿里云就提供了这样一个 go 的代理 ,是完全免费的服务。公共代理仓库会代理并缓存go模块,你可以利用该代理来避免 DNS 污染或其他问题导致的模块拉取缓慢或失败的问题,加速你的项目构建。

设置的方式非常简单,只需要设置如下环境变量即可,执行命令:

export GOPROXY=https://mirrors.aliyun.com/goproxy/

go1.13 加入了 mirror 机制,可以通过 go env -w 设置 mirror,其实就是之前的 GOPROXY 做的更到位一些,执行命令:

go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct

逗号后面可以增加多个 proxy,最后的 direct 则是在所有 proxy 都找不到的时候,直接访问,代理访问不到的私有仓库就可以正常使用了。

这个功能基本上是在家远程办公的必备工具了。

公司通过 gitlab 搭建了私有库,二方依赖库下载不下来怎么办?

这个几乎是最常见的问题,比较简单的解决方案是 hack 一下 git 配置:

git config --global url."git@gitlab.your-company.com:<your>/<package>.git".insteadOf "https://gitlab.your-company.com/<your>/<package>.git"

这个方案依赖你本地的 ~/.ssh/id_rsa, 这样你就可以正常 go get 了。

Dockerfile 中构建镜像怎么解决私有库的依赖包问题?

  • 方案一:上述方式通过修改 git config ,却依赖你本地的  ~/.ssh/id_rsa,在构建时可以通过 multistage-build 把私钥 add 到 stage 0 里面 build,然后用后面新的 stage 生成镜像,这样构建的镜像就不会包含私钥;
  • 方案二: 更为安全的方式是,在每次构建 Docker 镜像之前,先在本地用 go mod vendor 把包缓存下来,在 Dockerfile 构建镜像过程中还是用 GOPATH 和 Vendor 机制来管理依赖。


依赖包怎么更新指定版本?

先查看版本:

$ go list -m -versions rsc.io/sampler
rsc.io/sampler v1.0.0 v1.2.0 v1.2.1 v1.3.0 v1.3.1 v1.99.99


再更新:

$ go get rsc.io/sampler@v1.3.1
go: finding rsc.io/sampler v1.3.1
go: downloading rsc.io/sampler v1.3.1
go: extracting rsc.io/sampler v1.3.1
$ go test
PASS
ok example.com/hello 0.022s


某些依赖包的地址变更导致无法找到了怎么办?

go 的依赖与项目名直接相关,这就导致如果我们使用了 github 上的项目,然后项目的维护人员突发奇想改个项目名称,就会导致所有依赖它的项目都无法找到依赖。

还好有 replace 的机制:

replace golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a

版本冲突怎么办?

这就要梳理版本了,是最没有捷径的。一个比较简单的办法是把所有 go.mod 里不需要指定版本的包全部删掉,仅指定必要的包版本,然后通过 go build 让项目自动构建依赖包的版本。

通过 go mod graph 可以查看具体依赖路径:

$ go mod graph
github.com/oam-dev/oam-go-sdk github.com/go-logr/logr@v0.1.0
github.com/oam-dev/oam-go-sdk github.com/onsi/ginkgo@v1.10.1
github.com/oam-dev/oam-go-sdk github.com/onsi/gomega@v1.7.0
github.com/oam-dev/oam-go-sdk github.com/stretchr/testify@v1.4.0
github.com/oam-dev/oam-go-sdk golang.org/x/net@v0.0.0-20191004110552-13f9640d40b9
github.com/oam-dev/oam-go-sdk k8s.io/api@v0.17.0
github.com/oam-dev/oam-go-sdk k8s.io/apimachinery@v0.17.0
github.com/oam-dev/oam-go-sdk k8s.io/client-go@v0.17.0
github.com/oam-dev/oam-go-sdk sigs.k8s.io/controller-runtime@v0.4.0
...


左边是项目包,右边是被依赖的包和版本。

如果确实存在两个需要指定版本的包互相冲突,那就要做取舍,修改代码,升级或降级某个包了。

本地包如何引用?

如果在代码调试过程中,涉及到修改其他依赖项目代码,这时候就要引用本地包,也可以采用 replace 机制:

require (
golang.org/x/crypto v0.0.0
)
replace golang.org/x/crypto v0.0.0 => ../crypto


后面这个就是个相对项目路径的本地依赖所在路径。

解决了上面的这些问题,基本上你就可以愉快的使用 module 功能啦。

go mod 命令一览

go mod 里还有一些其他功能,也在此列举,方便大家查阅:

1.png


最后

OAM(Open Application Model)开放应用模型是阿里联合微软针对云原生应用的模型,第一次对“以应用为中心”的基础设施和构建规范进行了完整的阐述。应用管理者只要遵守这个规范,就可以编写出一个自包含、自描述的“应用定义文件”。

OAM 将应用划分为应用组件和应用特征两部分,应用组件是应用本身的逻辑,而应用特征则是云上的各种通用能力(如扩缩容、监控、灰度等等),大大提升了应用构建时模块化复用能力,将云上的各类资源和能力都转化为了标准化的可“声明”对象。

同时 OAM 强调关注点分离,通过标准化的模型将应用开发不同阶段的 API 进行分层,流程上先由研发定义应用组件,再由运维配置云上的各种策略,最后由基础设施团队统一提供各类模块化的能力。OAM 则在其中起着彼此协作的粘合剂作用,大大提高了应用交付的效率。

OAM 相关内容在 github 上完全开源,同时我们也为 Go 生态编写了 oam-go-sdk 方便快速实现 OAM。

目前,阿里巴巴团队正在上游贡献和维护这套技术,如果大家有什么问题或者反馈,也非常欢迎与我们在上游或者钉钉联系。

阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

0 个评论

要回复文章请先登录注册