侧边栏壁纸
博主头像
汪洋

即使慢,驰而不息,纵会落后,纵会失败,但一定可以达到他所向的目标。 - 鲁迅

  • 累计撰写 204 篇文章
  • 累计创建 79 个标签
  • 累计收到 128 条评论

Kubernetes: The Road to 1.0

汪洋
2024-07-22 / 0 评论 / 0 点赞 / 99 阅读 / 5,550 字

原文:Brian Grant Kubernetes: The Road to 1.0

Kubernetes:通往 1.0 的道路

在昨晚举行的 Kubernetes 1.0 庆典上,我讲述了 Kubernetes 1.0 的发展历程。凯尔西说我有 30 张幻灯片并不是开玩笑,不过这一方面是因为我不确定听众想听什么,另一方面是作为我的笔记。

我已经写过关于设计背景的部分内容,但这篇文章更多的是从 Borg 团队所在的意图出发,讲述设计的来龙去脉和构建过程。克雷格-麦克卢奇(Craig McLuckie)、乔-贝达(Joe Beda)、布兰登-伯恩斯(Brendan Burns)和维尔-艾卡斯(Ville Aikas)在西雅图,那里是谷歌计算引擎团队的驻地。我把时间大致分成 4 个学期,以及项目开始前的一段时间。

Borg 和 Omega 的经验教训:2009-2013 年

我于 2009 年初加入谷歌的 Borg 控制平面团队,距今已有 15 年。在此之前的 15 年多时间里,我一直在超级计算机领域工作,但 Borg 是一个多用户系统,在其上层、下层和邻近层都有许多其他组件。我的 "入门项目 "是通过并发处理请求来提高可扩展性,因为在此之前的一年半时间里,我一直致力于推动谷歌的许多单线程 C++ 应用程序向多线程迁移,项目涉及 Linux(NPTL 尚未推出)、g++(线程安全注释)、线程原语(C++11 之前)、多线程 HTTP 服务器、改进剖析、文档和其他项目。

为了提高性能,我不仅需要了解系统的实现,还必须弄清楚系统是如何被使用的。在开发 Borg 的第一年里,我发现 Borg 的控制平面架构和应用程序接口在很多方面并不是为其使用方式而设计的。

例如,Borg 并不具备真正的可扩展性,因此像滚动、批量调度、cron 调度以及横向和纵向自动扩展等附加功能都必须在其他服务和客户端中构建。这些其他服务会将其数据嵌入到作业资源中,并持续轮询变化(如新作业),这占了 Borg 控制平面所有 API 请求的 99% 以上。通过 Watch API 订阅变化的功能仅支持作业任务端点,具体方法是将动态调度的主机 IP 地址和动态分配的主机端口写入 Chubby(Zookeeper 的灵感来源--键/值存储)。

1721637913376.png

另外,使用 Chubby 进行服务发现对在 Borg 上运行的工作负载产生了普遍影响,因为它们无法使用标准机制进行服务命名、发现、负载平衡、反向代理、身份验证等。我们希望现有的应用程序能够在 Kubernetes 上运行,因此我们让动态分配的 Pod IP 地址可以路由,这在当时是一个有争议的决定。

我在 2010 年启动了一个名为 Omega 的研发项目,针对 Borg 的使用方式对其进行重新设计,并为围绕 Borg 的生态系统提供更好的支持。在很多方面,Kubernetes 更像是 "开源的 Omega",而不是 "开源的 Borg",但它得益于从 Borg 和 Omega 身上吸取的经验教训。

Omega 有一个基于 Paxos 的键/值存储,其中心是 Watch API。这些组件(在 Kubernetes 中称为控制器)以异步方式运行,监视所需的状态对象并写回观察到的状态。与 Kubernetes 不同的是,这些都是存储中的独立记录,这对乐观并发性很有好处,但要拼接起来却有点困难。我们也从来没有用统一的 API 对存储进行过包装,尽管曾有过这样的提议。

1721637966916.png

另一个说明 Borg 无法按设计方式使用的例子是,Borg 中的 Allocs 是跨机器、集群水平切片计划的资源预订集合。工作任务可以被安排到这些时段中。这是一个相当复杂的模型,使得调试和水平自动扩展等许多事情变得更加复杂,很少有用户会利用它。Allocs 的大多数用户都将特定的工作任务集固定在实例中。这就产生了让这些容器捆绑成为 Omega 中复制和调度的一流单元的想法,这种单元被称为调度单元(Scheduling Units),最终在 Kubernetes 中被命名为 Pod。

1721638011703.png

