Kubernetes:从理念到1.0的历程
2024/12/20 2:03:04
本文主要是介绍Kubernetes:从理念到1.0的历程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
昨晚我在一个离项目开始时我坐的地方几分钟路程的地方跨过了一条小溪进行了演讲。但是10分钟实在太短了,我只能大致介绍一下。Kelsey 没有骗我,我确实准备了30张幻灯片,虽然我不确定观众想听到什么内容,部分也是为了我自己做笔记。
我之前写过一些关于该设计背景的内容,但这更多是关于它是如何形成的,以及当时 Borg 团队在山景城那边工作,我们这边的开发过程。Craig McLuckie、Joe Beda、Brendan Burns 和 Ville Aikas 则在西雅图的 Google Compute Engine 团队工作。我大致将其分为四个阶段,加上项目开始前的一段时间。
2009年初,我加入了Google的Borg 控制平台团队,这已经是15年前的事了。在那之前,我曾从事过超过15年的超级计算机相关工作,但Borg是一个多用户的系统,围绕着它有许多其他组件。我的“首个项目”是通过处理并发请求来提高系统的扩展性,因为在之前的一年半时间里,我一直在致力于将Google的许多单线程C++应用程序转换为多线程。这些项目包括了Linux(NPTL尚未推出)、g++(线程安全注解)、C++11之前的线程原语、多线程HTTP服务器、改进的性能分析、文档以及其他相关工作。
为了提升性能,我不仅要理解它的实现,还要了解它如何被使用。在第一年负责Borg的工作期间,我发现Borg的控制平面架构和API在很多方面实际上并没有按实际使用情况设计。
例如,Borg 并不是真正可扩展的,因此像部署、批量调度、cron 调度、水平和垂直扩展等附加功能必须在其他服务和客户端中实现这些功能。这些其他服务会将它们的数据嵌入 Job 资源中,并持续轮询 Job 的更改,例如新的 Job,这占了超过 99% 的所有发送到 Borg 控制平面的 API 请求。通过 Watch API 订阅更改的功能仅支持 Job Task 端点,即通过将动态调度的主机 IP 地址和动态分配的主机端口写入 Chubby(一个启发了 ZooKeeper 的键值存储)来实现。
博尔格分配系统中的作业任务和分配的端口
顺便说一下,Chubby 在服务发现中的使用对运行在 Borg 上的工作负载产生了重大影响,因为它们无法利用标准机制进行服务命名、发现、负载均衡、反向代理、认证等工作。我们希望现有的应用程序能够顺利迁移到 Kubernetes,因此我们使动态分配的 Pod IP 地址可路由,这在当时是一个有争议的决定。
我从2010年开始了一个名为Omega的研发项目,旨在对Borg进行重新设计,使其更适合实际应用场景,并更好地支持Borg周围的生态系统。在很多方面,Kubernetes更像是“开源的Omega”,而不是“开源的Borg”,但它从Borg和Omega中汲取了许多经验。
Omega有一个基于Paxos的键值存储,此存储的核心功能是一个Watch API。这些组件在Kubernetes中被称为控制器,它们异步地监控所需的状态对象,并写回观察到的状态信息。与Kubernetes不同,这些是存储中的独立记录,这有利于乐观并发,但稍微难于拼接在一起使用。我们也没有来得及为这个存储添加一个统一的API包装层,尽管曾经有一个提议这样做。
Omega 枢纽和辐射架构
另一个 Borg 并未按设计方式使用的一个例子是,Borg 中的 Allocs 是跨机器调度的资源预留集合,即集群的水平分割。作业任务可以调度到这些槽中。这是一套相当复杂的模型,使诸如调试和水平自动扩展等许多事情变得更复杂,而很少有用户实际利用这些特性。大多数使用 Allocs 的用户将特定的作业任务绑定到实例上。这导致了在 Omega 中将这些容器捆绑作为调度的一等单位的想法,这些单位最终在 Kubernetes 中被称为 Pods。
安排单位
我们在Kubernetes中将Labels 设为一个核心概念,使之成为系统的核心部分。这一想法源于用户尝试将长达180个字符的任务元数据打包进任务名称中,然后通过正则表达式进行解析。Omega中的对应概念更复杂,但额外的底层结构并不必要,一个简单的映射就足够了。同样,Annotations 是受到Borg客户端尝试将信息塞进一个单一的notes
字符串的启发,这有点像User-Agent(当时我们Google的RPC库中并没有这个功能),不过这个notes
字符串会在任务中被持久化。
标签的由来:(仅显示标签值)
Kubernetes 中的 CPU 和内存请求和限制设置 比 Borg 更一致而言,并且相比之下,比 Omega 更简化。
我们能够挑选出有效的方法,摒弃无效的方法,简化了一些过于复杂的内容,并对一些方法进行了多次迭代。Omega 中的一些概念,如调度单元等,在 Kubernetes 中被直接复用。有些概念,比如Taints和Tolerations,被简化了,但名称保持了与 Omega 中的一致。术语“claim”也源自 Omega。破坏预算(disruption budgets)的概念同样来自 Omega,灵感源自 Borg 中的破坏代理服务。
这10年的积累的经验和教训使Kubernetes在像libswarm这样的项目刚开始接触Docker时就领先一步。这也使得Kubernetes比预计的更早变得复杂,但这些功能被广泛使用。
所有来自 Borg 和 Omega 的经验使我们迅速进入状态。在 2013 年下半年,当我们开始讨论要构建什么样的容器产品时,我开始勾画 API 的轮廓。它已经具备了一个当今 Kubernetes 用户熟悉的界面。这是我在那次会议上首次演示原型时所做的演讲摘要。
- CRUD:配置和API使用相同的架构
- 调度单元(简称sunits或分子):资源、任务和数据的集合
- 新创建或更新实例的sunit原型
- 单独的复制规范指定了所需的实例数量
- 通过标签和标签查询标识的不同类型的sunit集合,无索引
- 正交特性相互独立
调度单元(Pod,用于在Kubernetes集群中组织一组容器的资源管理单元)简要介绍
将多种不同类型的资源连接在一起
尽管我们还没有获批开放源代码,而且还在讨论要构建什么样的产品,但我们在这个时期还是积极地开始了项目。我们很快就招了不少人手。可惜,我目前无法访问过去的笔记,所以不能一一列出,但可以提几个名字。
有些人,比如Tim Hockin,Dawn Chen,和Eric Tune,参与了独立的实验和项目。例如,我们也不知道将Pods部署在Docker上的可行性如何。不清楚多个容器如何在不配置网络命名空间的前提下共享IP地址。另外,也没有直接的方式去嵌套cgroups。我们还探索了是否可以利用现有的组件,如Omlet节点代理和lmctfy容器运行时,但最终还是放弃了这个思路。
我们中的一些人去和Docker的Solomon Hykes和Ben Golub聊了聊,讨论了将Docker嵌入Kubernetes的一些挑战和遇到的问题。这次会议促成了与Docker的libcontainer合作的开始,以替换堆栈中的LXC。这两个项目,libcontainer和cadvisor,后者与Kubernetes一起发布,是由[Victor Marmol]、[Rohit Jnagal]和[Vish Kannan]开发的。
Docker 中 libcontainer 的角色
Tim 还开发了 Python container-agent,该工具在 2014 年 5 月发布,那时我们还没拿到 Kubernetes 开源的许可。该项目的容器清单被原封不动地纳入了最初的 v1beta1 Kubernetes 任务 API,这也是 Kubernetes 中“清单”(manifest)这一术语的由来。
容器代理及其清单(容器清单文件)
其他人,如Ville Aikas和Daniel Smith,参与了Go代码的编写。当时唯一的API是针对任务(后来改名为Pod)、ReplicationController 和 Service。当时没有节点。我最初手工记录了API,使用了RAML。
以下是Ville的设计文档中摘取的一个图表。请注意,这里没有Kubelet,并且Kube-proxy直接从Etcd读取数据。在我们发布Kubernetes之前,增加了一个简化的Kubelet。Kubelet也直接从Etcd读取数据,并且apiserver还会同步调用Kubelet来获取任务状态。
早七设计 (Early Seven Design)
我们原本打算在Dockercon上发布,所以我们发布了我们当时拥有的东西(发布日期是很有帮助的),然后在公开场合进行了迭代。关键的想法都已经有了:一个API,期望的状态,多容器实例,标签,控制器,调度/放置,服务发现。进行了一些清理工作,代码被复制到了一个新的仓库。原仓库仍然存在,但原code.google.com仓库及其提交历史已经丢失,正如Ville在他的演讲中提到的。
我们发布的版本并没有统一的控制平面,API也不完整且不一致,cloudcfg
命令行工具极其简陋,并且缺少一些用户必需的基本功能,因此在接下来的6到7个月里,我们继续开源Kubernetes并完善了这些领域,吸纳了来自Redhat及其他社区成员的想法。
为了加强控制平面,我们在apiserver中引入了Watch功能。这使我们能够消除Kubelet和Kube-proxy中对Etcd的直接调用。我们还移除了调度器中的Etcd直接调用参见。
为了减少 apiserver 需要调用 Kubelet 或其他组件(例如节点、pod 或副本控制器)来获取状态信息的需求,我们实现了一个 /status API。
我们还将控制器管理器和调度程序组件从API服务器中分离,并[加强了组件之间的通信安全性],例如[从Kubelet到API服务器]。
API 本身也经历了许多变化。Task 被重命名为 Pod。添加了一个 Minion API,之后更名为 Node(详情请见)。调度器开始在 Pod 字段中记录节点分配(详情请见)。Service API 进行了全面改造,其中包括支持多个端口(详情请见)。为了生成 Swagger 描述 API,我将go-restful 集成到了 apiserver 中,因为手动保持这些变化已经变得不可持续。为了支持 API 版本控制,添加了在 API 版本和内部表示之间转换的功能。
克莱顿·科尔曼(Clayton Coleman)推动了一次全面的API重构。在这次重构中,我们今天所熟悉的Kubernetes API的形态逐渐成型,其中[元数据、期望状态(spec)和实际状态(status)]分离了出来(#1200)。注解也被添加了(#1201)。命名空间被引入到资源路径中(#2813)。资源类型和字段的一致性得到了提升,我还起草了第一个API规范草案。我们非常细致,以至于v1 API几乎没有引入任何向后不兼容的变更。
v1beta3 Pod:示例 Pod
当时的命令行工具叫做 cloudcfg
,但在我们开源 Kubernetes 时已经改变。我们很快就将其重命名为 kubecfg
,但它的结构不够扩展友好。幸运的是,Sam Ghods (山姆·戈德斯),自愿重写了 CLI,这之后就成了 kubectl
。此时,我们集成了 spf13/cobra 命令行框架,并且确立了动词-名词的模式。
我们也创建了kubeconfig,发布了一个客户端库,实现了跨多个文件和多种资源类型的批处理操作,并为声明性操作打下了基础。
我们添加的功能有多种目的。有些功能是为了让系统更易用,例如容器终止原因的报告和通过apiserver获取日志的能力。有些则是为了增强系统的安全性,例如用户认证、服务账号、ABAC授权等,并引入了命名空间。还有一些是为了完善模型设计,例如服务IP和域名解析(DNS)以及PersistentVolume和PersistentVolumeClaim。还有一些是为了在那个时期竞争激烈的市场中展示我们的技术领导地位,例如存活探针和就绪探针。
在2015年初,我们开始谈论创建Kubernetes基金会以及更广泛的云原生生态体系的构想。我们决定将1.0里程碑与7月的发布活动日期同步,目标是使系统能够在生产环境中运行。
现在我们有了一个截止日期,我们必须决定哪些功能要包含进来,哪些要推迟。我们实施了项目的第一个代码冻结期。我们甚至删除了一些仍未完成的代码。我们加入了我们认为对实际使用至关重要的功能,比如优雅终止和查看失败容器的日志信息的能力。我们通过诸如清理死容器、重启不健康的组件和事件去重等改进,增强了系统的连续运行能力。
许多重要的功能都被推迟到1.0之后才发布:kubectl apply, Deployment, DaemonSet, StatefulSet, Job, CronJob, ConfigMap,HorizontalPodAutoscaler,节点端口(node ports),Ingress,kube-proxy的iptables,通过apiserver暴露的资源指标信息,容器QoS(服务质量),大多数调度功能,Kubernetes仪表板,以及第三方资源。对于最小可行性产品(MVP),这样的决定绝对是正确的。
我们也修复了P0级别的错误,解决了包括未认证端口在内的安全问题,进行了升级测试,增加了API验证,并使用Prometheus的客户端库instrumented组件来增强可观测性。
在最后几个月里,我们创建了 kubernetes.io 网站,并将一些现有的文档迁移到那里。同时我们还撰写了一份新的用户指南。在宣布网站的那天,网站出现了一些问题,但我们及时解决了这些问题。首页仍然保留了我当时写的一些内容,例如“生产级容器编排”的标语,“Kubernetes 是一个开源系统,用于自动化部署、扩展和管理容器化应用”,以及一些功能描述,尽管其中一些当时还是展望性的。
几十个人在这一里程碑的背后提供了各种帮助,从查找并修复文档中的错误,到组织活动的安排,到推广项目,以及很多我可能已经忘记的事情。
这时,项目已经投入了大量工作。距首次发布大约一年,但从开始算起,工作时间已超过一年半,之前的研发工作更是延续了数年。这些努力对它的成功起到了关键作用。
这篇关于Kubernetes:从理念到1.0的历程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-21Kubernetes生产环境问题排查指南:实战教程
- 2024-12-20使用Encore.ts构建和部署TypeScript微服务到Kubernetes集群
- 2024-12-18第28天:Kubernetes中的蓝绿部署讲解
- 2024-12-15从零到Kubernetes安全大师:简化集群安全防护
- 2024-12-15掌握Kubernetes节点调度:污点、容忍、节点选择器和节点亲和性
- 2024-12-14第五天:与容器互动
- 2024-12-11CKA(Kubernetes管理员认证)速查表
- 2024-12-08.NET Aspire应用部署到Azure和Kubernetes实战指南
- 2024-12-07云原生周报:K8s未来三大发展方向不容错过
- 2024-12-07《 Kubernetes开发者的书评:从入门到生产实战》