go modules 是 golang 1.11 新加的特性。如今 1.13 都已经发布了第 7 个小版本了,几乎所有大项目均已开始使用,这自然也包括 Kubernetes 生态中的众多项目。笔者在开发 OAM 相关项目的时候,却发现 modules 的各项功能看似简单,却并没有那么好用,于是便想给大家分享一下使用心得,希望大家也能在最短时间内学会 modules 的使用,避免踩坑。
简单说就是包管理,Golang 的包管理素来以混乱著称,以前是依赖 $GOPATH,只要你的代码放在指定路径下就好了,完全没有“包管理”的概念。被社区吐槽了很久以后开始搞 vendor 机制,简单来说就是代码不光是可以放到指定路径,还可以放在项目自身路径的 vendor 文件夹。
这个解决的问题是:你引用的代码包上游变更不会直接影响你的项目,这显然是开始关心“包版本”了。遗憾的是依旧没有解决包管理的问题,比如不同的包依赖了同一个包的不同版本怎么办?版本间代码冲突怎么办?vendor 机制并没有解决,于是围绕 vendor/ 社区就出了几十个包管理工具,一时间百花齐放、百家争鸣、各有所长,导致 golang 的包管理生态变得有些混乱。
更有意思的是,在 go 官方社区看到包管理工具的乱象后,也做了个功能类似的工具 dep,原理与其他各类依靠 vendor/ 机制的包管理工具类似,准备对包管理做统一。
当大家对 dep 工具报以期望并纷纷开始切换到 dep 工具管理依赖包的时候,go 官方又发布了现在的 modules 机制,完全放弃了之前的 dep 工具与 vendor 机制。这样的操作在社区引起了巨大的争议,modules 与 go get、go 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 get、go build、go run 都会自动查找并在 go.mod 里面更新依赖。
所以按照 Go 官方团队的意思,一般情况下,你根本不用关心包管理的问题了。( 当然,这真的纯粹只是官方在 YY )
所以到这里你就已经学会 go modules 了,没超过 5 分钟吧?
实际因为 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 都找不到的时候,直接访问,代理访问不到的私有仓库就可以正常使用了。
这个功能基本上是在家远程办公的必备工具了。
这个几乎是最常见的问题,比较简单的解决方案是 hack 一下 git 配置:
git config --global url."[email protected]:/.git" .insteadOf "https://gitlab.your-company.com//.git"
这个方案依赖你本地的 ~/.ssh/id_rsa, 这样你就可以正常 go get 了。
先查看版本:
$ 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 里还有一些其他功能,也在此列举,方便大家查阅:
OAM(Open Application Model)开放应用模型是阿里联合微软针对云原生应用的模型,第一次对“以应用为中心”的基础设施和构建规范进行了完整的阐述。应用管理者只要遵守这个规范,就可以编写出一个自包含、自描述的“应用定义文件”。
OAM 将应用划分为应用组件和应用特征两部分,应用组件是应用本身的逻辑,而应用特征则是云上的各种通用能力(如扩缩容、监控、灰度等等),大大提升了应用构建时模块化复用能力,将云上的各类资源和能力都转化为了标准化的可“声明”对象。
同时 OAM 强调关注点分离,通过标准化的模型将应用开发不同阶段的 API 进行分层,流程上先由研发定义应用组件,再由运维配置云上的各种策略,最后由基础设施团队统一提供各类模块化的能力。OAM 则在其中起着彼此协作的粘合剂作用,大大提高了应用交付的效率。
OAM 相关内容在github 上完全开源,同时我们也为 Go 生态编写了oam-go-sd 方便快速实现 OAM。