写点什么

使用 Zig 在 arm64 上引导 Uber 的基础设施

  • 2023-07-04
    北京
  • 本文字数:5447 字

    阅读完需:约 18 分钟

使用Zig在arm64上引导Uber的基础设施

简   述


2021 年 11 月,我们决定评估 arm64 架构在 Uber 的可行性。我们的大多数服务是用 Go 或 Java 编写的,但我们的构建系统只能编译成 x86_64。现在,得益于开源合作,Uber 拥有了一个独立于系统的构建工具链,可以无缝地支持多种架构。我们使用这个工具链来引导 arm64 主机。本文将分享我们是如何着手去做这件事情的,以及我们早期的想法、遇到的问题、达成的一些成就和未来的方向。


我们从 2021 年 11 月开始使用专门的 Linux/x86_64 基础架构,而到了 2023 年 1 月,我们有:


  • 用于生产环境服务器架构(x86_64 和 arm64)的 C++ 工具链,由 zig cc 提供支持;

  • 一些在 arm64 硬件上运行的核心基础设施服务,为未来的扩展提供了可能性。


让我们来看看我们是如何做到的。


为什么要考虑 arm64 架构


所有的主流云供应商都在 arm64 上投入巨资,再加上 arm64 与古老的 x86_64 相比所表现出来的平台优势(能耗、价格、计算性能),我们觉得很有必要认真考虑让 arm64 成为我们平台的一部分。


于是,我们开始尝试自己去探究。我们的第一个目标如下所述:


在 arm64 架构上运行一个大型的应用程序,并对可能节省的成本进行度量。


其中一个关键点是最小化运行和基准测试消耗多个核心的服务所需的工作量。我们找到了两种截然不同的方法:


  • 在并行区域或现有区域中的独立集群提供基本的 arm64 支持,并在那里运行测试(实验质量);

  • 让所有的核心基础设施都知道现在不止一种架构,然后像生成其他 SKU 一样生成 arm64 主机并测试应用程序。


考虑到最小化工作量是我们优先考虑的事项,所以第一个选项似乎更适合我们。毕竟,我们为什么要把时间和金钱投入到有可能被放弃的东西上呢?我们考虑运行一个“并行区域”,它具备 arm64 架构,但在其他方面与生产环境是分离的(并且质量要求更为宽松,方便我们快速前进)。


不久之后,我们有了一个更重要的支持 arm64 的理由:如果我们可以在 arm64 上运行工作负载,就可以让平台的能力多样化,从而让自己处于一个更有利的位置。于是,我们的使命变成了(直到今天仍是如此):


通过在 arm64 上部署一些生产应用程序来降低 Uber 的计算成本、增加容量多样性,以及使我们的平台现代


我们最初是带着原型思维开始的,但现在却有了 180 度转变,形成了一个指导原则:


没有 hack,所有的内容都在主线上(也就是说,没有长期的分支或补丁)。


既然我们的核心基础设施需要提供一流的 arm64 支持,那么这个项目就很自然地被分成两个部分:


  1. 第一个任务是将包含了我们几乎所有基础架构代码的 Go 代码库编译成 arm64 二进制文件;

  2. 修改与构建、存储、下载和执行代码相关的所有东西(构建主机、工件存储和调度器),让它们知道现在存在两种架构。


那么如何编译成 arm64 二进制文件?当然是直接在 arm64 主机上进行原生构建,或者通过交叉编译。我们有必要先来了解一下原生编译和交叉编译的差异和要求。


原生编译和交叉编译的基础知识


