缩小Go二进制文件大小 环境 youmen@youmendeMacBook-Pro % gcc -dumpversion 12.0.5 youmen@youmendeMacBook-Pro % go version go version go1.16.5 darwin/amd64
go build使用的是静态编译,会将程序的依赖一起打包,这样一来编译得到的可执行文件可以直接在目标平台运行,无需运行环境(例如 JRE)或动态链接库(例如 DLL)的支持。
虽然 Go 的静态编译很方便,但也存在一个问题:打包生成的可执行文件体积较大,毕竟相关的依赖都被打包进来了;
下面假设我们将本地编译好的 bluebell 二进制文件、配置文件和静态文件等上传到服务器的/data/app/bluebell目录下。
补充一点,如果嫌弃编译后的二进制文件太大,可以在编译的时候加上-ldflags "-s -w"参数去掉符号表和调试信息,一般能减小20%的大小;
在程序编译的时候可以加上-ldflags "-s -w"参数来优化编译,原理是通过去除部分链接和调试等信息来减小编译生成的可执行程序体积,具体参数如下:
-a:强制编译所有依赖包
-s:去掉符号表信息,不过panic的时候stace trace就没有任何文件名/行号信息了
-w:去掉DWARF调试信息,不过得到的程序就不能使用gdb进行调试了
若对符号表无需求,-ldflags直接添加"-s"即可
注:不建议-w和-s同时使用
-o:指定输出的文件名
-k:保留备份原文件
-1:最快压缩,共1-9九个级别
-9:最优压缩,与上面对应
-d:解压缩decompress,恢复原体积
-l:显示压缩文件的详情,例如upx -l main.exe
-t:测试压缩文件,例如upx -t main.exe
-q:静默压缩be quiet
-v:显示压缩细节be verbose
-f:强制压缩
-V:显示版本号
-h:显示帮助信息
--brute:尝试所有可用的压缩方法,slow
--ultra-brute:比楼上更极端,very slow
UPX的原理upx 压缩后的程序和压缩前的程序一样,无需解压仍然能够正常地运行,这种压缩方法称之为带壳压缩,压缩包含两个部分:
在程序开头或其他合适的地方插入解压代码;
将程序的其他部分压缩;
执行时,也包含两个部分:
首先执行的是程序开头的插入的解压代码,将原来的程序在内存中解压出来;
再执行解压后的程序;
也就是说,upx 在程序执行时,会有额外的解压动作,不过这个耗时几乎可以忽略。
如果对编译后的体积没什么要求的情况下,可以不使用 upx 来压缩。一般在服务器端独立运行的后台服务,无需压缩体积。
这个Dockerfile中使用了两次FROM指令,第二条FROM scratch行,它告诉Docker从一个全新的,完全空的容器镜像重新开始,然后将上个阶段编译好的程序复制到其中。这个才是我们随后将用于运行的Go应用程序的容器镜像。
scratch镜像是Docker项目预定义的最小的镜像。 Docker用于Go程序的多阶段构建很常见,使用scratch镜像可以节省大量空间,因为我们实际上不需要Go工具或其他任何东西来运行我们的编译好的程序,这可能也是Go在容器时代的一个优势吧。
使用scratch镜像制作的Go应用镜像在运行时会有一个不识别时区的问题,这个也是我们最近项目往Kubernetes上迁移时遇到的第一个问题,不过还好经过Google和查看Go加载时区的源码找到了解决方法
多阶段允许在创建Dockerfile时使用多个from,它非常有用,因为它使我们能够使用所有必需的工具构建应用程序。举个例子,首先我们使用Golang的基础镜像,然后在第二阶段的时候使用构建好的镜像的二进制文件,最后阶段构建出来的镜像用于发布到我们自己的仓库或者是用于上线发布。
在上述的案例中,我们总共有三个阶段:
1 . build编译阶段
2 . certs(可选,可有可无)证书认证阶段
3 . prod生产阶段