导读: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."[email protected]:<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 机制来管理依赖。