一些我们可能不太熟悉的术语:


  • 二进制文件是由源代码编译而来的机器代码程序。

  • 工具链是将源代码编译为二进制文件所需的一组工具,通常包括预处理器、编译器、链接器等。

  • 密闭(hermetic)工具链是指无论在什么样的环境下,只要给定相同的输入,总是产生相同输出的工具链。这里的“密闭”是指它不使用来自主机的文件,并且包含编译文件所需的所有东西。

  • 主机(host)是指编译二进制文件的机器。

  • 目标平台(target)是指运行二进制文件的机器。

  • 在进行原生编译时,主机和目标是相同的平台(即操作系统、处理器架构和共享库是相同的)。

  • 在进行交叉编译时,主机和目标是不同的平台(例如,从 macOS arm64 (M1) 编译成 x86_64 Linux)。有时候,目标机器可能无法编译代码,但可以运行。例如,一块智能手表可以运行已编译的代码,但不能运行编译器,因此我们可以使用交叉编译器为手表编译程序。

  • sysroot 是目标平台文件系统的归档。例如,特定于目标平台的头文件、共享库、静态库。通常是交叉编译工具链所必需的,下面将会讨论。

  • aarch64 或 arm64 是指处理器架构。


下图显示了如何通过原生编译(左)和交叉编译(右)将源文件 main.c 编译成可执行文件。



图 1:输入文件 main.c 原生编译(左)或交叉编译(右)为 aarch64 架构。


原生编译只需要较少的配置和准备工作就可以使用,因为这是大多数编译器工具链的默认模式。从表面上看,我们可以在云供应商的平台上启动一些 arm64 虚拟机,并从那里开始引导我们的工具。但是,我们所有的服务器都使用相同的基础镜像,包括构建主机。基础镜像包含许多从 Go 代码库编译出来的内部工具。因此,我们遇到了一个先有鸡还是先有蛋的问题:如何为我们的第一个 arm64 构建主机编译工具?


示例:使用 GCC 和 Clang 进行交叉编译


让我们在 x86_64 Linux 主机上编译一个 C 文件,目标平台是 Linux aarch64:



GCC 调用目标平台特定的可执行文件(aarch64-linux-gnu-gcc),而 Clang 接受目标平台作为命令行参数(-target <…>):



表面上看,用 GCC 和 Clang 交叉编译 C 源文件似乎很容易,但背后都发生了什么?


基于 LLVM 的 C/C++ 工具链


“clang”使用哪些文件来构建最终的可执行文件?我们来跟踪一下:



以下是这些相关的文件:


  • (没有显示出来的)工具:C 编译器(Clang)和链接器(ld)。

  • /usr/aarch64-linux-gnu/include 中的头文件。这些通常是 GNU C 库头文件。有些程序使用 Linux 内核的公共头文件,但本例中没有。头文件是特定于目标平台的。

  • 编译的、特定于目标架构的库:

  • 动态链接器 /usr/aarch64-linux-gnu/lib/ld-linux-aarch64.so.1;

  • C 库,共享对象:/usr/aarch64-linux-gnu/lib/libc.so.6;

  • 程序加载器:crt.o。

  • 其他库:libgcc 和 libc_nonshared。


现在我们已经知道交叉编译器使用了哪些东西,我们可以将依赖项分为两类:


  • 特定于主机的工具(编译器、链接器和其他与目标平台无关的程序);

  • 特定于目标平台的库和头文件,它们是为目标平台编译最终程序所必需的。


Uber 需要支持以下这些目标平台:


  • Linux x86_64(带有 glibc 2.28);

  • Linux x86_64(带有 glibc 2.31);

  • Linux x86_64(带有 musl);

  • Linux arm64(aarch64,带有 glibc 2.31);

  • Linux arm64 (aarch64,带有 musl)。


在撰写本文时,GCC 和 LLVM 都不能交叉编译 macOS 二进制文件。因此,我们维护了一个专门的构建集群来编译 macOS 目标平台。交叉编译 macOS 目标平台是非常有必要的,但我们目前还没有做到这一点。


以下是我们目前支持的主机平台:


  • Linux x86_64:构建集群、DevPod 和开发者笔记本电脑;

  • macOS x86_64:老一代 macOS 开发者笔记本电脑;

  • macOS aarch64(Apple Silicon):新一代 macOS 开发者笔记本电脑。


下图画出了主机工具链、sysroot 以及它们之间的关系,每个主机工具链(左)都可以使用任意特定于目标平台的 sysroot(右):



图 2:基于 LLVM 的工具链需要每个主机和目标平台的 tarball(“sysroot”)


