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,更多内容参考go依赖包管理工具对比golang包管理解决之道——go modules初探再探go modules:使用与细节

理论篇

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 安装
go get -u -v github.com/tools/godep

# 安装新的包
go get github.com/voidking/k8s-client-go
import "github.com/voidking/k8s-client-go"
godep save

# 更新依赖包
go get -u github.com/voidking/k8s-client-go
godep update github.com/voidking/k8s-client-go
# or
godep update github.com/voidking/...

# 下载godeps.json里的包到GOPATH/src
godep restore

govendor

govendor是在vendor之后出来的,功能相对godep多一点,不过就核心问题的解决来说基本是一样的。govendor生成vendor目录的时候需要2条命令:
govendor init生成vendor/vendor.json,此时文件中只有本项目的信息。
govendor add +external更新vendor/vendor.json,并拷贝GOPATH下的第三方包到vendor目录中。

其他命令:

1
2
3
4
5
6
7
8
9
10
11
# 安装
go get -u github.com/kardianos/govendor

# 获取指定版本的包
govendor fetch golang.org/x/net/context@v1

# 查看依赖的包
govendor list

# 查看哪些包使用了fmt
govendor list -v fmt

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机制

1
2
3
4
5
# linux and mac
export GO111MODULE=on

# windows powershell
$env:GO111MODULE = "on"

2、初始化test项目为module
go mod init test
初始化之后,目录下会自动生成go.mod文件。当我们使用go build,go test以及go list时,go会自动得更新go.mod文件,将依赖关系写入其中。

3、整理依赖
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
3
4
5
6
7
8
9
10
11
# 帮助
go help mod

# 已有go.mod,下载依赖到cache
go mod download

# 拷贝依赖到vendor(没有则先下载)
go mod vendor

# 清空cache
go clean -modcache

gomod实践篇

安装client-go

假设一个项目依赖client-go,那么参考官方说明进行安装。

1、查看k8s版本
kubectl version
假设看到 GitVersion:"v1.15.0",那么说明k8s版本为v1.15.0。

2、查找对应版本的client-go
kubernetes/client-go上查找k8s v1.15.0对应的client-go版本,找到版本为12.0

3、安装对应版本的client-go
go get k8s.io/client-go/12.0/kubernetes
这里有个坑,国内无法访问k8s.io,需要科学上网(恰好我的搬瓦工服务器也访问不到k8s.io)。因此,这里使用GOPROXY的方式来解决,参考一键解决 go get golang.org/x 包失败

1
2
3
4
# export GOPROXY=https://goproxy.cn
export GOPROXY=https://goproxy.io
export GO111MODULE=on
go get k8s.io/client-go/12.0/kubernetes

失败了,返回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
2
3
export GOPROXY=https://goproxy.io
export GO111MODULE=on
go mod vendor

完成之后,所有的依赖都下载到了client-go/vendor目录下,一个完整的client-go依赖库就准备好了。

依赖库版本管理

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
2
3
go mod init vk-project
go mod tidy -v
go mod vendor

上传代码时忽略vendor目录,在gitignore中添加:

1
/vendor/

安装依赖

1、下载项目后,进入项目目录
cd vk-project

2、下载依赖
go mod download

3、拷贝依赖到vendor(可选)
go mod vendor

0%