图书前言

译  者  序

近年来,随着云计算和容器技术的发展,应用系统的部署方式正在发生深刻变化。容器编排系统已经成为现代软件基础设施的重要组成部分。 Kubernetes 、Mesos 、Nomad 等系统广泛应用于互联网企业和云平台,但对很多开发者而言,这些系统往往规模庞大、结构复杂,理解其内部原理并非易事。

《从零构建编排器(Go语言)》一书提供了一条极具启发性的学习路径。作者没有从现有平台的使用方法入手,而是通过构建一个简化的编排系统,让读者逐步理解编排系统的核心机制。全书围绕一个名为 Cube 的示例系统展开,通过清晰的步骤实现任务调度、节点管理、任务运行及系统接口等关键功能,使读者能够从整体上理解编排系统的基本结构和工作方式。书中使用较为精简的代码实现这些功能,使复杂系统的核心思想得以直观呈现。

本书不仅介绍了编排系统的基本概念,更重要的是展示了现代基础设施软件的设计思路。通过构建完整的系统原型,读者可以理解任务、调度器、管理节点和工作节点之间的协作关系,以及系统状态管理和资源调度的基本逻辑。这种从原理出发的学习方式,对于理解云原生技术体系具有重要价值。

作为一名从事计算机教学二十余年的教师,我长期关注计算机系统与软件工程教育的发展。近年来,我的研究方向逐渐扩展到通用人工智能以及人工智能在医学领域的应用。在这些领域,大规模计算资源的管理和任务调度同样至关重要,而容器编排技术正是支撑这些系统运行的重要基础设施。因此,理解编排系统的基本原理,对于从事现代计算平台和智能系统研究的读者具有现实意义。

本书结构清晰,实践性强,适合具有一定编程基础并希望深入理解云原生基础设施的读者阅读。通过动手实现一个简化的编排系统,读者不仅可以理解相关技术原理,也能够体会复杂系统设计中的工程思维。希望本书能够帮助读者从更底层的角度理解现代软件基础设施的工作机制,并从学习和实践中获得启发。

最后,谨向清华大学出版社王军老师表示衷心感谢。多年来,他的支持与帮助使我得以翻译并出版多部机器学习、人工智能、云计算和高性能计算领域的著作,也让我有机会通过翻译这一方式与读者分享知识。

殷海英

埃尔赛贡多市,加利福尼亚州

2026年3月

致珍妮弗:

即便在我对自己失去信心时,你也始终对我满怀信任。

自        序

2007年,我加入谷歌时第一次接触到了编排器。当时,我接触的并不是Borg,而是Ganeti 。Ganeti是谷歌内部开发的集群管理系统,在虚拟机上运行。那时,它其实只是一个基于Xen开源版本的简单封装工具,提供了一个集群化的解决方案,使我们能够为工程师提供虚拟机器,而不是物理机器。

我们当时并没有把Ganeti称为编排器,也没有像谈论Borg那样深入讨论它。回过头看,我认为将Ganeti看作一种编排器其实并不为过。它不是操作任务(以容器形式),而是操作虚拟机。在谷歌内部,Ganeti起到了桥梁的作用,它帮助团队从部分工程师在物理机上运行应用的阶段,过渡到每位工程师都能在 Borg 上部署并运行应用的新阶段。

几年后,当我们重写了用于管理Ganeti集群和虚拟机的生命周期管理系统时,我才真正接触到Borg。我们将系统运行在Borg上。转眼到了2020年,新冠疫情暴发,像其他人一样,我开始居家办公。突然,我每天多出了三个多小时的工作时间,因为不再需要通勤到曼哈顿的办公室。那么,我该用这些时间做些什么呢?

显然,最直接的选择是启动一个个人项目。但做什么项目呢?鉴于已与编排器打了13年交道,我觉得从零开始写一个编排器应该是挺有趣的。这会有多难呢?

2020年夏天,我花了大量时间开发我的编排器。我把它命名为Cube,沿用《星际迷航》的主题。出乎意料的是,仅用了不到3000行代码,就让它成功运行了。

