Kubernetes 系列从设计理念说起
Posted 范桂飓
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kubernetes 系列从设计理念说起相关的知识,希望对你有一定的参考价值。
目录
文章目录
- 目录
- Kubernetes 的发展历史
- Kubernetes 的电梯间演讲
- Kubernetes 的核心理念
- 云基础设施的两个关键
- 定义强大的控制平面
- 一切皆在 API 中
- Kubernetes API Server 将成为软件的通用控制平面
Kubernetes 的发展历史
Kubernetes,名词源于希腊语,意为 “舵手”。Google 在 2014 年开源了 Kubernetes 项目,建立在 Google 在大规模运行生产工作负载方面拥有十几年的经验的基础上,结合了社区中最好的想法和实践。
2003-2004 年,Google 发布了 Borg 系统,它最初是一个小规模项目,约有 3~4 人合作开发。而现在,Borg 是一个大规模的内部集群管理系统,它在数千个不同的应用程序中运行数十万个作业,跨越许多集群,每个集群拥有数万台计算机。
2013 年,Google 继 Borg 系统之后发布了 Omega 集群管理系统,这是一个适用于大型计算集群的灵活、可扩展的调度程序。
2014 年,Google 发布了 Kubernetes,其是作为 Borg 的开源版本发布的。同年,Microsoft、Red Hat、IBM、Docker 等加入 Kubernetes 社区。Google 之所以选择间接开源 Kubernetes 而不是直接开源 Borg 项目,其实背后的原因也比较容易理解:Borg/Omega 这样的系统太复杂了,是没办法提供给 Google 之外的人使用,但是 Borg/Omega 这样的设计思想却可以借助 Kubernetes 让大家接触到,这也是开源 Kubernetes 的重要背景。
2015 年,Google 在美国波特兰的 OSCON 2015 大会上宣布并正式发布 Kubernetes 1.0。Google 与 Linux 基金会合作组建了云原生计算基金会(CNCF)。CNCF 旨在为云原生软件构建可持续发展的生态系统,并围绕一系列高质量开源项目建立社区,整合这些开源技术来让编排容器成为微服务架构的一部分。CNCF 自创立以来已经拥有非常多的高质量项目,其中包括:Kubernetes、Prometheus、gRPC、CoreDNS 等。
2016 年,Kubernetes 成为主流。在 CloudNativeCon 2016 大会上,来自世界各地的约 1000 名贡献者和开发者齐聚一堂,交流有关 Fluentd、Kubernetes、Prometheus、OpenTracing 和其他云原生技术的内容。
2017 年,互联网巨头纷纷表示支持 Kubernetes。在这一年,Google 和 IBM 发布微服务框架 Istio,其提供了一种无缝连接、管理和保护不同微服务网格的方法。Amazon 宣布为 Kubernetes 提供弹性容器服务,用户可以在 AWS 上使用 Kubernetes 部署、管理和扩展容器化应用程序。同年年底,Kubernetes 1.9 发布。
2018 年,无人不知 Kubernetes。在 KubeCon + CloudNativeCon Europe 2018 峰会上,有超过 4300 名开发者聚集在一起讨论 Kubernetes 生态技术。同年,Kubernetes 1.10 发布。KubeCon 第一次在中国举办。
Kubernetes 的电梯间演讲
Kubernetes 一个用于容器集群的自动化部署、扩容以及运维的开源平台。通过 Kubernetes,你可以快速有效地响应用户需求;快速而有预期地部署你的应用;极速地扩展你的应用;无缝对接新应用功能;节省资源,优化硬件资源的使用。为容器编排管理提供了完整的开源方案。
Kubernetes 的优点包括:
- 敏捷应用开发和部署:与使用虚拟机镜像相比,Kubernetes 改进了容器镜像创建的便捷性和效率。
- 容器开发、集成和部署:支持高频次、可靠的容器镜像构建和部署,并由于镜像的不可更改性,使得回滚更为快速和简单。
- 有效隔离开发(Dev)和运维(Ops):应用容器镜像创建是在构建 / 发布阶段,而非部署阶段,使应用得以从架构中解耦。
- 可观察性(Observability):不仅涵盖操作系统层级的信息和度量,而且涉及应用健康等深层信息。
- 跨开发、测试和生产的环境一致性:无论对于云环境中,还是在个人笔记本上,都具有一致的运行环境。
- 云 / 操作系统发行版的可移植性:可运行在任何环境中,包括 Debian、Ubuntu、CoreOS、本地部署、Google Kubernetes Engine、Amazon Elastic Container Service 等。
- 以应用为中心的管理:将抽象层级从在虚拟硬件上运行操作系统,提升为使用逻辑资源在操作系统上运行应用。
- 松耦合、分布式、弹性、无约束的微服务:应用切分为可动态部署并管理的更小尺度独立部分,而非运行在专用机器上的单体应用。
- 资源隔离:支持应用性能可预测。
- 资源使用:更高效、更紧密。
为尽可能提供完备的功能,Kubernetes 依赖并使用了其它一些开源工具。其中包括:
- 注册:例如 Docker Registry。
- 网络:例如 Flannel、 Calico、 MetalLB、 CoreDNS 等。
- 遥测:例如 Prometheus、 Sysdig 和 Istio。
- 安全:例如 LDAP、 SELinux、 RBAC 和具有多层的 OAuth。
- 服务:对于过去常用应用模式创建的内容,支持以多种目录形式提供。
Kubernetes 的核心理念
Kubernetes 项目的核心定位是要做一个 “Platform for Platform” 的项目,即:“用来帮助使用者构建分布式系统的分布式系统”。这也意味着,Kubernetes 项目的首要目标用户其实是分布式应用的开发人员。这里的分布式应用可以是稍具规模的网站,也可以是 TiDB 这样复杂的数据库系统。
Kubernetes 项目的核心理念以及 “Platform for Platform“ 的定位催生出整个云原生的生态,是其社区迅猛增长的主要动力源泉。
云基础设施的两个关键
基础设施是可编程的
与早期的基础设施相比,云提供商需要从头开始构建完全由 API 调用驱动的计算、存储、网络资源。借助标准且稳定的 API,提供商和用户都可以在其上构建更高级别的资源,例如:无服务器计算。还可以创建基本原语,例如:调度程序和控制平面,通过可编程的代码来管理这些资源。
云计算的服务自始自终就被设计为极具弹性、自助服务和 API 驱动。在这样的模型中,用户不需要手动干预即可创建虚拟机和数据库。云计算将 IT 资产的虚拟化复杂性封装为服务,最终用户只需要进行函数调用即可。
像所有程序一样,一旦核心服务公开为 API,就可以通过重用功能来利用它们来创建更高级别的抽象。例如:AWS Fargate、Lambda 之类的产品是更高抽象级别的计算服务,但实际上它们最终都会在 EC2 虚拟机上运行。云平台的用户还可以利用相同的 API 来构建自己的抽象。例如:Netflix 建立了 Titus,以 EC2 实例作为基础来调度和运行容器的工作负载。APIs 经济支持为不同供应商提供不同的专业产品。
也就是说,将基础设施原语作为 API 提供的创新使云提供商可以构建更高级别的服务。这层抽象快速将云提供商与传统的商品托管提供商分开,因为传统托管在没有没有足够投资的情况下不能满足足够的灵活性。
基础设施具有声明性
为了规模化交付基础设施,云提供商需要保护用户免受内部工作流故障的影响。而声明式 API 正式一种面向结果的编程方式,可以将底层基础设施配置的复杂性淹没。
在规模化的云交付场景中,不能依靠过去传统 IT 面向过程(命令式)的运维思路。从规模上讲,对于工作流中遇到的每个故障,手动恢复都是不切实际且不经济的。所以,业界需要一种让用户定义软件基础结构的方法,而又不让他们暴露于内部系统可能会发生的大量故障中。最终的解决方案是采用声明式 API。使用声明式 API,用户告知云计算平台他们想看到什么结果,而不是如何实现的过程。对于用户来说,他们找到了一种更加直观地与云提供商合作的方式:描述部署的外观,而不是精确地部署它。
对于云提供商而言,必须要将管理软件和底层硬件的复杂性淹没在 API 背后。用户不必受限于完成工作流中的特定步骤,例如:基于命令式的 API 构建应用服务。如此的,云提供商就可以根据技术发展快速迭代实现工作流的各个部分,用户的声明式 API 又可以始终保持不变。
可见,声明式 API 之所以对云提供商在可用性方面具有吸引力的决定性因素就是:以这种方式设计基础架构可以减少与用户的摩擦。随着摩擦的减少,云提供商在就可以提供以更高级别的服务和诸如计算调度程序之类的原语形式出现的新抽象。
基础设施可以是不可变的
无侵入性
为了尽可能满足用户(工程师)的需求,减少工程师的工作量与任务并增强灵活性,Kubernetes 为工程师提供了无侵入式的接入方式,每一个应用或者服务一旦被打包成了容器镜像就可以直接在 Kubernetes 中无缝使用,不需要修改应用程序中的任何代码。这一点得益于容器技术的特征。
Container 和 Kubernetes 就像包裹在应用程序上的两层,它们两个为应用程序提供了容器化以及编排的能力,在应用程序内部却不需要任何的修改就能够在 Container 和 Kubernetes 集群中运行,这是 Kubernetes 在设计时选择无侵入带来最大的好处,同时无侵入的接入方式也是目前几乎所有应用程序或者服务都必须考虑的一点。
可移植性
在微服务架构中,我们往往都会让所有处理业务的服务变成无状态的服务,以前在内存中存储的数据、Session 等缓存,现在都会放到 Redis、etcd 等数据库中存储,微服务架构要求我们对业务进行拆分并划清服务之间的边界,所以有状态的服务往往会对架构的水平迁移带来障碍。
然而有状态的服务其实是无可避免的,我们将每一个基础服务或者业务服务都变成了一个个只负责计算的进程,但是仍然需要有其他的进程负责存储易失的缓存和持久的数据,Kubernetes 对这种有状态的服务也提供了比较好的支持。
PersistentVolume(持久化存储)和 PersistentVolumeClaim 的概念用来屏蔽底层存储的差异性,目前的 Kubernetes 支持下列类型的 PersistentVolume。这些不同的 PersistentVolume 会被开发者声明的 PersistentVolumeClaim 分配到不同的服务中,对于上层来讲,所有的服务都不需要接触 PersistentVolume,只需要直接使用 PersistentVolumeClaim 得到的卷就可以了。
定义强大的控制平面
数据面跟控制面分离的思想来源于 SDN 领域,Marc Brooker 进一步概括了这一概念:
- 数据平面组件直接位于请求路径上。组件需要成功运行并可以随着系统的请求数量线性扩展。
- 控制平面组件帮助数据平面完成其工作。包括:资源管理(添加、删除资源),容错(监控和纠正硬件/软件故障)和部署(随时间推移更改系统)。由于不需要控制平面来满足系统请求,因此控制平面组件可以中断一段时间而不影响数据平面。
通过控制平面来管理基础设施的理论依据来源于 “控制论” 这一个跨越多个行业的广阔领域:
- 声明所需状态:系统不是静态的,对外部和内部情况做出响应并反馈变化。稳定的系统需要向预定的状态迁移。
- 控制循环:循环持续观察数据平面组件,连续的控制回路和反馈机制可以不断纠正与期望状态的偏差。
那么,Kubernetes 如何应用控制平面原理呢?Kubernetes 在 Informer 和 Workqueues 之类的控制器负责控制循环的构建。
贯穿 Kubernetes 架构最常见主题是标准化的思想:Kubernetes 资源模型很大程度上依赖于资源结构的一致性,并严格限制运行访问数据的入口。可以在许多不同的抽象中使用相同的原语,这些原语还实现了云计算的核心原理:资源分层以创建更高级别的抽象(例如:Deployments 抽象 Pods)。
因此,通用控制平面更多地取决于 Kubernetes API 的设计,而不是容器编排。
一切皆在 API 中
Kelsey Hightower 经常提到这个目标,Daniel Smith 也提出过未来目标的设想:Kubernetes API 可以管理一切,从虚拟化(如:虚拟机)到物理层面(如:路由器设备)的所有元素。
Kubernetes API 首先对资源进行建模,且对资源要求非常严格。资源的数据结构必须全部遵循标准规范(apiVersion、type,metadata 和 spec),并且资源(发布,放置,获取,删除等)动作都必须与 API 动词(HTTP Method)一致。
基于各类资源的广泛标准,Kubernetes 的工具和库可以在所有场景中保持一致地工作,而无需为每个资源进行定制化。Kubernetes 社区中的许多 SIG 组织都在不断的扩展着这一 API 系统,而不是将功能转移到其他组件上。当然了,这也是 Kubernetes 复杂度讨论的高峰。
因为 Kubernetes API 标准化,可以将所有基础设施进行资源建模。因为资源数据结构标准化,控制器也可以忽略资源之间的差异。例如:检查资源标签上的策略是否符合要求的控制器不需要知道每个资源的作用,只要结构是一致的,控制器可以去相同的位置为每个对象检查。
声明式 API
声明式(Declarative)的编程方式一直都会被工程师们拿来与命令式(Imperative)进行对比,前者是 “面向结果的”,后者是 “面向过程的”。
通常,我们接触的都是命令式编程,它要求我们描述为了达到某一个效果或者目标所需要完成的指令,常见的编程语言 Go、Ruby、C++ 其实都为开发者了命令式的编程方法。
而 SQL 则是种常见的声明式编程语言,它能够让开发者自己去指定想要的数据是什么。声明式的编程方式能够大量地减少开发者的工作量,极大地提升了开发及运维的效率。当然了声明式编程,往往需要基于一个强大的 Resource Management System(资源管理系统)来作为支撑,例如:SQL 对应的 RDBMS。
Kubernetes 就是这么一个面向应用开发与部署的 Resource Management System,它围绕 etcd 构建出来的一套 “面向终态” 编排体系。当用户向 Kubernetes 提交了一个 API 对象(Kubernetes Object)的描述(Spec)之后,Kubernetes 会负责为你保证整个集群里各项资源的状态(Status),都与你的 API 对象描述的需求相一致。更重要的是,这个保证是一项 “无条件的”、“没有期限” 的承诺:对于每个保存在 etcd 里的 API 对象,Kubernetes 都通过启动一种叫做 “控制器模式”(Controller Pattern)的无限循环,不断检查,然后调谐,最后确保整个集群的状态与这个 API 对象的描述一致。
可见,Kubernetes 项目实现 “容器编排” 的核心,在于一个叫做 “控制器模式” 的机制,即:通过对 etcd 里的 API 对象的变化进行监视(Watch),Kubernetes 项目就可以在一个叫做 Controller 的组件里对这些变化进行响应。而无论是 Pod 等应用对象,还是 iptables、存储设备等服务对象,任何一个 API 对象发生变化,那么 Kubernetes 接下来需要执行的响应逻辑,就是对应的 Controller 里定义的编排动作。
for
实际状态 := 获取集群中对象X的实际状态(Actual State)
期望状态 := 获取集群中对象X的期望状态(Desired State)
if 实际状态 == 期望状态
什么都不做
else
执行编排动作,将实际状态调整为期望状态。
例如:当我们通过 YAML 来描述(定义)预期服务的拓扑结构和状态后,Kubernetes 就会帮助我们从现有的状态中进行迁移至预期的终态。
apiVersion: v1
kind: Pod
metadata:
name: rss-site
labels:
app: web
spec:
containers:
- name: front-end
image: nginx
ports:
- containerPort: 80
- name: rss-reader
image: nickchase/rss-php-nginx:v1
ports:
- containerPort: 88
当然,这里 Controller 正是依靠 etcd 的 Watch API 来实现对 API 对象变化的感知的。
简而言之,Kubernetes 中声明式的 API 就是指定集群所期望的运行状态,在出现任何与预期不一致的时候,它本身都可以通过指定的 YAML 文件对线上集群进行状态的迁移,最终会根据当前的状态自动做出做合适的操作。
Kubernetes API Server 将成为软件的通用控制平面
Kubernetes API 生态风起云涌,几乎在每个星期,甚至每一天,都有太多围绕着 Kubernetes 开发者生态的角逐,在这个无比繁荣的社区背后,以不为人知的方式开始或者谢幕。
而这一切纷争的根本原因却无比直白。Kubernetes 项目,已经被广泛认可为云计算时代应用开发者们的终端入口。这正是为何,无论是 Google、微软,还是 CoreOS 以及 Heptio,所有这个生态里的大小玩家,都在不遗余力的在 Kubernetes API 层上捍卫着自己的话语权,以期在这个未来云时代的开发者入口上,争取到自己的一席之地。
在过去几年激烈的竞争中,Kubernetes 的战略定于为容器编排平台的标准。但从长远来看,Kubernetes 的愿景不仅是围绕容器,而是围绕其 API,它试图成为一个在更加基本和更广泛层面上的软件管理平台。这个目标会使 Kubernetes API 成为软件的通用控制平面,API Server 会成为管理软件的权威接口。
以上是关于Kubernetes 系列从设计理念说起的主要内容,如果未能解决你的问题,请参考以下文章
有容云干货-容器系列Kubernetes调度核心解密:从Google Borg说起