基本知识
我们都从相同的基础开始,编译一个简单的 go 程序:
package main
import "fmt"
func main() {
fmt.Printf("hello world")
}
$go build -o mybin *.go
将在你的文件夹中生成一个二进制的“ mybin”
交叉编译
如果我们希望这个程序直接拷贝就可以运行到我们的服务器上,我们应该怎么做? 将这个编译好的文件直接制作成docker基于alpine的镜像,我们会发现镜像无法运行
我们需要进行交叉编译实现一个静态的编译好的文件
$GOARCH 目标平台(编译后的目标平台)的处理器架构(386、amd64、arm) $GOOS 目标平台(编译后的目标平台)的操作系统(darwin、freebsd、linux、windows)
$ GOOS=linux GOARCH=arm GOARM=6 go build -o mybin-arm *.go
这将构建一个 linux + arm (armel)兼容的二进制文件
如果你想运行在arm并且也可以运行在x64的系统上
$ GOOS=linux GOARCH=amd64 go build -o mybin-arm *.go
Debian 交叉编译依赖项
无聊但实际上很简单:
# aptitude install cross-gcc-dev -t jessie-backports
# dpkg --add-architecture armhf
# dpkg --add-architecture armel
# aptitude update
# aptitude install crossbuild-essential-armel crossbuild-essential-armhf
然后通过这样进行编译
$ CC=arm-linux-gnueabi-cc GOOS=linux GOARCH=arm GOARM=6 go build -o mybin-arm *.go
更多类型的二进制
现在,当 libc 版本差异、网络库、缺少证书等等给您带来麻烦时,您可以使用更多的静态二进制文件。
$ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo -ldflags '-w' -o mybin *.go
你会发现命令越来越长了
现在我们禁用 CGO,你可能不需要根据库和你做的事情,强制重建与-a 和标记 netgo,以确保我们使用内置的网络包,而不是系统的。
禁用 CGO 消除了交叉编译依赖关系的需要。
这个 ldflags-w 只是禁用调试让文件更小。
但是如果你真的需要集成 CGO,你可以考虑这个:
$ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo -ldflags '-w -extldflags "-static"' -o mybin *.go
这将使链接的 c 部件也静态地转化为二进制文件,从而降低兼容性风险。
最小的go镜像
从上面的操作我们可以通过交叉编译生成一个最小的go镜像
FROM scratch
COPY mybin /mybin
CMD ["/mybin"]
这意味着你的 docker 映像实际上只是你的二进制,没有其他的内容包含在里面。
库 vs 二进制
有时候你可能希望构建一个库,让他也有一个默认的二进制文件
与制作不同的项目相反,您可以使用所需的包名称作为库
如下:
package main
import "myproj"
func main() {
}
现在你可以通过简单的导入在其他项目中使用这个库,但是你可以用它来制作一个二进制文件:
go build -o mybin cmd/myproj/main.go
好处是你不需要编译 *.go
相关链接
https://medium.com/@diogok/on-golang-static-binaries-cross-compiling-and-plugins-1aed33499671