# GO111MODEDULE变量以及Go Module的使用建议

> 作者/来源: UCloud 运营管理员
> 发布时间: 2022-10-08T06:07:00.000Z
> 分类: 其他
> 标签: Kubernetes, 存储, Docker
> 原文链接: http://117.50.162.249:3000/yun/articles/2409

---

# GO111MODEDULE变量以及Go Module的使用建议

> 来源: https://www.ucloud.cn/yun/127930.html
> 作者: 社区管理员
> 发布日期: 发布于2022-10-08 14:07

我们可能在很多地方如 README 文件、Makefile 文件以及 Dockerfile 文件中看到`GO111MODULE=on`， 对于刚接触的`Golang`的开发者可能对此有很多疑惑。

这片文章，我将详细介绍`GO111MODULE`变量的意义，以及什么时候需要使用到该变量， 同时也总结了一些在使用 Go Modules 时需要注意的细节，帮助你在下次遇到这个变量时不再疑惑。

```
GO111MODULE=on go get -u golang.org/x/tools/gopls@latest
```

## 从 GOPATH 到 GO111MODULE

首先，对于 GOPATH，无论是资深开发者和新开发者都不会陌生。GO 在 2009 年发布时，并没有提供包管理器（Package manager）， `go get`会根据导入路径拉取所有的源代码，存储在`$GOPATH/src`目录中。这种方式使用`master`分支作为包的稳定版本。 可想而知，这种方式缺乏灵活性同时很容易造成版本管理混乱的状态。

`Go Modules`(旧版本被成为`vgo-versiond Go`)在 Go 1.11 被正式引入，与 GOPATH 方式存储`a signle git checkout of every package`的方式不同， Go Module 通过`go.mod`文件管理包的版本信息，并且能够指定版本号。

两种包管理方式`GOPATH`和`Go Module`同时存在，成为 Go 语言使用最大的坑，后续官方试图通过一个变量：`GO111MODULE`去解决这个问题， 不过随之而来的是更大的灾难。

## GO111MODULE 环境变量

环境变量`GO111MODUEL`的作用是控制 Go 包导入的方式，可以设置为`on，off，auto`三个值，由于该变量的语意会根据 Go 的版本变化， 这在很多地方造成严重的歧义。

### Go1.11 和 Go.12 版本 GO111MODULE

- `GO111MODULE=on`无论项目是否在 GOPATH 目录，都会强制使用 Go Modules。
- `GO111MODULE=off`无论项目是否在 GOPATH 目录，都会强制使用 GOPATH。
- `GO111MODULE=auto`是默认模式，当项目不在 GOPATH 中会启用 Go Modules；当项目在 GOPATH 目录会强制使用 GOPATH。

当你的项目在 GOPATH 目录中，但是你又想使用 Go modules 下载一个指定版本的包或打包时，只能通过 GO111MODULE：

```
GO111MODULE=on go get github.com/golang/mock/tree/master/mockgen@v1.3.1
```

### Go1.13 版本 GO111MODULE

在 Go 1.13 版本，`GO111MODULE`默认`auto`的用法发生了该改变：

- 如果项目包含`go.mod` 或者项目不再 GOPATH 目录中（无论是否包含`go.mdo`文件）都会使用 Go module。所以使用 Go 1.13， 你可以将所有项目保存到 GOPATH 中。
- 只有在项目保存在`GOPATH`中同时又不包含文件`go.mod`文件时，才会使用`GOPATH`。

### 为什么很多地方使用 GO111MODULE

`Go Module`可以通过`git tag`控制项目依赖包的版本，而`GOPATH`默认拉取`master`分支的最新提交。 环境变量`GO111MODULE`控制了是否启用 Go module。我们则可以`GO111MODULE=on go get xxxx/@1.33`的方式拉取指定版本的包。

```
GO111MODULE=on go get -u golang.org/x/tools/gopls@latest
GO111MODULE=on go get -u golang.org/x/tools/gopls@master
GO111MODULE=on go get -u golang.org/x/tools/gopls@v0.1
GO111MODULE=on go get golang.org/x/tools/gopls@v0.1.8
```

`latest`标签使用最新版本 git tag。`-u`选项强制更新，在指定版本是这个选项没有意义。

## 使用 Go Modules 的建议

其实在新版本`Golang`中，如果不接触旧的项目，可以不用关注这个变量意义。这里总结了使用 Go Modules 的一些建议

### 记住 go get 将会更新 go.mod 文件

`go get`主要用于下载依赖包和安装二进制文件，但是当项目中存在`go.mod`文件，使用`go get`会自动修改`go.mod`文件。 这听起来很奇怪，但这也是 Go modules 的一大亮点：自动维护依赖包版本。

在一些`CICD`流程中，我们需要注意这点。比如当使用`Jenkins`时，其会在 slave 节点`workspace`目录中上拉取代码，如果需要执行一些脚本命令时， 如测试，编译等，如果项目依赖有变化，则`go.mod`文件会产生修改，这就导致下次执行构建时，产生错误。

![image.png](https://ucloud-blog.cn-bj.ufileos.com/articles/127930/images/127930_000.png)

这里提供有两种方式解决，一种方法是添加一个`clean stage`，丢弃暂存区的修改记录；还有一种通过容器方式进行构建。 关于`Golang`项目`CICD`流程优化有机会会在后续多带带写一篇博客系统介绍，这里只提一下。

### Go Module 的依赖源来自哪里

当使用 Go Modules，`go build`会使用存储在`$GOPATH/pkg/mod`下的包，在使用 vim、VScode 等编辑器开发 Golang 项目， 可能默认会使用 GOPATH 中的包而不是使用 pkg/mod。

第二个问题是当我们在测试时需要替换一个包版本时，通常有以下几种做法：

Solution 1: 使用`go mod vendor + go build -mod=vendor`，这将会强制 go 使用 vendor/目录下的包，而不是`$GOPATH/pkg/mod` 中包

Solution 2: 在`go.mod`文件中使用`replace`。`../beers`是相关依赖的 copy。

```
use replace github.com/maelvls/beers => ../beers
```

### 私有 Go modules 和 Dockfile

在公司中，通常会使用很多私有仓，我们可以使用`GOPRIVATE`去设置让`Go`跳过包代理，直接从私有代码仓拉取包。 但是但我们使用 docker 构建镜像时，如何拉取私有仓呢，有以下方案

1. vendoring: 使用`go mod vendor`，这样就不要传递私有仓凭证给 docker build context。因为所有依赖都保存在 vendor 目录 中，构建时需要使用选项`-mod=vendor`。

- 优点：加速 docker 镜像构建，（go mod download 和 docker cache 配合使用也可以优化构建速度）
- ```
  ENV GOPROXY https://goproxy.ioWORKDIR /workspace## All these steps will be cachedCOPY go.mod go.sum ./## Get denpendencies will also be cached if wo don't change go.mod and go.sumRUN go mod download
  ```
- 缺点：代码管理复杂，需要跟踪依赖包的更新

1. no vendoring: 如果`vendor/`目录很大（kubernetes vendor 目录大约是 30MB），这种情况使用 vendoring 的方式会 很糟糕，这时候可以通过定义变量`GITHUB_TOKEN`传递给 dockerfile 文件。
2. ```
   ENV GOPROXY https://goproxy.ioWORKDIR /workspace## All these steps will be cachedCOPY go.mod go.sum ./## Get denpendencies will also be cached if wo don't change go.mod and go.sumRUN go mod download
   ```