2.1 Kubernetes的核心对象
API Server提供了RESTful风格的编程接口,其管理的资源是Kubernetes API中的端点,用于存储某种API对象的集合,例如,内置Pod资源是包含了所有Pod对象的集合。资源对象是用于表现集群状态的实体,常用于描述应于哪个节点进行容器化应用、需要为其配置什么资源以及应用程序的管理策略等,例如,重启、升级及容错机制。另外,一个对象也是一种“意向记录”——一旦创建,Kubernetes就需要一直确保对象始终存在。Pod、Deployment和Service等都是最常用的核心对象。
2.1.1 Pod资源对象
Pod资源对象是一种集合了一到多个应用容器、存储资源、专用IP及支撑容器运行的其他选项的逻辑组件,如图2-1所示。换言之,Pod代表着Kubernetes的部署单元及原子运行单元,即一个应用程序的单一运行实例,它通常由共享资源且关系紧密的一个或多个应用容器组成。
图2-1 Pod通常由一到多个共享网络和存储资源的容器组合而成
Kubernetes的网络模型要求其各Pod对象的IP地址位于同一网络平面内(同一IP网段),各Pod之间可使用其IP地址直接进行通信,无论它们运行于集群内的哪个工作节点之上,这些Pod对象都像是运行于同一局域网中的多个主机。
读者可以将每个Pod对象想象成一个逻辑主机,它类似于现实世界中的物理主机或VM(Virtual Machine),运行于同一个Pod对象中的多个进程也类似于物理机或VM上独立运行的进程。不过,Pod对象中的各进程均运行于彼此隔离的容器中,并于各容器间共享两种关键资源:网络和存储卷。
□网络(networking):每个Pod对象都会被分配一个集群内专用的IP地址,也称为Pod IP,同一Pod内部的所有容器共享Pod对象的Network和UTS名称空间,其中包括主机名、IP地址和端口等。因此,这些容器间的通信可以基于本地回环接口lo进行,而与Pod外的其他组件的通信则需要使用Service资源对象的ClusterIP及其相应的端口完成。
□存储卷(volume):用户可以为Pod对象配置一组“存储卷”资源,这些资源可以共享给其内部的所有容器使用,从而完成容器间数据的共享。存储卷还可以确保在容器终止后被重启,甚至是被删除后也能确保数据不会丢失,从而保证了生命周期内的Pod对象数据的持久化存储。
一个Pod对象代表某个应用程序的一个特定实例,如果需要扩展应用程序,则意味着为此应用程序同时创建多个Pod实例,每个实例均代表应用程序的一个运行的“副本”(replica)。这些副本化的Pod对象的创建和管理通常由另一组称之为“控制器”(Controller)的对象实现,例如,Deployment控制器对象。
创建Pod时,还可以使用Pod Preset对象为Pod注入特定的信息,如ConfigMap、Secret、存储卷、卷挂载和环境变量等。有了Pod Preset对象,Pod模板的创建者就无须为每个模板显式提供所有信息,因此,也就无须事先了解需要配置的每个应用的细节即可完成模板定义。这些内容将在后面的章节中予以介绍。
基于期望的目标状态和各节点的资源可用性,Master会将Pod对象调度至某选定的工作节点运行,工作节点于指向的镜像仓库(image registry)下载镜像,并于本地的容器运行时环境中启动容器。Master会将整个集群的状态保存于etcd中,并通过API Server共享给集群的各组件及客户端。
2.1.2 Controller
Kubernetes集群的设计中,Pod是有生命周期的对象。用户通过手工创建或由Controller(控制器)直接创建的Pod对象会被“调度器”(Scheduler)调度至集群中的某工作节点运行,待到容器应用进程运行结束之后正常终止,随后就会被删除。另外,节点资源耗尽或故障也会导致Pod对象被回收。
但Pod对象本身并不具有“自愈”功能,若是因为工作节点甚至是调度器自身导致了运行失败,那么它将会被删除;同样,资源耗尽或节点故障导致的回收操作也会删除相关的Pod对象。在设计上,Kubernetes使用“控制器”实现对一次性的(用后即弃)Pod对象的管理操作,例如,要确保部署的应用程序的Pod副本数量严格反映用户期望的数目,以及基于Pod模板来重建Pod对象等,从而实现Pod对象的扩缩容、滚动更新和自愈能力等。例如,某节点发生故障时,相关的控制器会将此节点上运行的Pod对象重新调度到其他节点进行重建。
控制器本身也是一种资源类型,它有着多种实现,其中与工作负载相关的实现如Replication Controller、Deployment、StatefulSet、DaemonSet、DaemonSet和Jobs等,也可统称它们为Pod控制器。如图2-2中的Deployment就是这类控制器的代表实现,是目前最常用的管理无状态应用的Pod控制器。
图2-2 Replication Controller
Pod控制器的定义通常由期望的副本数量、Pod模板和标签选择器(Label Selector)组成。Pod控制器会根据标签选择器对Pod对象的标签进行匹配检查,所有满足选择条件的Pod对象都将受控于当前控制器并计入其副本总数,并确保此数目能够精确反映期望的副本数。
需要注意的是,在实际的应用场景中,在接收到的请求流量负载显著低于或接近于已有Pod副本的整体承载能力时,用户需要手动修改Pod控制器中的期望副本数量以实现应用规模的扩容或缩容。不过,若集群中部署了HeapSter或Prometheus一类的资源指标监控附件时,用户还可以使用“HorizontalPodAutoscaler”(HPA)计算出合适的Pod副本数量,并自动修改Pod控制器中期望的副本数以实现应用规模的动态伸缩,提高集群资源利用率,如图2-3所示。
图2-3 Horizontal Pod Autoscaler
Kubernetes集群中的每个节点都运行着cAdvisor以收集容器及节点的CPU、内存及磁盘资源的利用率指标数据,这些统计数据由Heapster聚合后可通过API Server访问。HorizontalPodAutoscaler基于这些统计数据监控容器健康状态并做出扩展决策。
2.1.3 Service
尽管Pod对象可以拥有IP地址,但此地址无法确保在Pod对象重启或被重建后保持不变,这会为集群中的Pod应用间依赖关系的维护带来麻烦:前端Pod应用(依赖方)无法基于固定地址持续跟踪后端Pod应用(被依赖方)。于是,Service资源被用于在被访问的Pod对象中添加一个有着固定IP地址的中间层,客户端向此地址发起访问请求后由相关的Service资源调度并代理至后端的Pod对象。
换言之,Service是“微服务”的一种实现,事实上它是一种抽象:通过规则定义出由多个Pod对象组合而成的逻辑集合,并附带访问这组Pod对象的策略。Service对象挑选、关联Pod对象的方式同Pod控制器一样,都是要基于Label Selector进行定义,其示意图如图2-4所示。
图2-4 Service对象功能示意图
Service IP是一种虚拟IP,也称为Cluster IP,它专用于集群内通信,通常使用专用的地址段,如“10.96.0.0/12”网络,各Service对象的IP地址在此范围内由系统动态分配。
集群内的Pod对象可直接请求此类的Cluster IP,例如,图2-4中来自pod client的访问请求即可以Service的Cluster IP作为目标地址,但集群网络属于私有网络地址,它们仅在集群内部可达。将集群外部的访问流量引入集群内部的常用方法是通过节点网络进行,实现方法是通过工作节点的IP地址和某端口(NodePort)接入请求并将其代理至相应的Service对象的Cluster IP上的服务端口,而后由Service对象将请求代理至后端的Pod对象的Pod IP及应用程序监听的端口。因此,诸如图2-4中的External Clients这种来自集群外部的客户端无法直接请求此Service提供的服务,而是需要事先经由某一个工作节点(如Node Y)的IP地址进行,这类请求需要两次转发才能到达目标Pod对象,因此在通信效率上必然存在负面影响。
事实上,NodePort会部署于集群中的每一个节点,这就意味着,集群外部的客户端通过任何一个工作节点的IP地址来访问定义好的NodePort都可以到达相应的Service对象。此种场景中,如果存在集群外部的一个负载均衡器,即可将用户请求负载均衡至集群中的部分或者所有节点。这是一种称为“LoadBalancer”类型的Service,它通常是由Cloud Provider自动创建并提供的软件负载均衡器,不过,也可以是由管理员手工配置的诸如F5 Big-IP一类的硬件设备。
简单来说,Service主要有三种常用类型:第一种是仅用于集群内部通信的ClusterIP类型;第二种是接入集群外部请求的NodePort类型,它工作于每个节点的主机IP之上;第三种是LoadBalancer类型,它可以把外部请求负载均衡至多个Node的主机IP的NodePort之上。此三种类型中,每一种都以其前一种为基础才能实现,而且第三种类型中的LoadBalancer需要协同集群外部的组件才能实现,并且此外部组件并不接受Kubernetes的管理。
2.1.4 部署应用程序的主体过程
Docker容器技术使得部署应用程序从传统的安装、配置、启动应用程序的方式转为于容器引擎上基于镜像创建和运行容器,而Kubernetes又使得创建和运行容器的操作不必再关注其位置,并在一定程度上赋予了它动态扩缩容及自愈的能力,从而让用户从主机、系统及应用程序的维护工作中解脱出来。
用到某应用程序时,用户只需要向API Server请求创建一个Pod控制器,由控制器根据镜像等信息向API Server请求创建出一定数量的Pod对象,并由Master之上的调度器指派至选定的工作节点以运行容器化应用。此外,用户一般还需要创建一个具体的Service对象以便为这些Pod对象建立起一个固定的访问入口,从而使得其客户端能够通过其服务名称或ClusterIP进行访问,如图2-5所示。
图2-5 应用程序简单的部署示例
API Server的常用客户端程序是Kubernetes系统自带的命令行工具kubectl,它通过一众子命令用于实现集群及相关资源对象的管理操作,并支持直接命令式、命令式配置清单及声明式配置清单等三种操作方式,特性丰富且功能强大。而需作为集群附件额外部署的Dashboard则提供了基于Web界面的图形客户端,它是一个通用目的管理工具,与Kubernetes紧密集成,支持多级别用户授权,能在一定程度上替代kubectl的大多数操作。
本章后面的篇幅将介绍在部署完成的Kubernetes集群环境中如何快速部署如图2-5所示的示例应用程序,并简单说明如何完成对容器化应用的访问,以及如何进行应用规模的动态伸缩,并借此让读者了解kubectl命令的基本功能和用法。