云计算
我是谁
我叫张磊。 是Kubernetes社区的资深成员和项目维护者。 在Kubernetes和Kata Containers社区从事上游开发,建立了容器镜像亲密性调度、基于等价类的调度优化等多个核心特性,具有容器运行时界面、安全容器沙箱作为主要的研发人员和维护者之一,经历了Serverless Container概念的诞生和兴起。
工作之余,创办并组织了《docker 容器与容器云》一书,得到了众多渴望先进容器技术的读者的好评。 参与并经历了从“开跑”到“除尘”的容器技术全过程
文末福利有获取K8S知识福利的指南。
从“容器”到“容器云”
“容器”实际上是一个由Linux Namespace、Linux Cgroups和rootfs三种技术构建的流程隔离环境。
因此,实际上,您可以将正在运行的Linux容器分为“两半”进行查看:
与/var/lib/docker/aufs/mnt联合装载的一组rootfs。 这部分称为“容器镜像”( Container Image ),是容器的静态视图。
由Namespace Cgroups构成的隔离环境。 此部分称为“容器运行时”( Container Runtime ),是容器的动态视图。
作为开发人员,我不关心容器在运行时的差异。 因为,在“开发-测试-发布”流程中,实际承载和传递容器信息的不是容器的运行时,而是容器的镜像。
这个重要假设是集装箱技术圈在Docker项目成功后迅速走向“集装箱编制”这一“上层建筑”的主要原因。 作为云服务提供商或基础设施提供商,如果用户提交的Docker能够起到容器的作用,就可以成为这个非常热闹的容器生态图上的一个装载点,将整个容器技术堆栈的价值沉淀在我的节点上。
更重要的是,只要从我这个载体点开始,向Docker镜像制作者和用户的方向追溯,CI/CD、监视、安全性、网络、存储等,整个路径的各服务节点都有我可以发挥并受益的余地这一逻辑是所有云计算提供商都关注容器技术的重要原因。 通过容器镜像,您可以直接与潜在用户(即开发人员)相关联。
从一个开发者和单一的容器镜像,到无数开发者和巨大的容器集群,容器技术实现了从“容器”到“容器云”的跨越,标志着它真正得到了市场和生态的认可。
这样,集装箱从开发者手中的小工具,成为云计算领域的绝对主角。能够定义容器组织和管理规范的“容器组织”技术,成为容器技术领域的“靠山”。
其中,最具代表性的集装箱编制工具是由Docker公司的Compose Swarm集团,以及谷歌和RedHat公司共同主导的Kubernetes项目。
Kubernetes项目的设计和体系结构
我想谈谈Kubernetes项目的设计和体系结构。
与许多基础设施领域先有工程实践,后有方法论的发展路线不同,Kubernetes项目的理论基础远远超前于工程实践。 这当然要归功于谷歌公司2015年4月发表的Borg论文。
Borg系统一直以来被称为谷歌公司内最强大的“秘密武器”。 虽然有点夸张,但这种说法并不是骄傲。 因为,与Spanner和BigTable等较高层的项目相比,Borg应该承担的责任是支撑谷歌公司整个基础设施的核心依赖。 在谷歌公司公开的基础设施体系论文中,Borg项目位于整个基础设施技术堆栈的最底层。
图片来源: Malte schwarzkopf .“operatingsystemsupportforwarehouse-scale computing”. PhD thesis.universityofcambridgecomputerleting
上图来自谷歌omega论文第一作者的博士毕业论文。 在这张图中,可以找到MapReduce、BigTable等著名项目。 您还将看到Borg及其继任者Omega位于整个技术堆栈的底部。
这样,Borg可以说是谷歌最不可能开源的项目。 幸运的是,多亏了Docker项目和容器技术的风靡,它终于以另一种方式与开源社区见面了。 这个方法是Kubernetes项目。
因此,与“小小骚动”的Docker公司、“用旧瓶子装新酒”的Mesos社区相比,Kubernetes项目从一开始就处于别人难以企及的水平。 在其成长阶段,各核心特性的提出几乎都来自于Borg/Omega系统的设计和经验。 更重要的是,这些特性在开源社区落地的过程中在社区的协助下得到了很大的改善,修复了当时Borg系统中遗留的许多缺陷和问题。
所以,虽然在发布之初就被批评为“唱腔高昂、沉默寡言”,但在注意到Docker技术堆栈的“稚嫩”和Mesos社区的“老迈”之后,很快就明白了这个社区。 Kubernetes项目在Borg系统的指导下,体现了自身的“先进性”和“完整性”。 这些是
为了更好地理解这两个特质,让我们从Kubernetes的顶层设计开始。
Kubernetes项目需要解决的问题
安排? 日程? 容器云? 还是集群管理?
其实,这个问题至今没有确定的答案。 因为Kubernetes需要重点解决的问题因发展阶段而异。
但是,对于大多数用户来说,我们希望Kubernetes项目带来的体验是确凿不移的。 现在我有了APP应用程序的容器镜像。 请在特定群集上运行此APP应用程序。
此外,我们希望Kubernetes提供一系列运输能力,包括路由网关、水平扩展、监控、备份和灾难恢复。
等一下,你好像对这些功能有点耳熟? 这不就是经典的PaaS (例如Cloud Foundry )项目的能力吗?
然后,一旦有了Docker,就完全不需要Kubernetes、PaaS了。 使用Docker公司的Compose Swarm项目,可以完全轻松地发现这些功能。
因此,如果Kubernetes项目仅用于镜像用户、运行容器和提供常规运输功能,则它当然会与“本机”Docker Swarm项目竞争,而且还是经典的PaaS项目
事实上,在定义核心功能的过程中,Kubernetes项目根据Borg项目的理论优势,在短短几个月内就站稳脚跟,确定了如下图所示的全局体系结构。
从这个框架中可以看出,Kubernetes项目的框架与其原型项目Borg非常相似,都由Master和Node两种节点构成,这两种作用分别与控制节点和计算节点相对
其中,作为控制节点的Master节点由密切协作的3个独立组件组合而成,分别是负责API服务的kube-apiserver、负责调度的kube-scheduler 整个群集的持久性数据由kube-apiserver处理,然后存储在Ectd中。
计算节点最核心的部分是名为kubelet的组件。
kubelet组件
在Kubernetes项目中,kubelet主要负责与容器(如Docker项目)的运行时交互。 此交互组件所依赖的是一个称为containerruntimeinterface ( CRI )的远程调用接口,它定义了容器运行时的每个核心操作,包括启动容器所需的所有参数。
因此,Kubernetes项目不关心什么容器运行时,引入了什么技术实现。 如果在运行此容器时可以执行标准容器镜像,则可以通过实现CRI来访问Kubernetes项目。 在特定的容器运行时,例如在Docker项目中,它通常通过名为OCI的容器运行时规范与底层Linux操作系统进行交互。 这意味着将CRI请求翻译为对Linux操作系统的调用(如Linux Namespace或Cgroups操作)。
kubelet还通过与gRPC协议相同的名为Device Plugin的插件进行交互。 该插件是Kubernetes项目用于管理GPU等主机物理设备的主要组件,也是基于Kubernetes项目的机器学习训练、高性能作业支持等工作必须关注的功能。
kubelet的另一个重要功能是调用网络插件和存储插件来配置容器的网络和持久性存储。 这两个插件是与kubelet交互的接口,分别是连续网络接口( CNI )和连续存储接口( CSI )。
实际上,kubelet这个奇怪的名字来自Borg项目的同源组件Borglet。 然而,如果您已经阅读了Borg论文,则此命名方法可能是kubelet组件和Borglet组件的唯一相似之处。 因为Borg项目不支持这里描述的容器技术,只是使用Linux Cgroups来限制进程。
也就是说,Borg中不存在像Docker这样的“容器镜像”,Borglet组件也不像kubelet那样需要考虑与Docker的交互以及如何管理容器镜像,而是可以使用CRI、CNI、CSI等多种容器
kubelet是完全为实现Kubernetes项目容器管理能力而重新实现的组件,与Borg之间没有直接的传承关系。
注:虽然没有使用Docker,但谷歌内部使用了名为midaspackagemanager(MPM )的软件包管理工具。 实际上,您可以替换Docker镜像的部分角色。
Borg的指导作用
Borg对Kubernetes项目的指导作用是什么? 答案是主节点。
在实现主节点的详细信息中,Borg项目和Kubernetes项目不同,但出发点是高度一致的。 这意味着如何组织、管理和安排用户提交的工作。
因此,Borg项目可以将Docker镜像视为一种新的APP打包方案。 这样,Borg团队过去在大型工作管理和组织方面的经验可以直接“应用”到Kubernetes项目中。
这些经历的最主要表现是,从一开始Kubernetes项目就不像同时期的各种“容器云”项目那样,将Docker作为整个体系结构的核心,而是作为最底层的一个容器运行时来实现。
Kubernetes项目需要重点解决的问题来自Borg研究人员在论文中阐述的非常重要的观点:
在大规模集群中运行的各种任务之间实际上存在各种各样的关系。 这些关系的处理,才是工作编制和管理系统最难的地方。
事实正是如此。
其实,这种任务和任务的关系在我们平时的各种技术场景中都可以看到。 例如,web APP应用程序和数据库之间的访问关系、负载平衡器及其后端服务之间的代理关系,以及入口APP应用程序和许可证组件之间的调用关系。
再进一步说,属于同一服务单位的不同功能之间也完全有可能存在这种关系。 例如,web APP应用程序和日志收集组件之间的文件交换关系。
在容器技术普及之前,在传统的虚拟机环境中,处理这种关系的方式是“粗粒度”。 您很清楚,许多功能不相关的APP应用程序被集中部署在单个虚拟机上,因为它们之间可能会出现一些HTTP请求。 更常见的是,APP应用程序部署到虚拟机后,需要手动维护许多与之协作的守护程序( Daemon ),以处理日志收集、灾难恢复和数据备份等辅助任务。
但集装箱技术问世后,在“功能单位”的划分上,可以看出集装箱具有独特的“细粒度”优势。 换句话说,容器的本质仅仅是一个过程。 这意味着,在同一个虚拟机中的每个APP应用程序、组件和守护程序都可以根据需要单独镜像,并在专用容器中运行。 它们互不干扰,具有各自的资源配额,可以调度到整个集群的任何机器上。 这正是PaaS系统最理想的工作状态,也是所谓“微服务”思想落地的前提条件。
当然,如果只实现“封装微服务,调度单个集装箱”的水平,Docker Swarm项目就绰绰有余了。 加上Compose项目,您还可以处理简单的依赖关系,如访问“Web容器”和数据库“DB容器”。
在Compose项目中,可以为这两个容器定义" link " Docker项目将保持这种“链接”关系。 具体来说,Docker将数据库容器的IP地址、端口等信息作为环境变量注入Web容器中,供供应流程使用。 例如,以下内容:
DB_NAME=/web/db
db _ port=TCP://172.17.0.5:5432 db _ port _ 5432 _ TCP=TCP://172.17.0.5:5432 db _ port _ 5432
如果数据库容器发生更改,则这些环境变量的值将由Docker项目自动更新,例如更新镜像、迁移到其他主机等。 这是平台项目自动处理容器之间关系的典型例子。
但是,如果我们现在的需要是,这个项目能够处理上述所有类型的关系,支持将来可能出现的更多类型的关系呢?
在这种情况下,为单个事例(如“link”)单独设计的解决方案太简单了。 从事过体系结构的工作,就会体会到要追求项目的普遍性,一定要从顶层设计。
因此,Kubernetes项目最主要的设计思想是从更宏观的角度统一定义任务之间的各种关系,为将来支持更多种类的关系留有余地。
例如,Kubernetes项目对容器之间的“访问”进行了分类,首先总结了非常常见的“密切交互”关系,即这些APP应用之间需要非常频繁的交互和访问。 或者通过本地文件直接交换信息。
在典型环境中,这些APP应用程序通常直接部署在同一台计算机上,并通过Localhost通信通过本地磁盘目录交换文件。 在Kubernetes项目中,这些容器被划分为一个“Pod”,Pod中的容器共享相同的Network Namespace和相同的数据集,从而实现有效的信息交换。
Pod是Kubernetes项目最基础的对象,来源于谷歌博客论文中的Alloc设计。 下一章将更详细地介绍Pod。
Kubernetes项目为更常见的需要提供了一种称为“服务”的服务,例如web APP应用程序和数据库之间的访问关系。 这两个APP应用程序往往不是故意部署在同一台计算机上的,因此,如果有web APP应用程序的计算机停机,数据库也不会受到任何影响。 但是,既然知道信息(如IP地址)对容器来说是不固定的,web APP应用如何找到数据库容器的Pod呢?
因此,Kubernetes项目的做法是将服务绑定到Pod,服务声明的IP地址等信息是“一生不变”的。 该服务的主要作用是Pod的代理接口( Portal ),代替Pod向外暴露固定的网络地址。
这样,对于web APP应用程序的Pod来说,需要关注数据库Pod的服务信息。 不难想象,作为服务后端真正代理的Pod的IP地址、端口等信息的自动更新和维护是Kubernetes项目的作用。
这样,以容器和Pod为中心放大到实际的技术场景后,可以探索以下Kubernetes项目核心功能的“全景图”。
根据这张图的线索,我们从容器这个最基础的概念开始,首先要面对容器之间“紧密合作”关系的问题,一旦有了遍布Pod的Pod,我们希望一次启动多个APP应用实例。 为此,需要名为Deployment的Pod的多个实例管理器。 有了这样一组相同的Pod,就需要通过固定的IP地址和端口以负载均衡的方式进行访问,服务器。
但是,当前,除了两个不同的Pod之间存在“访问关系”外,还需要在开始时添加授权信息。 最典型的例子是,web APP应用程序访问数据库时需要Credential (数据库用户名和口令)信息。 那么,在Kubernetes中如何处理这样的关系呢?
Kubernetes项目提供了一个名为Secret的对象。 这实际上是存储在Etcd上的键值对数据。 由此,将Credential信息作为Secret保存到Etcd时,Kubernetes在指定的Pod (例如web APP应用的Pod )启动时,自动将Secret的数据作为Volume装载到容器中这样,此web APP应用程序就可以访问数据库。
除了APP应用和APP应用之间的关系之外,APP应用的执行方式是影响“如何容器化此APP应用”的第二个重要因素。
因此,Kubernetes定义了一个新的基于Pod的改进对象。 例如,Job用于描述一次执行的Pod (例如,大数据任务)。 例如,DaemonSet用于编写在每台主机上需要且只能执行一个副本的守护程序服务。 例如,CronJob用于描述定时任务等。
这样,Kubernetes项目是定义容器之间关系和形态的主要方法。
您可以看到,Kubernetes项目不像其他项目那样为每个管理功能编写指令,并在项目中实现其逻辑。 这种做法,确实可以解决目前的问题,但更多的问题来了之后,往往力不从心。
相比之下,在Kubernetes项目中,我们推荐使用以下方法:
首先,用Pod、Job、CronJob等“组织对象”说明要管理的APP应用程序
然后定义“服务对象”,如Service、Secret和Horizontal Pod Autoscaler。 这些对象负责具体的平台级功能。
这个用法是所谓的“声明型API”。 与该API相对应的“排列对象”和“服务对象”都是Kubernetes项目的API对象( API Object )。
这是Kubernetes最核心的设计理念,也是接下来我要重点分析的关键技术点。
如何启动集装箱化任务
例如,我现在创建了Nginx容器镜像。 我想让平台启动这个镜像。 然后,让平台运行两个完全相同的Nginx副本,以负载均衡方式共同对外服务。
如果要自己DIY,可能需要启动两台虚拟机,分别安装两个Nginx,然后使用keepalived创建两个虚拟机的虚拟IP。
如果我用Kubernetes项目呢? 需要的是创建一个类似于以下内容的YAML文件: 例如,名为nginx-deployment.yaml。
API version:apps/v1 kind:deployment metadata:name:nginx-deployment labels:app:nginx spec:replicas:2 selector:melector
上面的YAML文件定义了部署对象。 其主体部分( spec.template部分)是使用Nginx镜像的Pod,该Pod的副本数为2(Replicas=2)。
然后执行:
$ kubectlcreate-f nginx-deployment.YAML
这将启动两个完全相同的Nginx容器的副本。
但是,这样看来,即使做了同样的事情,Kubernetes用户要做的工作也不少呢。
稍后,我们将了解“声明性API”(如Kubernetes项目)的各种好处,以及在此基础上实现的强大组织能力。
鸡蛋:免费领取Kubernetes技能地图
关注此公众号并回复“K8S”,即可免费接收Kubernetes项目维护者张磊
由Etcd项目作者、阿里系统软件事业部资深技术专家李响推出的“Kubernetes技能地图”。
详情请访问云服务器、域名注册、虚拟主机的问题,请访问西部数码代理商官方网站: www.chenqinet.cn