- Kubernetes进阶实战(第2版)
- 马永亮
- 3917字
- 2021-03-12 17:08:46
1.1 容器与容器编排系统
容器技术由来已久,却直到几十年后因dotCloud公司(后更名为Docker)于Docker项目中发明的“容器镜像”技术创造性地解决了应用打包的难题才焕发出新的生命力并以“应用容器”的面目风靡于世,Docker的名字更是响彻寰宇,它催生出或改变了一大批诸如容器编排、服务网格和云原生等技术,深刻影响了云计算领域的技术方向。
1.1.1 Docker容器技术
概括起来,Docker容器技术有3个核心概念:容器、镜像和镜像仓库(Docker Registry)。如果把容器类比为动态的、有生命周期的进程,则镜像就像是静态的可执行程序及其运行环境的打包文件,而镜像仓库则可想象成应用程序分发仓库,事先存储了制作好的各类镜像文件。
运行Docker守护进程(daemon)的主机称为Docker主机,它提供了容器引擎并负责管理本地容器的生命周期,而容器的创建则要基于本地存储的Docker镜像进行,当本地缺失所需的镜像时,由守护进程负责到Docker Registry获取。Docker命令行客户端(名为docker)通过Docker守护进程提供的API与其交互,用于容器和镜像等的对象管理操作。Docker各组件间的逻辑架构及交互关系如图1-1所示。
任何拥有Docker运行时引擎的主机都能够根据同一个镜像创建并启动运行环境完全一致的容器,在容器中添加新数据或修改现有数据的结果都存储在由容器附加在镜像之上的可写顶层中。因此,同一Docker主机上的多个容器可以共享同一基础镜像,但各有自己的数据状态。Docker使用aufs、devicemapper、overlay2等存储驱动程序来管理镜像层和可写容器层的内容,尽管每种存储驱动程序实现的管理方式不尽相同,但它们都使用可堆叠的镜像层和写时复制(CoW)策略。
删除容器会同时删除其创建的可写顶层,这将会导致容器生成的状态数据全部丢失。Docker支持使用存储卷(volume)技术来绕过存储驱动程序,将数据存储在宿主机可达的存储空间上以实现跨容器生命周期的数据持久性,也支持使用卷驱动器(Docker引擎存储卷插件)将数据直接存储在远程系统上,如图1-2所示。
拥有独立网络名称空间的各容器应用间通信将依赖于名称空间中可使用设备及相关的IP地址、路由和iptables规则等网络配置。Linux内核支持多种类型的虚拟网络设备,例如Veth、Bridge、802.q VLAN device和TAP等,并支持按需创建虚拟网络设备并组合出多样化的功能和网络拓扑。Docker借助虚拟网络设备、网络驱动、IPAM(IP地址分配)、路由和iptables等实现了桥接模式、主机模式、容器模式和无网络等几种单主机网络模型。图1-3显示了Docker默认的桥接网络拓扑。
对于跨主机的容器间互联互通需求,Docker默认通过端口映射(DNAT)进行,这需要将容器端口暴露给宿主机,且将服务端的容器地址设置为对客户端不可见。然而,生产环境中部署、运行分布式应用对于构建跨主机容器网络几乎是必然需求。目前,封包(Overlay Network)和路由(routing network)是常见的跨主机容器间通信的解决方案,前一种类型中常见的协议有VXLAN、IPIP隧道和GRE等。2015年3月,Docker收购了SDN初创公司SocketPlane,并由此创建了CNM(Container Network Model)及其由Docker中剥离出来的单独网络实现libnetwork,该实现使用驱动程序/插件模型支持许多基础网络技术,如IP VLAN、MAC VLAN、Overlay、Bridge和Host等。
1.1.2 OCI与容器运行时
OCI(Open Container Initiative,开放工业标准)的容器运行时规范设定的标准定义了容器运行状态的描述,以及运行时需要提供的容器管理功能,例如创建、删除和查看等操作。容器运行时规范不受上层结构绑定,不受限于任何特定操作系统、硬件、CPU架构或公有云等,从而允许任何人遵循该标准开发应用容器技术。OCI项目启动后,Docker公司将2014年开源的libcontainer项目移交至OCI组织并进化为runC项目,成为第一个且目前接受度最广泛的遵循OCI规范的容器运行时实现。
为了兼容OCI规范,Docker项目自身也做了架构调整,自1.11.0版本起,Docker引擎由一个单一组件拆分成了Docker Engine(docker-daemon)、containerd、containerd-shim和runC等4个独立的项目,并把containerd捐赠给了CNCF。
containerd是一个守护进程,它几乎囊括了容器运行时所需要的容器创建、启动、停止、中止、信号处理和删除,以及镜像管理(镜像和元信息等)等所有功能,并通过gRPC向上层调用者公开其API,可被兼容的任何上层系统调用,例如Docker Engine或Kubernetes等容器编排系统,并由该类系统负责镜像构建、存储卷管理和日志等其他功能。
然而,containerd只是一个高级别的容器运行时,并不负责具体的容器管理功能,它还需要向下调用类似runC一类的低级别容器运行时实现此类任务。containerd又为其自身和低级别的运行时(默认为runC)之间添加了一个名为containerd-shim的中间层,以支持多种不同的OCI运行时,并能够将容器运行为无守护进程模式。这意味着,每启动一个容器,containerd都会创建一个新的containerd-shim进程来启动一个runC进程,并能够在容器启动后结束该runC进程。Docker项目组件架构与运行容器的方式如图1-4所示。
近年来,出于各种设计目标的容器运行时项目越来越多,较主流的有CRI-O、Podman和Kata Containers等。CRI-O是一款类似于containerd的高级运行时,在底层同样需要调用低级运行时负责具体的容器管理任务,支持与OCI兼容的运行时(目前使用runC)。它为Kubernetes CRI(容器运行时接口)提供了轻量级的容器运行方案,核心目标是在kubelet和OCI运行时之间提供一个黏合层,支持从Kubernetes直接运行容器(无须再依赖任何其他代码或工具),以取代有着较长集成链路的Docker容器引擎。
提示
关于更多Kubernetes CRI和kubelet相关的话题,本书后续会有相应的介绍。
由Red Hat主要推动和维护的Podman项目则是另一款兼容OCI规范的高级容器运行时,它起初是CRI-O项目的一部分,后来单独分离成为libpod项目,Podman是相关的命令行管理工具。Podman在管理容器时使用无守护进程模型,它直接通过runC容器运行时进程(而非守护程序)与镜像Registry、容器和镜像存储以及Linux内核直接交互。它支持管理容器的整个生态系统,包括Pod(Kubernetes引入的组件,由关系紧密的容器组成的容器集)、容器、容器镜像,以及使用libpod库的存储卷。Podman用于构建镜像的功能则交由Buildah项目完成,支持基于Dockerfile构建镜像的podman build命令仅包含该项目的一个子集,使用bash脚本构建镜像是该项目更大的亮点。镜像Registry也有一个专用的项目Skopeo,支持Docker镜像和OCI镜像的签名、存储及推拉操作。
提示
Podman的命令格式与Docker命令几乎完全兼容,用户可直接迁移Docker命令行至Podman上。
与最初的Docker项目一样,CoreOS开发的rkt同时提供了高级容器运行时和低级容器运行时的功能。例如,它支持构建容器镜像、于本地存储库中获取和管理镜像,并通过命令将之启动为容器等。不过,它没有守护进程和远程可用的API。为了同Docker竞争,rkt还创建了应用程序容器(appc)标准以替代OCI,但未获得广泛采用。其他常见的容器运行时还有frakti和LXC等。
Docker和rkt都是经典容器技术的实现,同一主机上的各容器共享内核,轻量、快速,但也因隔离性差、内核版本绑定(容器应用受限于容器引擎宿主机的内核版本)以及不支持异构的硬件平台等原因为人诟病。所以,基于虚拟化或者独立内核的安全容器项目悄然兴起,2017年底,由Intel Clear Container和Hyper.sh RunV项目合并而来的Kata Containers就是代表之一。Kata Containers在专用的精简内核中运行容器,提供网络、I/O和内存的隔离,并可以通过虚拟化VT扩展进行硬件强制隔离,因而更像一个传统的、精简版的或轻量化的虚拟机,如图1-5所示。但Kata Containers又是一个容器技术,支持OCI规范和Kubernetes CRI接口,并能够提供与标准Linux容器一致的性能。Kata Containers致力于通过轻量级虚拟机来构建安全的容器运行时,因而也更适用于多租户公有云,以及对项目隔离有着较高标准的私有云场景。
1.1.3 为什么需要容器编排系统
Docker本身非常适合管理单个容器,若运行的是构建于有限几个或十几个容器上的应用程序,则可以仅在Docker引擎上自主运行,部署和管理这些容器并不会遇到太大的困难。然而,对于包含成百上千个容器的企业级应用程序来说,这种管理将变得极其复杂,甚至无法实现。
容器编排是指自动化容器应用的部署、管理、扩展和联网的一系列管控操作,能够控制和自动化许多任务,包括调度和部署容器、在容器之间分配资源、扩缩容器应用规模、在主机不可用或资源不足时将容器从一台主机迁移到其他主机、负载均衡以及监视容器和主机的运行状况等。
容器编排系统用于完成容器编排相关的任务。以Kubernetes、Mesos和Docker Swarm等为代表的这类工具通常需要用户在YAML或JSON格式的配置清单中描述应用程序的配置,以指示编排系统在何处检索容器镜像(私有仓库或者某外部仓库)、如何在容器之间建立网络、在何处存储日志以及如何挂载存储卷等。确定调度目标后,编排工具将根据预定规范管理容器的生命周期。
概括来说,容器编排系统能够为用户提供如下关键能力。
▪集群管理与基础设施抽象:将多个虚拟机或物理机构建成协同运行的集群,并将这些硬件基础设施抽象为一个统一的资源池。
▪资源分配和优化:基于配置清单中指定的资源需求与现实可用的资源量,利用成熟的调度算法合理调度工作负载。
▪应用部署:支持跨主机自动部署容器化应用,支持多版本并存、滚动更新和回滚等机制。
▪应用伸缩:支持应用实例规模的自动或手动伸缩。
▪应用隔离:支持为租户、项目或应用进行访问隔离。
▪服务可用性:利用状态监测和应用重构等机制确保服务始终健康运行。
Kubernetes、Mesos和Docker Swarm一度作为竞争对手在容器编排领域三分天下,但这一切在2017年发生了根本性的变化,因为在这一年发生了几个在容器生态发展史上具有里程碑式意义的重要事件。一是AWS、Azure和Alibaba Cloud都相继在其原有容器服务上新增了对Kubernetes的支持,甚至Docker官方也在2017年10月宣布同时支持Swarm和Kubernetes编排系统。二是rkt容器派系的CoreOS舍弃掉自己的调度工具Fleet,将商用平台Tectonic的重心转移至Kubernetes。三是Mesos也于2017年9月宣布了对Kubernetes的支持,其平台用户可以安装、扩展和升级多个生产级的Kubernetes集群。四是Rancher Labs推出了2.0版本的容器管理平台并宣布将全部业务集中于Kubernetes,放弃了其多年内置的容器编排系统Cattle。这种局面显然意味着Kubernetes已经成为容器编排领域事实上的标准。后来,Twitter、CNCF、阿里巴巴、微软、思科等公司与组织纷纷支持Kubernetes。
以上种种迹象表明,Kubernetes已成为广受认可的基础设施领域工业标准,其近两三年的发展状态也不断验证着Urs Hölzle曾经的断言:无论是公有云、私有云抑或混合云,Kubernetes将作为一个为任何应用、任何环境提供容器管理的框架而无处不在。