Go工程化实践之包管理

包管理

Go Package 管理发展史

Go 依赖管理是通过 Git 仓库模式实现,并随着版本的更迭已经逐渐完善。

  • GOPATH 模式

    GOPATH 目录是所有工程的公共依赖包目录,所有需要编译的 go 工程的依赖包都放在 GOPATH 目录下。

  • Vendor 特性

    为了解决 GOPATH 模式下,多个工程需要共享 GOPATH 目录,无法适用于各个工程对于不同版本的依赖包的使用,不便于更新某个依赖包。go 1.6 之后开启了 vendor 目录

  • Go Module 包管理

    GO 1.11 以后开始支持 Module 依赖管理工具,从而实现了依赖包的升级更新,在 GO 1.13 版本后默认打开

GOPATH 环境变量

GOPATH 为 Go 开发环境时所设置的一个环境变量。

历史版本的 go 语言开发时,需要将代码放在 GOPATH 目录的 src 文件夹下。go get 命令获取依赖,也会自动下载到 GOPATHsrc 下。

1
go get github.com/foo/bar

会将代码下载到 $GOPATH/github.com/foo/bar

GOPATH 具体结构如下,必须包含三个文件夹,具体如下图所示:

1
2
3
4
5
6
GOPATH
├── bin // 编译生成的二进制文件
├── pkg // 预编译文件,以加快程序的后续编译速度
└── src // 所有源代码
├── gitee.com
├── github.com

Using Go Modules

Go Modules 是从 Go 1.11 开始初步支持,解决了依赖版本的信息管理,并且保证安全性。

主要通过 go.modgo.sum 组成,主要包括依赖模块路径定义,以及通过 checksum 方式进行保证包的安全性。并且可以在 GOPATH 外进行创建和编译项目。

通过 go mod init xxx 初始化一个项目

image-20231017160148904

使用时

1
2
3
4
5
6
7
package main

import log "github.com/sirupsen/logrus"

func main() {
log.Info("hello")
}

通过 go get github.com/sirupsen/logrus ,可以下载或者更新依赖包,也可以指定版本 [email protected]

image-20231017160804276

go.mod 文件中可以使用到的语法关键词以及含意:

  • module:定义当前项目的模块路径
  • go:标识当前模块的 GO 语言版本,目前来看还只是个标识作用
  • require:说明 Module 需要什么版本的依赖
  • exclude:用于从使用中排除一个特定的模块版本
  • replace:替换 require 中声明的依赖,使用另外的依赖及其版本号

image-20231017161055173

Go Modules Checksum

为了解决 Go Modules 的包被篡改安全隐患,Go 开发团队在引入 go.mod 的同时也引入了 go.sum 文件,用于记录每个依赖包的哈希值,在构建时,如果本地的依赖包 hash 值与 go.sum 文件中记录的不一致,则会拒绝构建。

  • go.sum 文件中每行记录由 module 名、版本和哈希组成,并由空格分开;
  • 在引入新的依赖时,通常会使用 go get 命令获取该依赖,将会下载该依赖包下载到本地缓存目录 $GOPATH/pkg/mod/cache/download,该依赖包为一个后缀为 .zip 的压缩包,并且把哈希运算同步到 go.sum 文件中;
  • 在构建应用时,会从本地缓存中查找所有 go.mod 中记录的依赖包,并计算本地依赖包的哈希值,然后与 go.sum 中的记录进行对比,当校验失败,go 命令将拒绝构建。

Go Modules Proxy

Go 1.13 将 GOPROXY 默认成了中国大陆无法访问的 https://proxy.golang.org,所以在国内需要配置代理进行使用。

GOPROXY 可以解决一些公司内部的使用问题:

  • 访问公司内网的 git server
  • 防止公网仓库变更或者消失,导致线上编译失败或者紧急回退失败
  • 公司审计和安全需要
  • 防止公司内部开发人员配置不当造成 import path 泄露
  • cache 热点依赖,降低公司公网出口带宽

image-20231017162110185

Go Modules Private

GOPRIVATE 用来控制 go 命令把哪些仓库看作是私有的仓库,这样可以跳过 proxy server 和校验检查,这个变量的值支持用逗号分隔,可以填写多个值,例如:

1
export GOPRIVATE=*.corp.example.com,github.com/org_name

当配置 GOPROVATE,它的值同时也将作为 GONOPROXY 和 GONOSUMDB 的默认值,所以当配置 GOPRIVATE 后将会跳过代理,以及 checksum 校验检查。

通常推荐配置 GOPROXY 和 GOPRIVATE 进行使用,GOPRIVATE 也可以识别 Git SSH KEY 进行权限校验。

GOPROXY 编译部署

goproxy.io 是一个 Go Modules 开源代理,也可以作为公司内部代理。

https://github.com/goproxyio/goproxy

1
2
3
4
5
6
7
# 下载以及编译
git clone https://github.com/goproxyio/goproxy.git
cd goproxy
go build

# 运行 goproxy 代理
./goproxy -listen=0.0.0.0:8081 -cacheDir=/tmp/cache -proxy https://goproxy.io -exclude "github.com/private"

GOPROXY 配置信息

goproxy 运行配置信息:

1
2
3
4
5
6
7
8
9
10
11
12
./goproxy --help
Usage of ./goproxy:
-cacheDir string
Go Modules cache dir, default is $GOPATH/pkg/mod/cache/download 【指定 Go 模块的缓存目录】
-cacheExpire duration
Go Modules cache expiration (min), default is 5 min (default 5m0s) 【缓存过期时间】
-exclude string
exclude host pattern, you can exclude internal Git services 【proxy 模式下指定哪些 path 不经过上游服务器】
-listen string
service listen address (default "0.0.0.0:8081") 【服务监听端口,默认8081】
-proxy string
next hop proxy for Go Modules, recommend use https://goproxy.io 【指定上游 proxy server,推荐 goproxy.io】

GOPROXY 访问内网仓库

goproxy 配置访问公司内网的 git server

  • 用户本地配置 GONOSUMDB=github.com/private
  • goproxy server 配置 exclude 进行排除掉所代理仓库
  • goproxy server 配置 SSH Key ,并且在仓库添加只读权限
  • goproxy server 配置 .gitconfig 把 ssh 替换成 http 方式访问

image-20231017163331251

推荐 proxy

https://goproxy.io

https://github.com/gomods/athens

https://goproxy.cn

参考

Go Modules Reference

Go Modules in 2019

Using Go Modules

Migrating to Go Modules

Module Mirror and Checksum Database Launched

Publishing Go Modules

Go Modules: v2 and Beyond

Keeping Your Modules Compatible