就在那时,我读了Thorsten Ball的《用Go语言编写解释器》 (Writing an Interpreter in Go)。虽然我并不特别对解释器或编程语言感兴趣,但我想了解它们是如何工作的。然后,我灵感一闪!我可以写一本关于用Go语言编写编排器的书。这本书就是我2007年时所希望拥有的那本书!于是,这本书应运而生。

在写作过程中,我很快意识到,编排是一个庞大的话题。在谈论编排系统时,很容易被次要问题分心。比如,如何处理服务发现?如何处理DNS?如何处理共识机制?我希望能避免所有这些附加的内容,专注于编排系统的核心——支撑所有其他功能的基础。并非说服务发现、DNS和负载均衡等内容不重要,但在编排系统的背景下,我们讨论这些问题是因为它们是为实现编排器的核心功能服务的:将应用调度到一组节点上运行,并管理它们的生命周期。

简而言之,本书的主要内容就是接收用户提交的运行应用的请求,找出一台能够运行该应用的机器,然后向该机器发送请求,启动应用。听起来很简单,是不是?

除了介绍编排系统的基础概念,我写这本书的另一个目标是让它惠及更广泛的读者群体。所以,虽然我选择用Go语言编写Cube编排器,但我希望即使你从未写过Go代码,也能跟随本书一步步操作并使代码运行起来。本书中的代码使用的是Go的基本特性,其中使用了goroutine(Go语言的轻量级线程)来处理一些并发操作,但并没有使用通道(如果你感兴趣,有很多资源可供学习)。此外,本书也没有使用泛型(在Go 1.18发布后,我尝试过将代码重构,加入泛型。虽然最终让代码成功运行,但发现这为书籍增加了不必要的复杂性,反而成了一个需要解释的新内容,让书的内容变得冗杂)。

我希望你在阅读这本书时能感到愉悦,像我写作时一样,在享受过程的同时从中学到很多知识。

致        谢

生活中的许多事情都离不开他人相助,这本书的创作也不例外。

首先,我要感谢曼宁出版社的所有工作人员,有了他们的帮助,这本书才得以问世。特别感谢Andy Waldron,他接手了我的书稿,并在本书多次修改、完善的过程中始终支持我,给予我信心。Katie Sposato Johnson在本书的出版过程中发挥了重要作用,没有她的帮助,这本书恐难出版。我还要感谢曼宁出版社的其他团队成员,他们在书籍的制作和营销过程中提供了帮助。

我还要感谢在不同阶段阅读手稿并提供宝贵反馈意见的审稿人: Alain  Lompo 、 Alessandro Campeis 、Andres Sacco 、Becky Huett 、Bobby Lin 、Christopher Villanueva 、 Clifford Thurber 、David Paccoud 、Emanuele Piccinelli 、Ernesto Bossi 、Fernando Bernardino 、 Fernando Rodrigues 、Geert Van Laethem 、Gregory Reshetniak 、Katia Patkin 、Kosmas Chatzimichalis 、Larry Cai 、Lucian Enache 、Madiha Khalid 、Matthias Busch 、Michael Bright 、 Muneeb Shaikh 、Nathan B. Crocker 、Nghia To 、Richard Vaughan 、Sanket Naik 、Simone Sguazza 、Thomas Dybdahl 、Timothy R. J. Langford 、Tim van Deurzen 和 Vamsi Krishna。

特别感谢技术校对员Mike Haller,他在书籍出版前仔细审查了代码。保持书中的代码与源代码一致是一项极具挑战的任务; Mike帮助我整理和修正了代码中的不一致之处,发挥了不可估量的作用。

前        言

本书旨在帮助你更好地理解编排系统的基本组成部分。无论你是DevOps工程师、站点可靠性工程师(SRE)还是软件工程师,今天的许多技术看起来都像一个“闷葫芦”:你只需要将应用部署到云端,神奇的事情就会发生。大家都知道,当这些神奇技术正常工作时,体验确实很棒!但当它出现故障时—而且它肯定会出现故障—这种“神秘感”就可能成为我们快速识别并解决问题的障碍。随着越来越多的开发者将应用迁移到云端,他们将在编排系统上运行这些应用(或者很快会这样做)。除非他们在配备专门DevOps或SRE团队的大公司工作,否则他们很可能需要亲自部署和管理应用,其中也包括处理运行时出现的问题。本书的目标是帮助你驱散应用在编排器上运行时的神秘感。

