解决 Golang 升级到 1.18+ 版本后在容器中构建时出现 error obtaining VCS status: exit status 128 的问题

Intro

Golang 是个特别神奇的语言,他的语法不是很复杂,而且编译出来的程序是个 Binary,不需要安装什么额外的 runtime 就可以跑,如果要写一个 Hello World 之类的小程序只要跟着教程就可以快速写出来,如果要写点稍微复杂的 Web 应用,只要找个 Web 框架,比如 Gin,Fiber 之类的也可以有着和 ExpressJS 类似的体验,你看像我这种几乎不会写代码的人也可以在 BennyThink 大佬和 GitHub Copilot 以及一众 Stackoverflow 回答的参考和辅助下写出 WebP Server Go 这样的 Web 程序。

当然,Go 也有许多匪夷所思的点,比如他没有类似 pypi,npm 之类的中心化包管理平台,所有人写 Golang 的程序都是 import 了一堆 github.com 上面的东西,比如:

import (
	"fmt"
	"math"
	"regexp"
	"strconv"
	"unicode"

	"github.com/pingcap/errors"
	"github.com/pingcap/tidb/parser/ast"
	"github.com/pingcap/tidb/parser/auth"
	"github.com/pingcap/tidb/parser/charset"
	"github.com/pingcap/tidb/parser/mysql"
	"github.com/pingcap/tidb/parser/terror"
	"github.com/pingcap/tidb/parser/types"
)

等哪天 GitHub 无了还真不知道怎么去 import 这些包,此外还有 GOPATH 等问题导致看到很多人把自己的所有 Go 项目都放在 /home/User/go/ 下面进行开发,非常莫名奇妙。

WebP Server Go Upgrade

由于 Go 1.17 版本已经 EOL,我们对 WebP Server Go 的构建版本进行升级,由于 Golang 升级一般来说就是换一个镜像的事情,所以我们将构建的镜像从:

FROM golang:1.17.4-alpine as builder

换为了

FROM golang:1.19.0-alpine as builder

非常简单,然后很快,CI 就告诉我们镜像构建就不成功了(这种事件在某些公司内部就会有人大喊:「CI 挂了,@xxx 看看」):

https://github.com/webp-sh/webp_server_go/runs/7784058473?check_suite_focus=true

#14 [builder 6/6] RUN cd /build && sed -i "s|.\/pics|/opt/pics|g" config.json      && sed -i "s|""|"/opt/exhaust"|g" config.json      && sed -i 's/127.0.0.1/0.0.0.0/g' config.json      && go build -ldflags="-s -w" -o webp-server .
#14 0.395 error obtaining VCS status: exit status 128
#14 0.395 	Use -buildvcs=false to disable VCS stamping.
#14 ERROR: process "/bin/sh -c cd /build && sed -i \"s|.\\/pics|${IMG_PATH}|g\" config.json      && sed -i \"s|\\\"\\\"|\\\"${EXHAUST_PATH}\\\"|g\" config.json      && sed -i 's/127.0.0.1/0.0.0.0/g' config.json      && go build -ldflags=\"-s -w\" -o webp-server ." did not complete successfully: exit code: 1

用中文搜了一下,CSDN 告诉我两个选项:

所幸后者除了技术可能有点不太熟练以外解决方式还算靠谱,让我们来看看这个东西到底是什么吧.

Go 1.18 的 Release Note 中会发现有这么一段话:

The go command now embeds version control information in binaries. It includes the currently checked-out revision, commit time, and a flag indicating whether edited or untracked files are present. Version control information is embedded if the go command is invoked in a directory within a Git, Mercurial, Fossil, or Bazaar repository, and the main package and its containing main module are in the same repository. This information may be omitted using the flag -buildvcs=false.

可以看到从 Go 1.18 开始 Go 自带了 Version control ,这就导致如果你是在一个 Git 目录下,且这个目录有一个坏掉的 .git 目录的情况下用 go build 之类的操作的话,就会遇到以上的问题,但为什么会有一个坏掉的 .git 目录在构建环境中呢?

这得从某人加入了 .dockerignore 文件开始说起,由于技艺不精,一开始我们的 .dockerignore 里面是写成了 .git/*,导致这个目录本身上并没有被 Ignore 掉(而且这种问题在不出错的时候真的很难看出来,毕竟谁会构建镜像的时候去 Dockerfile 里面加一句 ls -a 看看到底传进去了什么东西呢,不看不知道,一看发现大家基本都在里面:

#0 0.071 .                                                                                                                                                     
#0 0.071 ..                                                                                                                                                    
#0 0.071 .dockerignore                                                                                                                                         
#0 0.071 .git
#0 0.071 .github
#0 0.071 .gitignore
#0 0.071 .idea
#0 0.071 Dockerfile
#0 0.071 builds
#0 0.071 config.go
#0 0.071 config.json
#0 0.071 coverage.txt
#0 0.071 encoder.go
#0 0.071 encoder_test.go
#0 0.071 exhaust
#0 0.071 go.mod
#0 0.071 go.sum
#0 0.071 helper.go
#0 0.071 helper_test.go
#0 0.071 pics
#0 0.071 prefetch.go
#0 0.071 prefetch_test.go
#0 0.071 remote-raw
#0 0.071 router.go
#0 0.071 router_test.go
#0 0.071 scripts
#0 0.071 update.go
#0 0.071 update_test.go
#0 0.071 webp-server.go
#0 0.071 webp-server_test.go
#0 0.161 error obtaining VCS status: exit status 128
#0 0.161        Use -buildvcs=false to disable VCS stamping.
------

由于写的是 .git/*,所以构建环境中会有个空的 .git 目录,如果在 Dockerfile 中加入一个 git status 的话可以看到:

 > [builder 6/7] RUN cd /build && git status:
#0 0.078 fatal: not a git repository (or any of the parent directories): .git

所以这里就导致了上面的报错。

知道了怎么回事我们就可以针对性地解决这个问题了,如果你和我们一样在容器中构建应用的话,可以:

  • 直接在 .dockerignore 中加入一行:
  .git

来让 Docker 构建的时候直接不复制 .git 目录到构建环境中。 * 或者也可以丑一点,在 Dockerfile 中加入一行 rm -rf .git,当然,这样就有点诡异了,如果本地 .git 目录太大的话, COPY . 这类操作会非常的卡 * 或者你也可以把 Go 版本降级到 1.18 以下

如果你是在容器以外的地方构建应用,且你的 Git 仓库有问题(无论是权限问题还是啥)的话,在构建参数中加入一个 -buildvcs=false 即可。

Epilogue

我们遇到的问题是由两个问题构成的:

  1. .dockerignore 文件一开始就没写对
  2. 升级的时候没有完整阅读 release note,而是想当然地改了镜像版本进行构建(当然这个 -buildvcs 默认开启也有点奇怪)

坏处是阻塞了一会我们的开发,好处是帮我们发现了一个之前一直没注意到的问题,可能这就是 “工业级语言” 吧。

修完这个问题之后,我们又开始和 Golang 官方库解析图片时的报错去斗争了,谁能想到一个语言要解析一个图片还得 os.Open() 之后 png.Decode() 来操作,而且还会报 invalid format: invalid checksumcorrupted: invalid JPEG format: missing 0xff00 sequence 这种错呢?可以参考: https://github.com/webp-sh/webp_server_go/issues/137.


comments powered by Disqus