为了支持这些主机和目标平台,我们需要维护 8 个压缩文件:3 个工具链(每个主机架构需要一个编译的 LLVM)和 5 个目标平台的 sysroot。一个典型的 LLVM 工具链需要 500 到 700MB(压缩包),一个典型的 sysroot 需要 100 到 150MB(压缩包)。在编译代码之前,加上其他工具,总共需要下载和解压约 1.5GB 的压缩文件。Linux x86_64 的 Go 1.20 工具链压缩包为 95MB,是编译代码所需的最大的下载文件。


为了完整起见,我们来看一下 GCC。你可能还记得之前提到 GCC 交叉编译器是 aarch64-linux-gnu-gcc,这意味着每个主机和目标平台都需要一个完整的工具链。因此,如果我们要使用基于 GCC 的工具链,就需要维护 35=15 个工具链。如果我们添加一个新的主机平台(例如 Linux aarch64)和两个目标平台(分别针对 x86_64 和 aarch64 的 Linux glibc.2.36),那么需要维护的压缩包数量将跃升至 4


7=28 个!


在购买 Bazel 工具链时,我们评估了 GCC 和基于 LLVM 的工具链。LLVM 更受青睐,因为它需要维护的压缩文件数量的增长是线性的(而不是 GCC 那样的二次幂增长)。但我们能做得更好吗?


Zig 工具链


Zig 采用了不同的方式:它对所有受支持的目标平台使用了相同的工具链。



它在编译时使用了哪些文件?如果我们跟踪一下会发现,它只使用了来自 Zig SDK 的文件(中间文件放在 /tmp 目录下)。主机系统没有受到任何影响,这意味着 Zig 是完全独立的。


为什么 Zig 能做到这样,而 Clang 却不能?Clang 和 Zig 之间主要的差异是什么?Zig 需要的依赖项与 Clang 一样,我们来看一下:


  • 工具:C 编译器(Clang)和链接器(lld)。

  • 它们被静态地链接到 Zig 二进制文件中,对于 macOS,Zig 实现了自己的链接器。

  • /usr/aarch64-linux-gnu/…中的头文件。

  • Zig 捆绑了多个版本的 glibc、musl libc、linux 内核和其他一些头文件,并自动包含它们。

  • 编译好的特定于目标平台的库:动态链接器、glibc(多版本)、程序加载器。

  • Zig 根据具体的平台在后台动态编译所有这些文件。

  • 其他库:libgcc 和 libc_nonshared。

  • Zig 重新实现了这些库中的函数。


因此,Zig 可以用一个工具链编译所有受支持的目标平台。为了支持我们的 3 个主机和 5 个目标平台,我们需要从 https://ziglang.org/download 下载 3 个 Zig tarball 文件:



图 3:每个主机平台需要 1 个工具链。同一工具链可以编译所有目标平台。


Zig 作者 Andrew Kelley 在他的博客中更详细地解释了 Zig 在 Clang 之上添加了哪些东西。不管我们希望支持多少个目标平台,只需要一个主机工具链,这是非常诱人的。


我们尝试做一些其他工具链无法做到的事情:在 Linux 机器上交叉编译和链接 macOS 可执行文件:



尽管在 2021 年底,Zig 还只是一项未经验证的新技术,但一个主机平台一个 tar 包和交叉编译 macOS 目标的能力赢得了团队的青睐。我们开始使用 Zig,将 zig cc 整合到我们的 Go 代码库中。


Bazel 与 Zig


对于 Bazel 来说,只有一个 C++ 工具链(在本例中是 Zig SDK)是不够的:它还需要一些粘合代码,一个工具链配置。2022 年 2 月,Go 代码库对 zig cc 的初步支持是通过添加到一个配置标志来实现的:


bazel build –config=hermetic-cc <…>
复制代码


最开始所有的东西都不正常,大部分的测试都无法构建通过,更不用说执行了。我们开始慢慢解决这些问题。到 2022 年 9 月,所有测试都通过了。自 2023 年 1 月起,Zig 工具链可以将 Uber Go 代码库中的所有 C 和 C++ 代码编译到 Linux 目标平台。


