一、Kubernetes诞生背景
如果要了解Kubernetes的诞生,就绕不开整个云计算的发展历程。了解了云计算的发展的过程,就会明白,Kubernetes是云计算发展到一定程度的必然产物。
云计算发展历程
云计算发展历程的时间轴如下图所示,从物理机过渡到传统的IaaS阶段,进而发展为早期的PaaS,直至发展到如今的基于Kubernetes架构的新兴PaaS平台。
用户使用资源的形态也由早期的物理机过渡到虚拟机,再进化到目前更轻量的Docker容器。本质上云计算实现的关键突破就在于资源使用方式的改变,其最初解决的核心的问题就是应用的托管即应用部署与管理问题。
早期物理机时代
云计算之前,开发者如需部署管理服务,需要根据需求,进行配置、管理与运维物理机。整体上维护困难,成本高昂,重复劳动,风险随机。以至于当年流传着运维的传统艺能:上线拜祖(如图)。
在那个时代,应用部署与管理面临着以下诸多问题:
IaaS平台
Infrastructure as a service (IaaS) 基础设施即服务,用户可以按需去申请基础设施资源(包括:计算、存储、网络)。
IaaS商业化道路上的一个标志性事件:2006年AWS推出了EC2(亚马逊弹性云端运算),其基于Xen虚拟化技术,用户可以在web界面上配置、获取虚拟机资源,部署应用。通过规模化来降低边际成本。
虚拟化技术
IaaS的底层核心技术是虚拟化技术。虚拟化技术是一种资源关联技术,是将计算机的各种实体资源,如服务器、网络、存储等,进行抽象、整合、管理与再分配的一种技术。最常用的一种方案是基于虚拟机(Hypervisor-based)的虚拟化实现。其通过一个软件层的封装,提供和物理硬件相同的输入输出表现,实现了操作系统和计算机硬件的解耦,将OS和计算机从一对一转变为多对多(实际上是一对多)的关系。该软件层称为虚拟机管理器(VMM/Hypervisor),它分为两大类:裸金属架构、宿主机架构。
裸金属架构:VMM直接安装和运行在物理机上;VMM自带虚拟内核的管理和使用底层的硬件资源。业界的Xen、VMWare ESX都是裸金属架构。
宿主机架构:物理机上首先会装一个操作系统,VMM安装和运行在操作系统上;在VMM再去装其他虚拟机操作系统,依赖与操作系统对硬件设备的支持与资源的管理。这种架构的好处是,VMM会变的非常简单,因为可以基于操作系统去管理系统资源,VMM只需要做额外的虚拟化工作。Oracle VirtualBox,VMWare Workstation、KVM都是这种架构,宿主机架构是目前虚拟化技术的主流架构。
下图中,对比了物理机架构与宿主机虚拟化架构的区别。
虚拟化架构有如下的优势:
OpenStack
当物理机转变为虚拟机之后,如何对多台虚拟机的资源进行管理与调度,成为了一个新的问题。
OpenStack给出了解决方案,它是一个开源的分布式的平台,能够统一管理多个服务器,按用户需求进行分配与调度虚拟机。其本质上是一组分配、管理虚拟机的自动化工具脚本。
目前,OpenStack已经发展成了IaaS的主流解决方案,即:OpenStack as IaaS。目前主流IaaS云服务厂商底层都是利用OpenStack技术。
IaaS平台一定程度上提升了物理机的资源利用率,由物理机时代的低于10%,提升到了15%。但虚拟机对资源利用率的提升仍存在一定的局限性,其相对笨重,启动慢,自身消耗大(其完整运行了一套操作系统),自身加载就要消耗几百兆的内存资源。此外,虚拟机可以预装一些软件,一定程度简化了应用程序的依赖安装。但应用程序的部署与打包,仍然需要开发人员各自解决,仍未高效的完成应用部署与分发。
资料领取直通车:Golang云原生最新资料+视频学习路线https://docs.qq.com/doc/DTllySENWZWljdWp4
Go语言学习地址:Golang DevOps项目实战https://ke.qq.com/course/422970?flowToken=1043212
PaaS平台
Platform-as-a-Service (PaaS)平台即服务。PaaS提供了包括服务器、存储空间和网络等基础结构,但它并未包括中间件、开发工具、数据库管理系统等。PaaS旨在支持应用程序的完整生命周期:生成、部署、管理和更新,提供应用的托管能力。
在IaaS阶段,服务厂商只提供虚拟机,虚拟机之上的软件栈都由用户管理,包括操作系统、持久化层、中间层、用户程序。在IaaS层面用户只是减少了关心底层硬件,而PaaS层面希望能够进一步解放用户,让用户真正只需关注应用本身。
PaaS主要功能
目前一个成熟的PaaS平台应具备的主要功能 ,如下图所示:
早期PaaS平台,更多关注运行时环境与依赖服务,而目前的PaaS平台新增大量的支持服务,包括:认证授权、系统日志、应用监控等,以上都是应用开发的常见需求。原则上:共用内容就应该抽象出统一通用的组件,由框架和平台来实现。让用户只关心逻辑或应用本身,避免重复造轮子。
PaaS早期代表Cloud Foundry
PaaS在成熟之前也经历了几个阶段,而PaaS早期的代表就不得不提Cloud Foundry。Cloud Foundry由VMWare开发,是第一款开源PaaS平台(2011年)。支持多种框架、语言、运行时环境、云平台及应用服务,使开发人员能够快速进行应用的部署,无需担心任何基础架构的问题。
它主要功能包括以下:
Cloud Foundry的出现,其描绘了PaaS平台的初步形态,推动了PaaS的发展,具有划时代的意义。
但其最终并未成为PaaS主流,是因为其存在一个核心不足:它只对应用和配置进行了打包,而没有打包整体依赖(所谓的整体依赖包括:中间环境、操作系统文件)。所以它的包在跨平台运行时,会出现运行失败的现象。这个问题非常致命。
而且,早期Cloud Foundry主要是针对单一Web应用的管理,对分布式应用所需的各项能力均未涉及,例如:服务发现、弹性扩缩等。
Docker
Docker公司的前身是dotCloud,它是2010年成立,提供Paas服务的平台。但当时Cloud Foundry做的相对完善和开放,2012年底dotClound濒临倒闭,创始人决定把内部的打包平台开源出去。因此,2013年3月dotCloud公司在github平台上开源了其内部的容器项目Docker。Github开源之后,受到了业界的热烈追捧,从而Docker大火。公司后来也改名为Docker。
Docker的成功,主要是通过镜像完美解决了开发、测试、生产环境不一致的问题。它的口号是:Build、Shipand Run any App、Anywhere,即一处构建,到处运行。
Docker的核心技术有三个:NameSpaces做视图隔离、Cgroups做资源限制,UnionFS联合文件系统,统一mount。通俗理解:NameSpaces、Cgroups通给进程设置属性,实现进程的隔离与限制,UnionFS给进程构造文件系统。这三项技术均有linux内核提供,Docker本身并没有创造新的技术。
但是Docker创造性的通过镜像整体打包了应用的依赖环境,包括:操作系统文件、中间依赖层、APP。
1.整体打包之后,镜像变大,又该如何优化?
Docker通过镜像分层复用的方式进行了优化。共用只读层,节省存储空间,提高镜像推送、拉取效率,镜像的操作是增量式。
2.当分层之后,在宿主机上如何合并多个层?
利用UnionFS实现合并,多个只读层加一个可写层mount成一个目录。并且上面的层会覆盖下面的层,当对底层的只读层修改时会采用写时复制策略(copy-on-write)。写时复制的含义:当另一个层第一次需要写入该文件时(在构建镜像或运行容器时),该文件会被复制到该读写层并被修改。该机制大大减少了容器的启动时间(启动时新建的可写层只有很少的文件写入),但容器运行后每次第一次修改的文件都需要先将整个文件复制到container layer 中。
如下图所示,Docker相比于虚拟机操作系统级的资源隔离,实现了进程级资源隔离,极大提升了资源利用率。具备以下特点:
容器编排
当Docker解决了应用打包的问题后,PaaS上应用大规模部署与管理的问题愈发突出。此时,业内明白:容器本身没有“价值”,有价值的是容器编排。
容器编排(Orchestration):对Docker及容器进行更高级更灵活的管理,按照用户的意愿和整个系统的规则,完全自动化的处理好容器之间的各种关系(对象之间的关系远重要于对象本身)。
容器技术做为底层基础技术,只能用来创建和启动容器的小工具,最终只能充当平台项目的“幕后英雄”。用户最终部署的还是他们的网站、服务、数据库,甚至是云计算业务。这就需要一个真正的PaaS平台,让用户把自己的容器应用部署在此之上。
在以上的历史背景之下,2014年左右,Docker、Mesos、Google相继发布自己的PaaS平台,容器编排之争正式开始。
Docker发布了Swarm平台,Swarm擅长跟Docker生态无缝集成,docker用户可以低成本过渡。其最大亮点是使用Docker项目原有的容器管理API来完成集群管理。例如: 单机Docker项目: docker run “我的容器”。 集群Docker项目:docker run -H “我的Swarm集群API地址” “我的容器”。
Mesos平台,擅长大规模集群的调度与管理。它是Apache基金会下的一个开源集群管理器,最初是由Berkeley分校开发的。它为应用程序提供了跨集群的资源管理和调度API。之后转向支持PaaS业务,推出了Marathon项目。它是一个高度成熟的PaaS项目,旨在让用户便捷管理一个数万级别的物理机集群,可使用容器在这个集群里自由部署应用。
Google推出的是Kubernetes平台,整个系统的前身是Borg系统,Kubernetes平台是Google在容器化基础设施领域十多年来实践经验的沉淀与升华。
经过近3年的角逐,容器编排之争的胜利者是Kubernetes。
Kubernetes,读者一定会有一个疑问:为什么最后是Kubernetes?
每个人对这个问题,都有一些自己的理解,本文从技术方面对该问题进行了阐述。
二、Why Kubernetes
Kubernetes源于希腊语,意为“舵手”。k8s 缩写是因为 k 和 s 之间有八个字符的原因。它是google在2015开源的容器调度编排的平台。它是建立在Google大规模运行生产工作负载(Borg系统)十几年经验的基础上, 结合了社区中最优秀的想法和实践,已经成为了目前容器编排的事实标准。
其实看到Docker和Kubernetes的Logo,就可以很快明白Kubernetes的作用。Docker的Logo是一条鲸鱼船,运载着许多封装好的集装箱(container),代表着一次打包到处运行的意图。而Kubernetes的Logo就是这条船的方向舵!
对于Why Kubernetes?很多人都有自己的理解,接下来笔者从技术的角度,阐述一下自己的观点。Kubernetes技术上的成功,个人认为核心在于三个关键点:
Kubernetes前身
Kubernetes的基础特性,并不是几个工程师突然“拍脑袋”想出来的东西,而是 Google 公司在容器化基础设施领域多年来实践经验的沉淀与升华。这个实践与升华的过程,就是Kubernetes的前身是Borg系统。
Borg系统一直以来都被誉为Google内部最强大的“秘密武器”,是Google整个基础设施的核心依赖。很多应用框架已经运行在Borg上多年,其中包括了内部的MapReduce、GFS、BigTable、Megastore等,上层应用程序更是有这些耳熟能详的产品:Gmail、Google Docs、Google Search等。
其架构图如下所示:
架构分析:
根据2015年4月google发布的Large-scale clustermanagement at Google with Borg,与其2020年7月发布的Borg: the nextgeneration,两篇论文中的数据表明:Borg系统通过对在线任务与离线任务进行混合部署,可以节约20%-30%的资源,极大提高了资源利用率。下表是2011年与2019年的Borg集群,与2015年AWS、Facebool、Twitter数据中心资源利用率的对比图。
对于成熟高效的Borg系统,继承者Kubernetes从中获得了宝贵的经验:
Kubernetes架构
整体架构
Kubernets整体架构,如下所示:
整个系统由控制面(Master)与数据面(Worker Node)组成。 Master核心组件:
Kubernetes架构具备高可用:一方面Master节点高可用;另一方面所部署的业务也是高可用的。系统高可用的核心在于冗余部署,当某一个节点或程序出现异常时,其他节点或程序能分担或替换工作。 Master节点高可用,主要由以下几个方面的设计实现:
Work Node节点由以下组件组成:
API Server中心枢纽
Kubernetes中API Server的核心功能是提供Kubernetes各类资源对象(如Pod、RC、Service等)的增、删、改、查及Watch等HTTP REST接口,成为集群内各个功能模块之间数据交互和通信的中心枢纽,是整个系统的数据总线和数据中心。除此之外,它还是集群管理的API入口,提供了完备的集群安全机制。API Server是由多实例同时工作,各个组件通过负载均衡连到具体的API Server实例上。
如下所示,各组件与API Server通信时,采用List-Watch机制,通过API server获取etcd配置与状态信息,进而触发行为。以下图为例是kubectl创建一个deployment时,各个组件与API Server的流程交互。
Api Server的作用:
Kubernetes核心设计
Kubernetes取的巨大的成功,与它良好的核心设计紧密相关。笔者认为Kubernetes有三大核心设计:
Pod对象
Kubernetes在对象抽象方面,核心创新在于Pod对象的设计。容器设计本身是一种“单进程”模型。该表述不是指容器里只能启动一个进程,而是指容器无法管理多个进程。只有容器内PID=1的进程生命周期才受到容器管理(该进程退出后,容器也会退出),其他进程都是PID=1的进程的子进程。根据容器设计模式,传统架构中多个紧密配合的业务进程(例如业务进程与日志收集进程,业务进程与业务网络代理进程)应该部署成多个容器。但这些容器之间存在亲密的关系,需要一起调度和直接共享某些资源(网络和存储)。
Kubernetes抽象出一个Pod对象,是一组(一个或多个)容器, 这些容器共享存储、网络等, 这些容器是相对紧密的耦合在一起的。Pod是Kubernetes内创建和管理的最小可调度单元,调度过程是按Pod整体所需资源一起进行调度的。Pod本身只是逻辑上的概念,在容器管理这层并不认识Pod对象。
Pod的实现需要使用一个中间容器(Infra容器),在这个Pod中,Infra容器永远是第一个被创建的容器,用户定义的其他容器通过Join Network Namespace的方式与Infra容器关联在一起。抽象一个中间容器的原因在于各个业务容器是对等的,其启动没有严格的先后顺序,需借助中间容器实现共享网络和存储的目的。
其中,Node、Pod与容器三者关系,如下图所示。Node表示一台机器,可调度多个Pod,而一个Pod内又能包含多个容器。
至此,再来通过Kubernetes中各个对象的关联关系来更为深刻的理解Pod的意义。下图可以看出,Pod其实是整个编排过程中操作的核心,很多对象直接或间接的同Pod相关联。
Service对象
Kubernetes编排抽象的另一个核心对象是Service对象,它统一的解决了集群内服务发现与负载均衡。Service是对一组提供相同功能的Pod的抽象,为其提供了一个统一的入口。Service通过标签选择服务后端,匹配标签的Pod IP和端口列表组成endpoints,由kube-proxy负责将请求负载到相关的endpoints。
下图是kube-proxy通过iptables模式来实现Service的过程,Service对象有一个虚拟clusterIP,集群内请求访问clusterIP时,会由iptables规则负载均衡到后端endpoints。
声明式API
Declarative(声明式设计)指的是一种软件设计理念和编程方式,描述了目标状态,由工具自行判断当前状态并执行相关操作至目标状态。声明式强调What,目标是什么。而Imperative(命令式)需要用户描述一系列详细指令来达到期望的目标状态。命令式强调How,具体如何做。
下图描绘了一个场景:目标副本数为3。对于声明式而言,用户设定目标为3,系统获取当前副本数为2,系统判定当前值与目标值的差为1,便自行加1,最终实现副本数为3的目标状态。而对于命令式,需用户判断当前副本数为2,用户给出指令副本+1,系统接收用户指令,执行副本数+1操作,最终系统副本数为3。
kubernetes的一大核心设计就是采用了声明式API,利用该设计思想有效的实现了系统的自动化运行。Kubernetes声明式API指定了集群期望的运行状态,集群控制器会通过List&Watch机制来获取当前状态,并根据当前状态自动执行相应的操作至目标状态。
Kubernetes中,用户通过提交定义好的API对象来声明期望状态,系统允许有多个API写端,以PATCH方式对API对象进行修改。Kubectl工具支持三种对象管理方式:命令式命令行、命令式对象配置(yaml)、声明式对象配置(yaml)。举例如下:
命令式命令行:
1 kubectl run nginx –image nginx
命令式对象配置:
1 kubectl create –f nginx.yaml
2 kubectl replace –f nginx.yaml
以上先kubectl create再kubectl replace的操作,与命令式命令行不存在本质区别。只是把具体命令写入yaml配置文件中而已。 声明式对象配置:
1 kubectl apply –f nginx.yaml
Kubernetes推荐使用:声明式对象配置(YAML)。kubectl replace执行过程是通过新的YAML文件中的API对象来替换原有的API对象,而Kubectl apply执行了一个对原有API对象的PATCH操作。 除此之外,YAML配置文件用于Kubernetes对象的定义,还会有以下收益:
开放插件
Kubernetes的设计初衷就是支持可插拔的架构,解决PaaS平台使用不方便、不易扩展等问题。为了便于系统的扩展,Kubernetes中开放了以下接口可对系统资源(计算、网络、存储)插件进行扩展,可分别对接不同的后端来实现自己的业务逻辑。
Kubernetes除了对系统资源可插件扩展外,也可以自定义CRD(Custom Resource Definition)来扩展API对象,同时也支持编写Operator对CRD进行控制。例如:对于一些有状态应用(etcd),可以定义新的CRD对象,并编写特定的Operator(本质上是新的controller)去实现控制逻辑。
Kubernetes的调度器Scheduler也是可以扩展的,可以部署自定义的调度器,在整个集群中还可以同时运行多个调度器实例,通过 pod.Spec.schedulerName 来选择具体指定调度器(默认使用内置的调度器)。
三、小结
根据以上两个章节的阐述,对于文章开头的经典问题:如何才能有效的部署与管理应用?到Kubernets大放异彩的今天,已经给出了答案:
感谢Kubernetes,将开发、运维人员从繁重的应用部署与管理工作中解放出来。到目前为止,Kubernetes已经成为了容器编排的事实标准,是新一代的基于容器技术的PaaS平台的重要底层框架。
Kubernetes的成熟,拉开了轰轰烈烈的云原生技术发展的大幕!