1.1 Kubernetes概述
在学习具体细节之前,需要先了解Kubernetes的技术基础,包括Kubernetes是什么,Kubernetes出现的背景,Kubernetes与Docker、云原生的关系等,这些都将为后续进一步学习Kubernetes打下基础。
本书使用的Kubernetes版本是v1.20.1,后续所有的内容都是基于该版本的。
1.1.1 Kubernetes的定义和背景
Kubernetes是一个开源的容器编排系统,所谓“容器编排”就是容器部署、迁移、管理、扩展和联网的自动化。
Kubernetes又简称k8s,8表示Kubernetes中间所省略的8个字符。
下面以LAMP(Linux/Apache/MySQL/PHP)Web应用为例,说明Kubernetes出现的背景,以此加深对Kubernetes的理解。
1.基于传统方式的LAMP Web应用的部署和运维
传统方式部署LAMP Web应用,需要先在服务器上安装Linux操作系统,然后安装Apache Web服务器、MySQL数据库和PHP等组件并配置,最后运行这些组件,对外提供服务。由于这些组件是直接安装在Linux之上的,因此,组件的运行环境是同操作系统紧密耦合的,这样会导致部署工作量大且难以复用,具体说明如下。
● 如果Linux系统崩溃无法启动,需要重新安装Linux系统,重新安装Apache Web服务器等组件,所有的部署工作都要重做。
● 如果升级了Linux系统,由于兼容性问题,可能会导致Apache Web服务器等组件也需要升级到更高的版本,从而增加了部署的工作量。
● 如果更换新的硬件服务器,则需要在新服务器上重新安装Linux、Apache Web服务器等组件,所有部署的工作要重做一遍,无法复用之前所做的部署工作。
这种紧密耦合同样会导致运维出现一系列的问题:例如服务器崩溃时,如果没有做热备,用户将无法访问该Web应用;又比如服务器负载太大时,由于系统无法动态迁移,也将导致Web应用的服务质量下降。
2.基于容器的LAMP Web应用的部署和运维
基于容器,可以将Apache Web服务器和MySQL数据库等组件(程序/应用)分别制作成容器镜像,然后启动这些容器来运行其中的组件。容器技术可以使得这些组件的运行环境同操作系统解除耦合,从而解决上述部署和运维的问题。以Docker容器技术为例,它对于减少重复的部署工作的具体说明如下。
容器(Container)是一个隔离的程序运行环境。Docker则是一个基于容器技术的开放平台,它解决了容器的完善性和易用性的问题,重新定义了“软件交付方式”,也因此成为最主流的容器平台。
Docker容器的相关知识是Kubernetes学习最重要的基础,有关Docker的核心概念和实操请参考艾叔编著的《Linux快速入门与实战——基础知识、容器与容器编排、大数据系统运维》一书。
● 如果Linux系统崩溃无法启动,则只需要将事先备份好的Docker镜像导入到其他安装了Docker引擎的主机上,就可以直接运行容器的组件了,完全不需要重复之前的部署工作。
● 如果升级了Linux系统,则只需要关注Docker引擎能否正常工作,如果Docker引擎因为兼容性不能正常工作,升级Docker引擎版本即可,已有的Docker镜像无须任何修改就可以正常启动,也就不需要重复之前的部署工作。
● 如果更换新的硬件服务器,则只需要在该服务器上安装好Docker引擎,然后导入之前备份的Docker镜像即可,从而复用之前的部署。
同样地,容器技术可以解决运维工作中的一系列问题:例如服务器崩溃时,可以立即在另一台服务器上启动这些组件对应的镜像,以保证其可用性;如果服务器负载太大时,可以将该Web应用对应的镜像迁移到性能更强的服务器上,然后运行容器组件即可,从而保证Web应用的服务质量。
迁移镜像和运行容器的这些操作,大多是通过命令或脚本来手动完成的。
3.基于Kubernetes的LAMP Web应用的部署和运维
从技术上讲,容器技术解除了程序运行环境同Host操作系统之间的耦合,解决了程序运行环境的隔离和迁移问题,大幅简化了部署和运维工作。但由于这些容器通常都是在集群上运行的,共享整个集群资源,那么集群中容器如何自动部署、如何管理、如何联网,以及如何保证容器的可用性和扩展性等,都是容器技术出现之后带来的新问题。这些问题不解决,大量的容器在同一集群中运行就会出现各种各样的问题,集群资源也就无法高效利用,在大规模集群中运维这些容器就会非常困难。
上述问题的根源在于缺乏一个从集群的角度来整体规划,实现容器部署、迁移、管理、扩展和联网的自动化的系统,即“容器编排”系统。虽然集群的各个节点通过网络连接,但在逻辑上是孤立的,当一个节点不可用时,此节点上的服务就不可用了,集群并不能利用其他节点继续对外提供相同的服务。就如同计算机出现之初,没有操作系统的情况一样。这就是Kubernetes出现的背景。
Kubernetes管理整个集群的资源,使之成为一个整体,并引入了一个新概念——Pod。Pod是Kubernetes的最小运行单元,Pod由一组(也可以是一个)关系密切的容器组成,它们互相协作,在同一个节点上运行,对外提供某种服务,即“微服务”。当Pod不可用时,Kubernetes可以通过重启Pod中的容器,或者将Pod调度到其他节点,来保证服务的可用性。
以LAMP Web应用为例,可以使用Kubernetes将LAMP Web容器放置到同一个Pod中,不仅可以完全继承容器技术所带来的好处,还能带来以下好处。
● 只需要一个命令,就可以实现LAMP容器的自动部署,Kubernetes会选择合适的节点来创建Pod,还可以避免用户同时部署容器所带来的冲突。
● 当容器崩溃或Pod节点不可用导致服务不可用时,Kubernetes会根据情况,自动重启Pod中的容器或将Pod自动迁移到其他节点上运行,确保服务的可用性。
● 如果节点负载太大时,Kubernetes会根据配置,在其他的节点上运行该Pod的副本,实现服务的水平扩展。
● Kubernetes对整个集群资源统一管理,可以实现集群中各种应用的和平相处,最大限度地提升集群资源的利用率。
总之:容器技术大幅降低了应用在单机上的部署和运维工作量,Kubernetes则是大幅降低了应用在集群上的部署和运维工作量,而且还可以提升应用的可用性和服务能力,充分利用集群资源,适合更多的应用场景。
访问https://kubernetes.io/获取更多有关Kubernetes的信息。
1.1.2 Kubernetes与Docker
Kubernetes和Docker之间的关系是错综复杂的。为简单起见,本书从纯技术的角度对Kubernetes和Docker两者的关系做一说明。
如图1-1所示,Docker在Kubernetes中主要是用作Node节点上的容器运行时,kubelet在Node节点上运行容器,依赖于“容器运行时”,包括“高层容器运行时”和“低层容器运行时”,其中“高层容器运行时”主要有3种:Docker、containerd和CRI-O,直至Kubernetes 1.20版本,Docker一直都是Kubernetes默认的“高层容器运行时”,其调用路线如图1-1所示,kubelet通过gRPC调用CRI接口,由于Docker中的dockerd不直接支持该调用,所以Kubernetes专门写了一个dockershim来适配kubelet和Docker,dockershim是一个gRPC Server,它接收kubelet的gRPC调用请求,然后将调用的CRI接口功能转换成对dockerd的调用,从而实现相应的功能。
有关Kubernetes架构相关信息参考1.3节。
2020年12月8日,Kubernetes发布1.20版本的同时,宣布后续的Kubernetes版本将会放弃Docker作为“高层容器运行时”,1.20版本将会给出Docker的弃用警告,后续的版本(目前计划是1.24)将会从Kubernetes中删除Docker,并切换到其他的“高层容器运行时”之一,如containerd或CRI-O。
图1-1 Kubernetes调用过程图
如图1-1所示,这两种“高层容器运行时”和Docker相比,调用的路径更短,效率更高,而且在逻辑上更清晰,维护的成本也更低。并且由于Docker本身也是使用containerd作为“高层容器运行时”的,Kubernetes弃用Docker,只是弃用之前调用dockerd的路径,底层的实现用的还是containerd。因此,对于Kubernetes的容器运行时来说,并没有本质的改变,稳定性也有保证。
对于今后新版本的Kubernetes,在每个Node节点上将不再需要安装Docker,这意味着Node节点更加简洁,维护成本更低,但这并不意味着Kubernetes和Docker彻底撇清了关系。因为Kubernetes底层的容器镜像规范(OCI Image spec)和运行时规范(OCI Runtime spec)都是以Docker作为主要参考对象制定的,而且Docker还捐赠了参考实现,因此,Docker镜像天生就是符合OCI规范的。后续即使新版本的Kubernetes不再支持Docker,但已有的Docker镜像和后续使用Docker build的镜像,依然是Kubernetes中的标准镜像,可以顺利使用。
总之,Kubernetes弃用Docker作为容器运行时,对于用户来说,并不会有大的影响,用户依然可以用Docker来build镜像,用户已掌握的有关Docker的知识,包括容器镜像格式和容器运行时,对于Kubernetes依然适用。
后续即便Docker公司在同Google公司的竞争中日渐式微,甚至Docker公司不复存在,但Docker作为一种颠覆式的IT技术,它重新定义了“软件交付方式”,已经在IT技术史册上留下了浓墨重彩的一笔。
1.1.3 Kubernetes与云原生
理解“云原生”,要从理解“云”开始,“云”是指:构建在网络之上的一个可以动态伸缩的资源池。典型的资源池有两种:计算和存储。例如,计算资源池可以看作是一个由无数台虚拟机组成的一个资源池。当用户需要一台新的计算机去完成某个任务时,不需要像以前一样去购买一台真正的物理机器,而是可以从提供“计算资源池”的厂商那里租一台机器,这台机器位于云端(网络),用完就还给厂商了,只需要对自己所使用的部分付费,既方便又经济。这样,以前跑在本地的程序(计算任务)就运行在了“云”端,因此也把这种程序的运行方式(计算模式)称为云计算,把提供这种服务的厂商,称为云计算厂商,例如Google、亚马逊、微软、阿里和腾讯都是典型的云计算厂商。根据云所在的位置不同:内网、公网、内外网,又分别称之为私有云、公有云和混合云。
资源池和自然界的云有诸多相似之处,例如:计算机网络的符号就是一朵云;资源池位于网络,云远在天边,都不在本地;资源池和云一样可大可小(弹性伸缩)。
云原生则是因云而生的技术,它用来帮助用户在云中构建和运行可伸缩的应用。典型的云原生技术包括:容器(Container)、服务网格(Service Mesh)、微服务(Micro Service)、不可变基础设施(Immutable Infrastructure)和声明式API(Declarative API)等。
容器:隔离的程序运行环境,Docker容器技术可以将整个程序的运行环境打包成一个镜像文件,在任意安装了Docker引擎的机器上运行,它解除了程序运行环境同Host操作系统之间的耦合,保证了程序运行环境的一致性,重新定义了软件交互方式。
服务网格:构建在服务之上的服务代理层,它用于实现服务请求的可靠传递、服务发现、认证授权、降级熔断等功能,使得应用可以专注自身的业务逻辑。
微服务:一种开发软件的架构和组织方法,将原来由一个进程提供所有(多个)服务的方式,修改为由一个进程提供一个服务(或少量几个)的方式,一个容器只运行一个进程(或少量几个进程),进程间通过服务进行通信,从而解决服务同进程间的耦合,减低部署工作量,提升灵活性、扩展性和代码的复用性等。
不可变基础设施:应用部署后其基础设施不再改变,如果要修改基础设施,则通过重新部署来实现。这种方式和传统的可变基础设施(可以随时修改)相比,从技术上保证了基础设施的修改可以追溯,基础设施的架构一致性和可靠性程度更高,部署过程更简单且更可预测。Docker容器技术中容器和镜像分离,且容器中的修改无法直接传递到镜像的特性,可以很好地支持不可变基础设施。
声明式API:在API中只描述期望对象所达到的状态,具体的实现则交由系统自身去实现,而不是像“命令式API”那样,通过一步步调用API接口,去达到所期望的状态。“声明式API”在降低接口使用难度、简化接口设计、提升服务并发度等方面优势明显。
为了构建云原生生态并推广云原生技术,Google牵头成立了CNCF(Cloud Native Computing Foundation,云原生基金会),Kubernetes就是CNCF的首个项目。Kubernetes作为一个平台,整合并支持这些云原生的核心技术,例如:采用容器作为底层引擎;采用不可变基础设施进行构建和运行应用;采用声明式API对外提供服务;支持构建微服务;支持Service mesh并对其进行扩展,等等。因此,Kubernetes是云原生的核心和基石。
Linux基金会报告显示,2021年,云原生技术首次超过Linux,成为最热门的开源技术,云原生(Cloud Native)应用将是今后的重点,应用的设计和开发从一开始就要考虑上“云”,而Kubernetes作为云原生关键技术和核心基础设施,更是热门中的热门。因此,无论我们今后是从事运维还是研发,都需要学习Kubernetes,学得越早,掌握得越好,就越会成为自身的一个优势。