从 go run 到可发布二进制
开发时我们常用:
go run .
但真正发布命令行工具或服务时,通常会构建二进制:
go build -o app .
Go 的发布体验很直接。很多程序构建后就是一个可执行文件,复制到目标机器就能运行。入门阶段除了 go build,还值得了解几个常见话题:如何指定输出名,如何注入版本号,如何交叉编译,运行时配置和构建时信息应该怎么分。
这篇文章面向小工具和小服务发布,讲 Go 构建参数的实用基础。
指定输出文件
go build -o notes .
如果项目入口在 cmd/notes:
go build -o notes ./cmd/notes
构建多个平台时,可以把输出放到目录:
mkdir -p dist
go build -o dist/notes .
不要把构建产物提交到源码仓库,除非项目明确需要发布二进制。一般源码只提交 .go、配置、文档和必要资源。
注入版本号
程序里定义变量:
package main
var version = "dev"
func main() {
if len(os.Args) > 1 && os.Args[1] == "version" {
fmt.Println(version)
return
}
}
构建时注入:
go build -ldflags "-X main.version=v1.0.0" -o notes .
运行:
./notes version
输出:
v1.0.0
-X 后面是包路径加变量名。对于 main 包,常见写法是 main.version。变量必须是字符串,并且不能是常量。
你也可以注入 commit:
var commit = "unknown"
构建:
go build -ldflags "-X main.version=v1.0.0 -X main.commit=abc123" .
这对排查线上版本很有用。
交叉编译
Go 很容易交叉编译。比如在 macOS 上构建 Linux amd64:
GOOS=linux GOARCH=amd64 go build -o dist/app-linux-amd64 .
构建 Windows:
GOOS=windows GOARCH=amd64 go build -o dist/app.exe .
常见组合:
linux/amd64
linux/arm64
darwin/amd64
darwin/arm64
windows/amd64
如果项目使用 CGO,交叉编译会复杂很多。入门阶段如果没有必要,尽量保持纯 Go 依赖,这样发布更轻。
运行时配置不要靠构建写死
版本号适合构建时注入,但端口、数据库地址、日志级别更适合运行时配置:
port := getenv("PORT", "8080")
不要为每个环境构建一个不同二进制:
go build -ldflags "-X main.databaseURL=prod..."
这会把敏感信息写进二进制,也让同一个版本在不同环境不一致。更好的方式是同一个二进制,通过环境变量或配置文件控制运行行为。
func getenv(key, fallback string) string {
value := strings.TrimSpace(os.Getenv(key))
if value == "" {
return fallback
}
return value
}
构建信息回答“这是什么版本”,运行配置回答“它在这个环境怎么运行”。两者不要混在一起。
发布脚本可以先保持朴素
小项目不一定需要复杂 CI 才能规范构建。一个简单脚本就能把版本、平台和输出目录固定下来:
#!/bin/sh
set -eu
VERSION=${VERSION:-dev}
COMMIT=$(git rev-parse --short HEAD)
mkdir -p dist
GOOS=linux GOARCH=amd64 go build \
-ldflags "-X main.version=$VERSION -X main.commit=$COMMIT" \
-o dist/notes-linux-amd64 .
GOOS=darwin GOARCH=amd64 go build \
-ldflags "-X main.version=$VERSION -X main.commit=$COMMIT" \
-o dist/notes-darwin-amd64 .
运行:
VERSION=v1.0.0 sh scripts/build.sh
脚本的价值不是高级,而是把构建方式写下来,避免每个人手敲不同命令。以后迁移到 CI,也可以把同样逻辑搬过去。
还可以加一个版本输出:
var version = "dev"
var commit = "unknown"
func printVersion() {
fmt.Printf("version=%s commit=%s\n", version, commit)
}
当用户反馈问题时,你可以先让他执行:
./notes version
比起猜“你运行的是不是最新版本”,直接拿版本和 commit 更可靠。
小结
Go 构建发布的基础命令是 go build -o name .。需要版本信息时,可以用 -ldflags "-X main.version=..." 注入字符串变量;需要不同平台二进制时,用 GOOS 和 GOARCH 交叉编译;运行时端口、路径、密钥和数据库地址应该通过环境变量或配置文件传入。
入门项目能从一开始区分构建信息和运行配置,后面部署会清楚很多。Go 的二进制发布很简单,但简单不代表随意,把版本、平台和配置边界想清楚,工具和服务才容易维护。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。