在实际开发中,当开发完一个 apiserver 特性后,会编译 apiserver 二进制文件并发布到生产环境,很多时候为了定位问题和出于安全目的(不能发错版本),我们需要知道当前 apiserver 的版本,以及一些编译时候的信息,如编译时 Go 的版本、Git 目录是否 clean,以及基于哪个 git commmit 来编译的。在一个编译好的可执行程序中,我们通常可以用类似 ./app_name -v
的方式来获取版本信息。
我们可以将这些信息写在配置文件中,程序运行时从配置文件中取得这些信息进行显示。但是在部署程序时,除了二进制文件还需要额外的配置文件,不是很方便。或者将这些信息写入代码中,这样不需要额外的配置,但要在每次编译时修改代码文件,也比较麻烦。Go 官方提供了一种更好的方式:通过 -ldflags -X importpath.name=value
(详见 -ldflags -X importpath.name=value)来给程序自动添加版本信息。
1.增加参数-v
package main
import (
"encoding/json"
"fmt"
"os"
...
v "apiserver/pkg/version"
...
)
var (
version = pflag.BoolP("version", "v", false, "show version info.")
)
func main() {
pflag.Parse()
if *version {
v := v.Get()
marshalled, err := json.MarshalIndent(&v, "", " ")
if err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
fmt.Println(string(marshalled))
return
}
...
}
pkg/version包中的实现:
base.go
package version
// 变量通过-ldflags -X importpath.name=value在编译时传入程序中
var (
gitTag = "" //
gitBranch = ""
gitCommit = "$Format:%H$"
gitTreeState = "not a git tree"
buildDate = "1970-01-01T00:00:00Z"
)
version.go
package version
import (
"fmt"
"runtime"
)
type Info struct {
GitTag string `json:"gitTag"`
GitBranch string `json:"gitBranch"`
GitCommit string `json:"gitCommit"`
GitTreeState string `json:"gitTreeState"`
BuildDate string `json:"buildDate"`
GoVersion string `json:"goVersion"`
Compiler string `json:"compiler"`
Platform string `json:"platform"`
}
func (info Info) String() string {
return info.GitTag
}
func Get() Info {
return Info{
GitTag: gitTag,
GitBranch: gitBranch,
GitCommit: gitCommit,
GitTreeState: gitTreeState,
BuildDate: buildDate,
GoVersion: runtime.Version(),
Compiler: runtime.Compiler,
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
}
}
其中的gitTag, gitBranch,gitComit,gitTreeState,buildDate信息都是在build的时候, 使用-ldflags -X key=value的方式指定参数
Makefile文件中:
.PHONY: all build windows mac run tidy check cert test cover lint docker clean help
BIN_FILE=golang-demo
MAIN_FILE=./cmd/app/main.go
SHELL := /bin/bash
BASEDIR = $(shell pwd)
# build with verison infos
versionDir = "golang-demo/pkg/version"
## 获取仓库tag
gitTag = $(shell if [ "`git describe --tags --abbrev=0 2>/dev/null`" != "" ];then git describe --tags --abbrev=0; else git log --pretty=format:'%h' -n 1; fi)
buildDate = $(shell TZ=Asia/Shanghai date +%FT%T%z)
gitCommit = $(shell git log --pretty=format:'%H' -n 1)
gitTreeState = $(shell if git status|grep -q 'clean';then echo clean; else echo dirty; fi)
gitBranch = $(shell git rev-parse --abbrev-ref HEAD)
ldflags="-w -X ${versionDir}.gitTag=${gitTag} -X ${versionDir}.buildDate=${buildDate} -X ${versionDir}.gitBranch=${gitBranch} -X ${versionDir}.gitCommit=${gitCommit} -X ${versionDir}.gitTreeState=${gitTreeState}"
# 默认执行
all: build
# 打包成二进制文件
build: tidy check
@CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags $(ldflags) -o $(BIN_FILE)_linux $(MAIN_FILE)
windows: tidy check
@CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -v -ldflags "-w -s" -o $(BIN_FILE)_windows.exe $(MAIN_FILE)
mac: tidy check
@CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -v -ldflags "-w -s" -o $(BIN_FILE)_darwin $(MAIN_FILE)
# 启动服务
run:
@go run $(MAIN_FILE) -c ./config/config.yaml
tidy:
@go mod tidy
# 代码验证
check:
@gofmt -s -w ./
@go vet $(MAIN_FILE)
cert:
@openssl req -new -nodes -x509 -out config/server.crt -keyout config/server.key -days 3650 -subj "/C=DE/ST=NRW/L=Earth/O=Random Company/OU=IT/CN=127.0.0.1/emailAddress=xxxxx@qq.com"
# 单元测试
test:
@go test ./...
# 覆盖测试
cover:
@go test -coverprofile xx.out
@go tool cover -html=xx.out
lint:
@golangci-lint run --enable-all
docker:
@docker build -t golang-demo:latest .
clean:
@go clean -x
rm -f "xx.out"
help:
@echo "make - 格式化go代码 并编译生成二进制文件"
@echo "make test - 执行测试case"
@echo "make check - 代码检查"
@echo "make cover - 检测测试覆盖率"
@echo "make run - 启动服务"
@echo "make lint - 执行代码检查"
@echo "make docker - 生成Docker镜像"
@echo "make clean - 清理中间目标文件"
@echo "make build - 编译成二进制文件(默认:Linux)"
@echo "make windows - 编译成二进制文件(Windows)"
@echo "make mac - 编译成二进制文件(Mac)"
# include ./TestMakefile 包含其他Makefile
-w: 去除调试信息, 无法使用gdb调试, 但是二进制文件更小
执行./golang-demo -v
{
"gitTag": "d62a870",
"gitBranch": "master",
"gitCommit": "d62a87056066b2f8ecf6caf64b3bd1d80d60a5e1",
"gitTreeState": "dirty",
"buildDate": "2023-08-24T17:10:45+0800",
"goVersion": "go1.20.7",
"compiler": "gc",
"platform": "linux/amd64"
}