1. Golang包管理工具简介
大一点的项目,通常会依赖很多第三方包。Golang把所有的第三方包都放在GOPATH/src目录下,且每个包仅保留一个版本。如果两个项目依赖不同版本的第三方包,就会产生问题。
为了解决这个问题,go在1.5版本引入了vendor属性(默认关闭),并在1.6版本中默认开启了vendor属性。简单来说,vendor属性就是让go编译时,优先从项目源码树根目录下的vendor目录查找代码(可以理解为切了一次GOPATH),如果vendor中有,则不再去GOPATH中去查找。
但是vendor目录又带来了新的问题:
(1)vendor目录中依赖包没有版本信息。这样依赖包脱离了版本管理,对于升级、问题追溯,会有点困难。
(2)如何方便的得到本项目依赖了哪些包,并方便的将其拷贝到vendor目录下?
为了解决这些问题,开发者在vendor基础上开发了多个管理工具,比较常用的有godep、govendor、glide,以及官方的dep和gomod。
本文中,我们来了解一下godep、govendor和gomod,并重点学习gomod。
参考文档:
2. Golang包管理工具对比
2.1. godep
godep的使用者众多,如docker、kubernetes、coreos等go项目很多都是使用godep来管理其依赖,当然原因可能是早期也没的工具可选。
godep早期版本并不依赖vendor,所以对go的版本要求很松,go 1.5之前的版本也可以用,只是行为上有所不同。在vendor推出以后,godep也改为使用vendor了。
godep使用很简单,当项目编写完成,使用GOPATH的依赖包测试没问题的时候,执行 godep save
,该命令会做两件事:
(1)扫描项目的代码,将项目依赖的包及该包的版本号(即git commit)记录到Godeps/Godeps.json文件中。
(2)将依赖的包从GOPATH/src中拷贝到vendor目录(忽略依赖包的.git目录)。对于不支持vendor的早期版本,则会拷贝到Godeps/_workspace/里。
其他常用命令:
1 | # 安装godep |
2.2. govendor
govendor是在vendor之后出现的,功能相对godep多一点,不过就核心问题的解决来说基本是一样的。govendor生成vendor目录的时候需要2条命令:govendor init
生成vendor/vendor.json,此时文件中只有本项目的信息。govendor add +external
更新vendor/vendor.json,并拷贝GOPATH下的第三方包到vendor目录中。
其他常用命令:
1 | # 安装govendor |
2.3. gomod
go modules随着golang1.11的发布和我们见面了,这是官方提倡的新的包管理,乃至项目管理机制,可以不再需要GOPATH的存在。
现在modules机制仍在早期阶段,所以golang提供了一个环境变量“GO111MODULE”,默认值为auto,如果当前目录里有go.mod文件,就使用go modules,否则使用旧的GOPATH和vendor机制,因为在modules机制下go get只会下载go modules,这一行为会在以后版本中成为默认值,这里我们保持auto即可,如果你想直接使用modules而不需要从GOPATH过度,那么把“GO111MODULE”设置为on。
modules和传统的GOPATH不同,不需要包含例如src,bin这样的子目录,一个源代码目录甚至是空目录都可以作为module,只要其中包含有go.mod文件。
1、启用modules机制
linux and mac 中:
1 | export GO111MODULE=on |
windows powershell 中:
1 | env:GO111MODULE = "on" |
2、初始化test项目为module
1 | go mod init test |
初始化之后,目录下会自动生成go.mod文件。当我们使用go build,go test以及go list时,go会自动得更新go.mod文件,将依赖关系写入其中。
3、整理依赖
1 | go mod tidy -v |
- 这条命令会自动下载依赖的module到cache,清除无用的module。
- 生成go.mod的require部分,和go.sum文件。go.sum是一个构建状态跟踪文件,它会记录当前module所有的顶层和间接依赖,go modules根据这些记录去寻找对应的依赖。
- 依赖的module下载到GOPATH/pkg/cache/和GOPATH/pkg/mod/目录下。
4、Goland启用gomod
File,Settings,Go,Go Modules,勾选Enable Go Modules(vgo) integration,Apply。
其他常用命令:
1 | # 帮助 |
2.4. 小结
综上,推荐使用gomod,这是官方提倡的新的包管理工具。
3. 使用gomod进行包管理
3.1. 安装client-go
假设一个项目依赖client-go,那么参考官方说明进行安装。
1、查看k8s版本
1 | kubectl version |
Client Version表示kubectl的版本,Server Version表示apiserver的版本(假设版本为v1.15.0)。
2、查找对应版本的client-go
在kubernetes/client-go上查找k8s v1.15.0对应的client-go版本,找到版本为12.0
3、安装对应版本的client-go
1 | go get k8s.io/client-go/12.0/kubernetes |
这里有个坑,国内无法访问k8s.io,需要科学上网(恰好我的搬瓦工服务器也访问不到k8s.io)。因此,这里使用GOPROXY的方式来解决,参考一键解决 go get golang.org/x 包失败。
1 | # export GOPROXY=https://goproxy.cn |
失败了,返回404,猜测是12.0版本在代理服务器不存在,使用另外一种下载方式。
1 | go get k8s.io/client-go@kubernetes-1.15.0 |
此处下载了两个版本的client-go,我们使用v0.0.0版本。
4、安装client-go相关依赖
client-go默认下载到$GOPATH/pkg/mod/k8s.io
目录下,把k8s.io目录移动到$GOPATH/src
目录下。然后把client-go@v0.0.0重命名为client-go,然后在client-go目录下执行安装依赖的命令:
1 | export GOPROXY=https://goproxy.io |
完成之后,所有的依赖都下载到了client-go/vendor目录下,一个完整的client-go依赖库就准备好了。
3.2. 依赖库版本管理
client-go依赖库准备好了,但是另一个问题来了:假设项目vk-project依赖client-go和库A,client-go依赖库A,两个依赖库A版本不一致,那么vk-project在编译时,会同时依赖两个不同的库A,导致编译出的二进制包有问题。
此时,一个简单的办法是把GOPATH/src/k8s.io/client-go/vendor目录剪切覆盖vk-project/vendor目录,然后把GOPATH/src/k8s.io/client-go目录移动到vk-project/vendor/k8s.io/目录下。这么做的话,client-go就和自己的依赖是平级的存在了。
但是,这么做有另一个问题:依赖库版本没有记录。因为没有版本记录,那么在上传vk-project项目时,就要把依赖全部上传,否则别人下载了vk-project也没办法正确安装依赖。
这里,使用gomod进行依赖库版本管理。
1 | go mod init vk-project |
上传代码时忽略vendor目录,在gitignore中添加:
1 | /vendor/ |
3.3. 安装依赖
1、下载项目后,进入项目目录
1 | cd vk-project |
2、下载依赖
1 | go mod download |
3、拷贝依赖到vendor(可选)
1 | go mod vendor |