我们将标签作为 Kubernetes 的核心概念。Borg 最初并没有标签。这个想法的灵感来自于用户试图将作业的元数据打包到长达 180 个字符的作业名称中,然后用正则表达式进行解析。Omega 中的相应概念更为复杂,但并不需要额外的子结构。一个简单的地图就足够了。同样,Annotations 的灵感也来自于 Borg 客户端,他们试图将信息压缩到一个 "notes "字符串中,这个字符串有点像 User-Agent(我们在 Google 的 RPC 库中并没有这个字符串),但会被持久化到 Jobs 中。

1721638028642.png

Kubernetes 中的 CPU 和内存请求与限制规范比 Borg 更一致,比 Omega 更简化。

我们能够挑选可行的方法,放弃不可行的方法,简单处理一些过于复杂的方法,并对一些方法进行多次迭代。欧米茄(Omega)中的一些概念,如调度单元(Scheduling Units),在Kubernetes中被直接重用。有些概念,如 "污点"(Taints)和 "阈值"(Tolerations),则进行了简化,但名称与欧米茄中的相同。索赔 "一词也来自于 Omega。中断预算的概念来自 Omega,灵感来自 Borg 的中断代理服务。

这 10 年的经验教训让 Kubernetes 比 libswarm 等使用 Docker 从头开始的项目更胜一筹。这也使得 Kubernetes 在早期就比原来更加复杂,但大多数功能都被大量使用。

早期 Container 应用程序接口设计:2013 年下半年

从 Borg 和 Omega 获得的所有经验让我们很快就开始了工作。2013 年下半年,当我们开始讨论构建什么样的容器产品时,我开始勾画 API 的草图。它已经有了今天 Kubernetes 用户所认可的形态。这是我当时在第一次原型演示的同一次会议上所做演示的摘要:

  • CRUD:配置和应用程序接口的模式相同
  • 调度单元(sunits,又称分子):资源、任务、数据的集合体
  • 新实例/更新实例的 sunit 原型
  • 单独的复制规范指定 # 所需的
  • 由标签、标签查询确定的潜在异构太阳子集;无索引
  • 正交特征解耦

1721638140096.png

1721638152764.png

发射前准备:2014 年上半年

虽然我们还没有获得开源的批准,我们还在讨论要开发什么样的产品,但在这段时间里,我们开始积极地开展项目。我们拉来了更多的人,实际上有不少。不幸的是,我已经无法查阅我的内部笔记,所以我可能无法在此一一列举他们的名字,但我还是要列举几个。

有些人,如 Tim Hockin、Dawn Chen 和 Eric Tune,从事独立的实验和项目。例如,我们也不知道在 Docker 上实现 Pod 的可行性有多大。在网络命名空间无法配置的情况下,多个容器如何共享一个 IP 地址并不明显。此外,也没有直接的方法来嵌套 cgroups。我们还探讨了是否可以调整现有组件,如 Omlet 节点代理和 lmctfy 容器运行时,但我们决定不这样做。

我们几个人去和 Docker 的 Solomon Hykes 和 Ben Golub 聊了聊在 Kubernetes 中嵌入 Docker 的问题,以及我们发现的一些挑战。这次会面促成了 libcontainer 与 Docker 的合作,以取代堆栈中的 LXC。Libcontainer 和与 Kubernetes 一起发布的 cadvisor 是由 Victor Marmol、Rohit Jnagal 和 Vish Kannan 共同开发的。

1721638227842.png

蒂姆还开发了 Python 容器代理,并于 2014 年 5 月发布,当时我们仍未获得开源 Kubernetes 的批准。这个项目中的容器清单被逐字移植到最初的 Kubernetes 任务 API v1beta1 中,这也是 Kubernetes 中 "清单 "一词的由来。

1721638241746.png

其他人,如 Ville Aikas 和 Daniel Smith,则负责 Go 代码。唯一的应用程序接口是 Task(后更名为 Pod)、ReplicationController 和 Service。没有节点。我最初使用 RAML 手工记录 API。

下图来自 Ville 的设计文档。请注意,这里没有 Kubelet,Kube-proxy 直接从 Etcd 读取数据。在我们发布 Kubernetes 之前,我们添加了一个最小的 Kubelet。Kubelet 也直接从 Etcd 读取数据,而 apiserver 会同步调用 Kubelet 来获取任务状态。

1721638263826.png

我们想在 Dockercon 上发布,所以我们发布了我们所拥有的东西(日期很有用),然后在公开场合进行了迭代。关键的想法已经有了:API、所需状态、多容器实例、标签、控制器、调度/放置、服务发现。我们进行了一些清理,并将代码复制到一个新的 repo 中。正如 Ville 在演讲中提到的,被复制到的 repo 仍然存在,但原来的 code.google.com repo 及其提交历史已经丢失。

完成设计的实施:2014 年下半年

