本小节源码下载路径:demo12
在实际开发中,当开发完一个 apiserver 特性后,会编译 apiserver 二进制文件并发布到生产环境,很多时候为了定位问题和出于安全目的(不能发错版本),
我们需要知道当前 apiserver 的版本,以及一些编译时候的信息,如编译时 Go 的版本、Git 目录是否 clean,以及基于哪个git commmit
来编译的。
在一个编译好的可执行程序中,我们通常可以用类似./app_name -v
的方式来获取版本信息。
我们可以将这些信息写在配置文件中,程序运行时从配置文件中取得这些信息进行显示。但是在部署程序时,除了二进制文件还需要额外的配置文件,不是很方便。
或者将这些信息写入代码中,这样不需要额外的配置,但要在每次编译时修改代码文件,也比较麻烦。Go 官方提供了一种更好的方式:
通过-ldflags -X importpath.name=value
(详见 -ldflags -X importpath.name=value)来给程序自动添加版本信息。
假设我们程序发布的流程是这样:
- 编码完成,提交测试工程师测试
- 测试工程师测试代码,提交 bug,更改 bug 并重新测试后验证通过
- 开发人员把验证通过的代码合并到 master 分支,并打上版本号:
git tag -a v1.0.0
- 开发人员将
v1.0.0
版本发布到生产环境
最终发布后,我们希望通过./apiserver -v
参数提供如下版本信息:
- 版本号
- git commit
- git tree 在编译时的状态
- 构建时间
- go 版本
- go 编译器
- 运行平台
为了实现这些功能,我们首先要在main
函数中添加用于接收-v
参数的入口
(详见 demo12/main.go):
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
}
...
}
通过pflag
来解析命令行上传入的-v
参数。
通过pkg/version
的Get()
函数来获取 apiserver 的版本信息。
通过json.MarshalIndent
来格式化打印版本信息。
pkg/version
的Get()
函数实现为(详见 demo12/pkg/version):
func Get() Info {
return Info{
GitTag: gitTag,
GitCommit: gitCommit,
GitTreeState: gitTreeState,
BuildDate: buildDate,
GoVersion: runtime.Version(),
Compiler: runtime.Compiler,
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
}
}
其中gitTag
、gitCommit
、gitTreeState
等变量的值是通过-ldflags -X importpath.name=value
在编译时传到程序中的。
为此我们需要在编译时传入这些信息,在Makefile
中添加如下信息
(详见 demo12/Makefile):
SHELL := /bin/bash
BASEDIR = $(shell pwd)
# build with verison infos
versionDir = "apiserver/pkg/version"
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)
ldflags="-w -X ${versionDir}.gitTag=${gitTag} -X ${versionDir}.buildDate=${buildDate} -X ${versionDir}.gitCommit=${gitCommit} -X ${versionDir}.gitTreeState=${gitTreeState}"
并在go build
中添加这些flag
:
go build -v -ldflags ${ldflags} .
``-w`为去掉调试信息(无法使用 gdb 调试),这样可以使编译后的二进制文件更小。