为OpenWrt路由器编译哪吒监控

  最近想重新写一套监控系统代替已经使用几年的ServerStatus云探针,在搜索过程中发现了一个现成的轮子哪吒监控完美的符合自己的需求,遂部署到自己的各设备上。但是官方提供的基于mips架构的二进制文件一直提示错误,于是打算自己重新编译一份用于路由器。

编译哪吒监控

  Go语言是Google开发的一种静态强类型的编译型语言,天生支持高并发,因此常用于网络通信相关程序的开发之中,比如著名的V2Ray项目,哪吒监控也是基于Go语言所开发的。此前完全没有接触过Go语言相关的开发工作,但是意料之外的发现编译一个Go语言项目,是如此的轻松,仅需要安装Go语言环境,并不需要类似JavaMaven或是JavaScriptnpm打包工具。

git clone git@github.com:naiba/nezha.git
cd nezha
go build cmd/agent/main.go
./main --help

交叉编译至路由器

  路由器使用的CPU是基于mips架构的MediaTek MT7621,进一步的发现Go语言的交叉编译完全不需要任何的交叉编译工具链,配置好环境变量,即可编译出各种平台(Windows,Liunx,macOS)各种架构(x86,arm,mips)下的二进制文件。

GOOS=linux GOARCH=mips go build cmd/agent/main.go
./main --help
./main: line 1: syntax error: unexpected "("

  这样编译出的文件其大小和行为都和官方提供的二进制文件一致,运行即提示line 1: syntax error: unexpected "("错误。经过查询发现这个问题多半是架构没有选对,经过再三确认MT7621的确是mips架构并不是mips64架构,于是怀疑是字节序的问题,采用小端模式重新编译,发现产生了不一样的问题。

GOOS=linux GOARCH=mipsle go build cmd/agent/main.go
./main --help
Illegal instruction

  再次查询,MT7621本身没有FPU(浮点运算器),需要使用软件模拟浮点运算器,于是加入softfloat编译参数,至此成功编译出MT7621CPU可运行的二进制文件。

GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build cmd/agent/main.go
./main --help
Usage of ./main:
  -d 允许不安全连接
  -i s
     unused 旧Agent兼容
  -p string
     Agent连接Secret
  -s string
     管理面板RPC端口 (default "localhost:5555")

为二进制文件瘦身

  虽然已成功编译出可用的二进制文件,但是其大小有17836KB,在寸土寸金的32MB闪存的路由器上,其直接占去了一半的空间,因此下一个目标就是为二进制文件瘦身。

GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "-s -w" cmd/agent/main.go

  为ld传递-s -w参数,使生成的二进制文件去除调试信息,产生Release模式下的二进制文件,文件大小立刻缩小了5M,占用12736KB。加入trimpath参数,删除二进制文件中的系统文件路径,进一步缩减大小,在x86架构下减少了约130KB,但是在mips架构下,并没有缩减任何的大小。

GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -trimpath -ldflags "-s -w" cmd/agent/main.go

  使用upx工具,进一步压缩二进制文件。最新的3.96版本在mips架构下存在bug,压缩后的二进制文件无法运行,而新版本又尚未发布,因此必须回退使用3.95版本。以下为upx工具各压缩比下的性能测试。

upx --best -o nezha-agent main
参数 程序大小 压缩比 耗时
原始程序 12736KB
-1 5042KB 60.41% 2s
-9 4222KB 67.53% 15s
–best 4031KB 68.35% 116s

总结

  提前配置好Go运行环境,下载upx压缩工具,不要使用3.96版本,使用3.95版本或未来3.96后的新版本。

git clone git@github.com:naiba/nezha.git
cd nezha

GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -trimpath -ldflags "-s -w" cmd/agent/main.go

upx --best -o nezha-agent main
./nezha-agent --help

参考文献