我们发布的 Kubernetes 没有连贯的控制平面,API 不完整、不连贯,cloudcfgCLI 极其简陋,而且缺少用户需要的一些基本功能,因此在开源 Kubernetes 之后的 6-7 个月时间里,我们一直在充实这些领域,并吸收 Redhat 和社区中其他人的想法。

为了巩固控制平面,我们在 apiserver 中实施了 Watch。这让我们能够消除 Kubelet 和 Kube-proxy 中的直接 Etcd 访问。我们还消除了调度程序中的直接 Etcd 访问。

为了让 apiserver 不再需要调用 Kubelet(节点、pod)或其他组件(复制控制器)来检索状态信息,我们实施了 /status API 端点。

我们还将控制器管理器和调度器组件从 apiserver 中分离出来,并确保组件间通信(如 Kubelet->apiserver)的安全性。

应用程序接口本身也经历了许多变化。Task 更名为 Pod。添加了 Minion API,后来更名为 Node。调度程序改为在 Pod 字段中记录节点分配的方法。对服务 API 进行了全面修改,包括支持多个端口。我在 apiserver 中集成了 go-restful,以便为 API 生成 Swagger,因为手工跟上这些变化已经难以为继。在 API 版本之间进行转换,并添加了内部表示,以支持 API 版本管理。

克莱顿-科尔曼推动了对整个 API 表面的全面改造。元数据、期望状态(规格)和观察状态(状态)被分离开来,这就是我们今天所知的 Kubernetes API 的雏形。注释被添加进来。在资源路径中插入了命名空间。提高了资源类型和字段之间的一致性,我还编写了应用程序接口约定的初稿。我们的工作非常彻底,因此第一版应用程序接口只包含了很少的非向后兼容更改。

1721638893105.png

我们开源 Kubernetes 时的命令行工具叫 cloudcfg。我们很快将其更名为 kubecfg,但它的结构并不适合扩展。幸运的是,萨姆-高兹(Sam Ghods)自愿重写了 CLI,这就是后来的 kubectl。这时,spf13/cobra CLI 框架被整合进来,动词名词模式也得到了巩固。

我们还创建了 kubeconfig,开发了客户端库,实现了跨多个文件和资源类型的批量操作,并为声明式操作奠定了基础。

我们添加的功能有多个目的。有些功能是为了提高系统的可用性,如容器终止原因报告和通过 apiserver 获取日志的功能。有些功能是为了安全,如用户身份验证、服务账户、ABAC 授权和命名空间。还有一些是为了充实模型,如服务 IP 和 DNS 以及 PersistentVolume 和 PersistentVolumeClaim。还有一些是为了展示在当时拥挤的领域中的思想领导力,如有效性探针和就绪性探针。

最后冲刺 1H2015

2015 年初,我们开始讨论为 Kubernetes 和更广泛的云原生生态系统创建一个基础的想法。我们决定将 1.0 里程碑与 7 月份的发布活动日期保持一致。我们的目标是让系统为在生产中运行做好准备。

既然有了最后期限,我们就必须决定哪些功能要加入,哪些功能要取消。我们实施了项目的第一次代码冻结。我们甚至删除了一些不完整的代码。我们加入了一些我们认为对实际使用非常重要的功能,比如优雅终止和查看失败容器日志的功能。我们通过清理死容器、重启不健康组件和重复数据删除等更改,强化了系统的持续运行能力。

许多重要功能都被推迟到了 1.0 之后:kubectl apply、Deployment、DaemonSet、StatefulSet、Job、CronJob、ConfigMap、HorizontalPodAutoscaler、节点端口和 Ingress、kube-proxy 的 iptables、通过 apiserver 公开的资源指标、容器 QoS、大多数调度功能、Kubernetes 面板以及第三方资源。这绝对是 MVP 的正确选择。

我们还修复了 P0 bug,解决了未认证端口等安全问题,实施了升级测试,增加了更全面的 API 验证,并使用 Prometheus 客户端库对组件进行了可观察性检测。

最后几个月,我们创建了 kubernetes.io 网站。我们将一些现有文档转移到了那里,同时还编写了新的用户指南。网站在发布当天出现了故障,但我们及时解决了。主页上仍然有我当时写的一些文字,比如 "生产级容器编排 "的标语,"Kubernetes 是一个开源系统,用于自动化部署、扩展和管理容器化应用",以及一些功能描述,尽管有些功能即使在 1.0 版本时也很理想。

数十人团结在里程碑的背后,以各种方式提供帮助,从查找和修正文档错误,到组织活动,再到宣传项目,还有很多事情我可能在这么多年后都忘了。

在这一点上,该项目已经做了大量工作。这是在首次发布后的一年左右,但从一开始就做了一年半以上的工作,之前还做了多年的研发工作。这些工作对项目的成功起到了一定的作用。

0

评论区