Uber 自 2022 年 4 月以来一直在运行 Zig 生成的二进制文件,因此我们对 Zig 信心满满。Bazel 和 Zig 之间的粘合代码最初放在 Adam Bouhenguel 的代码库 bazel-zig-cc 中,后来被 Motiejus Jakštys 克隆并进一步开发,最终转到了 https://github.com/uber/hermetic_cc_toolchain。


因为与 Zig 软件基金会合作,我们可以寻求对我们来说重要的解决方案。Zig 的人帮助我们发现和修复 Go 和 Zig 中的问题。因为在 2021 年合作进展顺利,Uber 决定将合作关系延长到了 2023 年和 2024 年。Zig 软件基金会所做的所有工作都是开源的,这让更大社区从中受益。


对 arm64 支持的进展


等到工具链足够成熟,可以进行 arm64 平台编译,我们就开始在内部加强对 arm64 的支持。例如:


  • 当开发人员在 Go 代码库中定义了 Docker 镜像(使用 rules_docker,它相当于 Dockerfile,只是是在 Bazel 中使用),CI 将编译 x86_64 和 arm64 的依赖代码,并且如果无法编译就不允许通过。

  • 我们将 Go 代码库中所有的 Debian 包编译到了 arm64 并发布,尽管它们中的大部分不是我们必需的。与 Docker 镜像类似,CI 确保它们可以编译到 arm64 和 x86_64。目前不可能在我们的 Go 代码库中声明一个不能编译到 arm64 的新的 Debian 包。


在能够将程序编译为 arm64 之后,我们开始采用所有可以存储、下载和执行原生二进制文件的系统。现在,我们有:


  • 开发环境中的 arm64 主机,就像其他 x86_64 主机一样;

  • 运行在 arm64 主机上的几个核心基础设施服务(例如,内部构建的容器调度器和支配程序);

  • 继续扩大 arm64 的使用和支持。


我们 2023 年的计划包括:


  • 为 arm64 增加 Kubernetes 支持;

  • 在 Kubernetes 的 arm64 主机上运行面向客户的服务。


Uber 有使用 Zig 语言吗


可以说有,也可以说没有。例如,ermet_cc_toolchain 中的启动器是我们用 Zig 编写的。嵌入到可执行文件中的运行时库(compiler-rt)是用 Zig 编写的。总而言之,我们的大多数 Go 服务都涉及到了一点 Zig,并且是用 Zig 编写的工具链编译的。


尽管如此,我们还没有将用 Zig 编写的生产应用程序引入到我们的代码库中(虽然工具链已经完全设置好了),因为目前公司中只有少数人知道这门语言。


总  结


截止 2023 年 1 月 16 日,所有发布到生产环境的 C/C++ 代码都通过 hermect_cc_toolchain 进行编译。因为 Zig 现在是我们 Go 代码库的关键组成部分,因此 hermetic_cc_toolchain 的维护得到了财务(与 Zig 软件基金会的合作将到 2024 年底)和 Uber 员工工时的支持。


虽然可以在 arm64 硬件上运行我们的核心基础设施,但我们还没有准备好运行面向客户的应用程序。我们的下一步是在 arm64 上试验面向客户的应用程序,这样就可以测试它的性能并决定未来的方向。


原文链接:


https://www.uber.com/en-SG/blog/bootstrapping-ubers-infrastructure-on-arm64-with-zig


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


今日好文推荐


十七年来奇葩大崩溃!为不让OpenAI和谷歌白拿数据,Reddit 收取巨额API 费用还诽谤开发者,社区爆发大规模抗议


“偷”代码建起公司、学历造假、6天拿下1亿美元却拖欠工资,这位AI独角兽CEO屡遭质疑后亲自回应了


市值暴涨10519%,原来全世界搞大模型的企业都在给这位华人打工!


2023-07-04 16:052973

评论

发布
暂无评论
发现更多内容

深入理解 Netty FastThreadLocal

vivo互联网技术

