作者 | 孙健波(天元) 阿里巴巴技术专家
导读: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 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分钟吧?
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 里还有一些其他功能,也在此列举,方便大家查阅:
最后
OAM(Open Application Model)开放应用模型是阿里联合微软针对云原生应用的模型,第一次对“以应用为中心”的基础设施和构建规范进行了完整的阐述。应用管理者只要遵守这个规范,就可以编写出一个自包含、自描述的“应用定义文件”。
OAM 将应用划分为应用组件和应用特征两部分,应用组件是应用本身的逻辑,而应用特征则是云上的各种通用能力(如扩缩容、监控、灰度等等),大大提升了应用构建时模块化复用能力,将云上的各类资源和能力都转化为了标准化的可“声明”对象。
同时 OAM 强调关注点分离,通过标准化的模型将应用开发不同阶段的 API 进行分层,流程上先由研发定义应用组件,再由运维配置云上的各种策略,最后由基础设施团队统一提供各类模块化的能力。OAM 则在其中起着彼此协作的粘合剂作用,大大提高了应用交付的效率。
OAM 相关内容在 github 上完全开源,同时我们也为 Go 生态编写了 oam-go-sdk 方便快速实现 OAM。
目前,阿里巴巴团队正在上游贡献和维护这套技术,如果大家有什么问题或者反馈,也非常欢迎与我们在上游或者钉钉联系。
参与方式:
- 钉钉扫码进入 OAM 项目中文讨论群
招聘
我们也在招聘,感兴趣的同学欢迎加入我们。在这里,既有 CNCF TOC 和 SIG 联席主席,也有 etcd 创始人、K8s Operator 创始人与 Kubernetes 核心维护成员组成的、国内最顶尖的 Kubernetes 技术团队。在这里,你将同来自全球的云原生技术领域专家们(比如 Helm 项目的创始人、Istio 项目的创始人)密切合作,在独一无二的场景与规模中从事 Kubernetes、Service Mesh、Serverless、Open Application Model ( OAM )等云计算生态核心技术的研发与落地工作,在业界标杆级的平台上既赋能阿里巴巴全球经济体,更服务全世界的开发者用户。
简历投递至 jianbo.sjb@alibaba-inc.com
“阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”