写点什么

Datadog 如何将其 Agent 的 Go 二进制文件缩减 77%

  • 2026-03-16
    北京
  • 本文字数:1183 字

    阅读完需:约 4 分钟

在 5 年时间里,Datadog Agent 从 428 MiB 增长到了 1.22 GiB。Datadog 工程师们开始着手缩减其二进制文件的大小。他们发现,对于大多数 Go 二进制文件,膨胀的主要原因是隐藏的依赖项、禁用的链接器优化以及 Go 编译器和链接器中的微妙行为。

 

不管是对于我们自己,还是对于我们的用户,这种增长都产生了不利的影响:网络成本和资源使用增加,Agent 感知变差,并且在资源受限的平台上使用 Agent 变得更加困难。

 

Datadog 软件工程师 Pierre Gimalac 写道,为了解决这个问题,他们采取了一些措施,以便尽可能地缩小二进制文件的大小,其中包括:审计导入项、隔离可选代码以及消除反射/插件陷阱。

 

实际上,在分析了 Agent 的增长情况后,Datadog 工程师发现,这主要是由新功能、额外的集成和大型第三方依赖项(如 Kubernetes SDK)引起的。特别是,Go 的依赖模型包括传递性导入,使得即使是一个小的变化也可能引入数百个包。

 

Datadog 工程师设计了两种实用的方法来移除不必要的依赖项:使用构建标签(//go:build feature_x)来排除可选代码,并将代码移到单独的包中,从而使非可选包尽可能保持小巧。这两种技术都需要系统地审计导入项,以便确定哪些文件或包可以从给定的构建中排除。例如,仅仅将一个函数移动到它自己的包中,就从一个不使用它的二进制文件中移除了约 570 个包和约 36 MB 的生成代码。

 

审计依赖项并不是一项简单的任务,但 Go 生态系统提供了三个有用的工具:go list,可以列出构建中使用的所有包;goda,可以可视化依赖图和导入链,帮助开发人员理解为什么需要某个特定的依赖项;go-size-analyzer,可以显示每个依赖项使二进制文件的空间占用增加了多少。

 

除了优化依赖项外,通过最小化反射机制的使用,Datadog 工程师额外获得了 20%的大小缩减。反射会悄悄地禁用一些链接器优化,包括死代码消除:

 

如果你使用非恒定方法名,那么链接器在构建时就无法知道哪些方法将在运行时被使用。因此,它需要保留每个可达类型的每个导出方法,以及它们所依赖的所有符号,这可能会大幅增加最终二进制文件的大小。

 

为了解决这个问题,他们尽可能地消除了动态反射,无论是在他们的代码库中还是在依赖项中。后一步骤需要向 kubernetes/kubernetes、uber-go/dig、google/go-cmp 等项目提交多个 PR。

 

另一个禁用死代码消除的功能是 Go 插件(一种允许 Go 程序在运行时动态加载 Go 代码的机制)。实际上,仅仅导入 plugin 包就导致链接器将二进制文件视为动态链接的,“这会禁用方法死代码消除,甚至迫使链接器保留所有未导出的方法”。在部分构建中,这一变化额外带来了约 20%的缩减。

 

最后,Gimalac 强调,这些改进是在六个月的时间里实现的。最重要的是,没有移除任何功能。他的文章里提供了更多的细节,如果想了解整个事情的来龙去脉,请阅读原博文

 

声明:本文为 InfoQ 翻译,未经许可禁止转载。

 

原文链接:https://www.infoq.com/news/2026/03/datadog-go-binary-optimization/