性能优化 Netty ThreadLocal 内存泄漏 FastThreadLocal

研发日常踩坑-Mysql分页数据重复 | 京东云技术团队

京东科技开发者

MySQL 数据库 分页 企业号10月PK榜

即时通讯音视频开发(二十):一文读懂视频的颜色模型转换和色域转换

JackJiang

网络编程 即时通讯 IM

cpu温度监测推荐 Turbo Boost Switcher Pro激活最新版

胖墩儿不胖y

Mac软件 温度监测工具

腾讯云入选2023 Gartner分布式混合基础设施魔力象限

Geek_2d6073

产品需求交付质量保证的“七重门” | 京东云技术团队

京东科技开发者

测试 交付质量 企业号10月PK榜

管控变更对提升质量的重要性

老张

质量保障 配置管理

Mac电脑高效音频录制 Piezo 最新 for mac

mac大玩家j

Mac软件 音频录制软件 录音软件

不会写代码同学的福音——AI 代码生成器 Amazon CodeWhisperer(通过注释写代码)

亚马逊云科技 (Amazon Web Services)

人工智能 CodeWhisperer Amazon Lambda 云上探索实验室

DAPP智能合约双币质押挖矿项目系统开发

l8l259l3365

万字长文:拆解银行数智运营之困!

京东科技开发者

人工智能 数字化转型 金融 企业号10月PK榜

C4D 2024插件:Arnold for mac(C4D S2024阿诺德渲染器) v4.6.6.1完美激活版

mac

苹果mac Windows软件 Arnold for Cinema 4D C4D R24插件

选择香港服务器发展线上业务的未来趋势:技术与市场的变化

一只扑棱蛾子

香港服务器

ABAQUS二次开发怎样接入Python代码?ABAQUS软件教程

思茂信息

abaqus abaqus软件 abaqus有限元仿真

中国水泥行业数字化采购:驱动产业链供应链现代化的关键

用友BIP

数智采购

用大模型Prompt解决行业问题

百度开发者中心

大模型训练 Prompt

HarmonyOS音频开发指导:使用AVPlayer开发音频播放功能

HarmonyOS开发者

HarmonyOS

即刻报名,企业服务与新经济论坛亮点提前揭秘!

SelectDB

数据库 大数据 数据仓库 实时数仓 apache doris

正确选择数据库安全运维平台的几个原则-行云管家

行云管家

数据库 数据安全 数据库安全 安全运维

IBM只有29%的职位看学历?基于技能的招聘到底是什么?

用友BIP

智能招聘

Loopback for Mac(音频路由和虚拟音频设备软件) v2.3.2直装激活版

mac

苹果mac Windows软件 Loopback 虚拟音频软件

和鲸ModelWhale与中科可控X系列异构加速服务器完成适配认证,搭载海光芯片,构筑AI算力底座

ModelWhale

gpu 服务器 信创 算力 数据科学

对话在行人|九州通:携手用友打造招聘共享中心实现招聘数智化

用友BIP

2023全球商业创新大会 对话在行人

哪家堡垒机支持国密算法?有哪些功能?

行云管家

运维 堡垒机 安全运维 国密浏览器 国密算法

高性能计算与多模态处理的探索之旅:英伟达GH200性能优化与GPT-4V的算力加速未来

GPU算力

云计算进入 AI 原生时代

Baidu AICLOUD

大模型 RDMA AI 原生云

峰会倒计时 3 天!互联网与文娱论坛演讲亮点预告!

SelectDB

数据库 大数据 数据仓库 实时数仓 apache doris

瑞技伙伴 | ZPE 带外管理方案,稳固您的IT世界

Bytebridge

数据中心 带外管理方案 ZPE

梦幻西游手游详细图文架设教程

echeverra

梦幻西游

在Vue中使用Mock.js虚拟接口数据实例详解

树上有只程序猿

Vue Mock.js

社区团购,拯救消费降级的利器

用友BIP

社区团购

使用Zig在arm64上引导Uber的基础设施_编程语言_Uber 工程播客_InfoQ精选文章