目标读者

本书既适合那些负责部署和维护编排系统的人员(比如DevOps工程师和SRE),也适合那些负责在编排系统上部署和管理应用程序的开发人员(比如软件工程师)。如果你想了解编排器是如何工作的,可以阅读Kubernetes或Nomad的源代码,这两个开源项目都可以在GitHub上找到。Kubernetes的代码超过200万行,全部用Go语言编写;而Nomad的代码量虽然少一些,但也有50万行以上。说实话,我自己也不太理解如何从50万行代码中获得有效信息,更不用说200万行代码了。

本书结构

本书分为5部分,共13章。第Ⅰ部分介绍了Cube编排器的思维模型,并搭建了一个贯穿全书实现过程的基础代码框架。

●    第 1 章简要说明了编排系统的用途,并介绍了Cube 编排器的思维模型,Cube 是本书将要实现的编排器。

●    第 2 章基于第 1 章中的思维模型,为 Cube 编排器的核心概念构建代码框架。

●    第 3 章通过详细实现 Task 对象的代码,展示了如何在这个基础框架上扩展和完善功能。

第Ⅱ部分实现了工作节点组件所需的概念。

●    第 4 章详细介绍了Worker 对象的实现过程。

●    第 5 章为 Worker 对象构建了 API。

●    第 6 章设计了一个框架,使工作节点能够展示自身状态和所运行任务状态的监控数据。第Ⅲ部分实现了管理器组件所需的概念。

●    第 7 章详细介绍了 Manager 对象的实现过程。

●    第 8 章为 Manager 对象构建了 API。

●    第 9 章探讨了故障场景,并实现了相关的解决方案。

第Ⅳ部分指导读者重构初期实现的两个组件。

●    第 10 章介绍了调度器接口,并实现了一种更复杂的调度算法。

●    第 11 章设计并构建了存储接口,使得管理器和工作节点组件能够将任务数据保存在内存中或持久化存储到数据库中。

第Ⅴ部分实现了一个命令行接口(CLI),方便读者操作编排器。

●    第 12 章构建了一个 CLI,支持启动管理器和工作节点、启动和停止任务,并查询系统中任务的状态。

●    第 13 章总结了本书的内容,并提供了一些后续的学习建议。关于代码

本书包含许多源代码示例,这些示例既以带编号的代码清单的形式呈现,也会穿插在正文段落中。无论哪种形式,源代码都使用等宽字体格式,以便与普通文本区分开来。有时,代码也会以粗体显示,用来强调与本章前文步骤中的代码有所不同的部分,例如添加新功能到现有代码行时。

在许多情况下,本书对原始源代码格式进行了调整,增加了换行符,并调整了缩进,以适应本书页面的排版空间。在极少数情况下,即使这样调整仍无法满足排版需求,代码清单中还会加入行连接符()。此外,当代码内容已在正文中有详细说明时,源代码中的注释通常会被省略。许多代码清单还配有注释,帮助强调重要概念。

书中所有示例的完整代码可以从GitHub上获取,网址如下: https://github. com/buildorchestratoringo/code。也可以扫右侧二维码获取。

关于本书封面

本书封面上的插图名为《巴什基尔女人》(Femme Baschkirienne),选自雅克 · 格拉塞 ·德 ·圣索韦尔(Jacques Grasset de Saint-Sauveur)于1788年出版的作品集。这幅插图经过了精细的手工绘制和上色处理。

在那个时代,人们仅凭衣着就能轻易分辨出一个人的居住地、职业或社会地位。曼宁出版社通过这种封面设计,展示了几百年前地区文化的丰富多元性,并通过这些珍贵藏品插图让昔日的文化风貌重焕生机,彰显了计算机行业的创造力与开创精神。

作  者  简  介

蒂姆 · 博林是一位拥有20多年行业经验的软件工程师。在其职业生涯的大部分时间里,他一直是编排系统的实际使用者,所接触的系统包括Borg 、Kubernetes和Nomad等。