5.2 Kubernetes
Kubernetes 是现今最主流的容器编排技术,它已成为行业事实标准。本节将会对它的设计理念与特性、运行架构和API对象等进行介绍,让读者了解如何通过Kubernetes来实现无状态应用和有状态应用。
5.2.1 设计理念与特性
Kubernetes最大的价值在于其提倡的声明式API和以此为基础的控制器模式。Kubernetes为使用者提供了API扩展能力和良好的API编程范式,从而催生出一种完全基于Kubernetes API构建的上层应用服务生态,这样的理念使Kubernetes具备了非常好的扩展性。具体来说,其设计理念如下。
1.一切皆为对象
在Kubernetes中,所有的信息都是以API对象的形式存储在etcd中的,所有的操作都是对API对象的操作。同时Kubernetes通过使用一种API对象(如Deployment)来管理另一种API对象(Pod)的方法,全面使用对象实现业务建模。
2.声明式API
声明式API是指对所有API的调用都是通过配置文件完成的。传统的模式是用镜像交付,镜像最大限度地把我们需要的运行时环境和应用可执行文件打包在一起,在各种环境中都能完美地运行。但除镜像之外,还需要配置启动参数、分布式应用的组网、服务发现等。Kubernetes的声明式 API 将所有这些配置都放到声明文件中,实现了真正的 Application/Infrastructure as Code。它使用一个YAML文件来表达应用的最终运行状态,并且自动对应用进行运维和管理。
所谓“声明式”,是指只需要提交一个定义好的API对象来“声明”所期望的状态。同时声明式 API 允许有多个 API 写端,以 PATCH 的方式对 API 对象进行修改,而无须关心某个YAML文件的内容。有了上述两种能力,Kubernetes才可以实现对API对象的增、删、改、查,在完全无须外界干预的情况下,完成对实际状态和期望状态的调谐(reconcile)过程。
3.切面式管理
Kubernetes 通过控制器模式(controller pattern)实现切面式管理及职责划分。Kubernetes的控制器可以对任何一种API对象实现全生命周期管理,控制器循环地对比实际状态与期望状态,执行响应操作,从而确保实际状态趋向期望状态。在Kubernetes中,响应操作也被叫作“调谐”。在具体实现中,实际状态往往来自Kubernetes集群本身,而期望状态一般由用户定义。控制器模式真正把Kubernetes从简单的容器部署工具提升为容器生命周期管理工具。Kubernetes通过声明式的方式来描述应用的“终态”,然后Kubernetes通过控制器不断地将整个系统的实际状态向这个“终态”逼近并且达成一致。
此外,控制器模式也实现了“面向切面编程”模式,不同的控制器负责不同方面的管理,也就是应用的不同切面,从而保证了不同切面之间相互协调、共同协作。这个思想不仅是Kubernetes里每一个组件的“设计模板”,也是Kubernetes能够将开发者紧紧团结到自己身边的重要因素。
4.无隐藏API
Kubernetes 暴露了所有底层 API,所以它具有以其自身为底座进行二次建设的能力。简单地说,如果是Kubernetes已有的能力,则可以直接使用;如果Kubernetes底座的能力不够,则应该补充和加强Kubernetes的能力,体现为实现新的控制器;如果Kubernetes的抽象不够,比如对于某些复杂场景,现有的CRD(Customer Resource Defination)不适用或不够用,则应该定义新的抽象,体现为添加新的CRD。将加固Kubernetes底座(controller)和扩展Kubernetes抽象(CRD)结合起来,就可以实现应用功能向基础设施的下沉。在通过这种方式打造新的云原生基础设施后,再在这些云原生基础设施的基础上生产新的云原生产品。
基于上述设计理念,Kubernetes具有如下一些专属特性。
Kubernetes服务发现和负载均衡。
自动调度。
存储编排。
应用的自恢复。
应用版本更新及回滚。
配置管理。
批处理任务。
弹性伸缩。
5.2.2 运行架构
Kubernetes的运行架构由Master和Node(节点)组成,它们分别扮演着控制节点和计算节点的角色,如图5-3所示。
图 5-3
1.Master
Master由4个紧密协作的独立组件组合而成,它们分别是数据库etcd、API Server(即负责API服务的kube-apiserver)、Scheduler(即负责调度的kube-scheduler)和Controller Manager (即负责容器编排的kube-controller-manager)。整个集群的持久化数据,则由kube-apiserver处理后保存在etcd中。
(1)etcd
etcd是用来存储所有Kubernetes的集群状态的,它除了具备状态存储的功能,还有事件监听和订阅、Leader选举的功能。所谓事件监听和订阅,是指各个组件之间的通信等并不是通过相互调用API来完成的,而是把状态写入etcd中(相当于写入一个消息),其他组件通过监听etcd中状态的变化(相当于订阅消息)进行后续的处理,并把更新的数据写入etcd中。所谓Leader选举,是指其他组件(比如Scheduler)为了实现高可用性,通常会有3个副本,etcd需要从多个实例中选举出一个做Master,其他的都是Standby(旁站)。
(2)API Server
etcd 是整个 Kubernetes 的核心,所有组件之间的通信都需要通过 etcd。在以前版本的Kubernetes中,其他组件直接通过WATCH机制调用etcd进行通信,从而造成etcd的压力过大;而在现在版本的Kubernetes中,各个组件并不是直接访问etcd的,而是访问一个代理API Server。API Server通过标准的RESTful API,重新封装了对etcd接口的调用。此外,这个代理还实现了一些附加功能,比如身份认证、缓存等。API Server提供了操作Kubernetes各种对象的RESTful接口,如图5-4所示。无论是客户端命令行工具kubectl,还是Master中的Scheduler、Controller Manager,抑或是Node中的kubelet,都要通过API Server实现对etcd的访问,这就是Kubernetes的通信机制。
图 5-4
API Server可以通过Web端、客户端命令行工具kubectl及用户界面等方式由用户进行访问,其内部也有认证(authentication)、授权(autorization)、访问控制(addmission control)等功能,如图5-5所示。
图 5-5
(3)Scheduler
Master中的Scheduler负责资源调度,Controller Manager会将任务对资源的要求写入etcd中,当 Scheduler 监听到有新的资源需要调度(新的 Pod)时,它就会根据整个集群的状态把Pod分配到具体的节点上。具体流程如下:
① 从等待执行的Pod队列中取出Pod。
② 通过预选(predicate)机制筛选出符合要求的节点,例如不能满负载、无法通信等。
③ 通过评级(priority)机制对满足需求的节点打分,选择得分最高的节点来进行调度。
④ 最后完成绑定过程,把新建的Pod调度到选中的节点上并更新缓存。
(4)Controller Manager
控制器是Kubernetes实现资源管理的实际组件。当一个任务请求Kubernetes创建各类资源时,比如Deployment、DeamonSet或者Job,将任务请求发送给Kubernetes之后都是由控制器来处理的。每一种类型的资源都对应一个控制器,比如Deployment对应Deployment控制器,DaemonSet对应DaemonSet控制器。图5-6展示了Kubernetes中默认的控制器列表,这个列表可以随着开发者对Kubernetes的深度使用而不断扩大,这也是Kubernetes成长性的重要体现。
图 5-6
(5)kubectl
kubectl是一个命令行工具,它会调用API Server发送请求,或者将更新状态写入etcd中,或者查询etcd中的更新状态。
2.Node
(1)kubelet
在Node(节点)上最核心的是kubelet组件,其负责与容器运行时(比如Docker)协同交互。而这个交互所依赖的是一个称作CRI(Container Runtime Interface)的远程调用接口,该接口定义了容器运行时的各项内容和核心操作,比如启动一个容器需要的所有参数。这也是Kubernetes并不关心容器运行时是什么(可以是Docker,也可以是Rocket)的原因,只需确保容器运行时符合OCI规范,就可以通过实现CRI接入Kubernetes中。而具体的容器运行时通过OCI规范与底层的Linux进行交互,将CRI请求翻译成对Linux的调用(操作Linux namespace和cgroup等)。此外,kubelet还通过gRPC协议与一个名为device plugin的插件进行交互,这个插件是Kubernetes用来管理GPU等宿主机物理设备的主要组件。总体来说,kubelet的主要功能是调用网络插件CNI、存储插件CSI为容器配置网络和持久化存储,以及使用device plugin等插件。
具体来说,当Master中的Scheduler将新建的Pod绑定到一个节点时,Kubernetes就需要按照 Pod 的定义在节点上创建该 Pod。完成创建这一系列操作即通过 kubelet 来实现,kubelet本身也是按照控制器模式来工作的——它是一个Agent,运行在每一个节点上,它会监听Master API Server中的Pod信息,如果发现分配给其所在节点的Pod有需要运行的,就在节点上运行相应的Pod,并把状态更新回etcd中。注意,目前负责节点监控的cAdvisor已经被集成到kubelet中。kubelet实际的工作原理,可以用图5-7来表示清楚。kubelet的工作核心就是一个控制循环——SyncLoop(即图5-7中的大圆圈)。而驱动这个控制循环运行的事件有4种:
Pod更新事件。
Pod生命周期变化。
kubelet本身设置的执行周期。
定时的清理事件。
图 5-7
(2)kube-proxy
kube-proxy负责从API Server获取所有的Server信息,并根据Server信息为Pod创建代理服务,实现从Server到Pod的请求路由和转发,进而实现Kubernetes层级的虚拟转发网络。具体来说,就是实现集群内的客户端Pod访问Service,或者集群外的主机通过NodePort等方式访问Service。在较早版本的Kubernetes中,kube-proxy默认使用的是iptables模式,通过各个节点上的iptables规则来实现Service的负载均衡,但是随着Service数量的增加,iptables模式由于线性查找匹配、全量更新等特点,其性能会显著下降。从Kubernetes 1.8开始,kube-proxy引入了IPVS模式,IPVS模式与iptables同样基于netfilter,但是其采用hash表,因此当Service数量达到一定规模时,hash表的查找速度优势就会体现出来,从而提高了Service的服务性能。
5.2.4 API对象
这里以一个简单的应用为例,具体说明Kubernetes是如何通过其API对象来实现应用的。Kubernetes首先从基本的容器出发,通过Pod实现了容器间的紧密协作,通过Deployment实现了Pod的多实例管理;然后通过Service为一组相同的Pod提供固定的VIP和端口,并以负载均衡的方式访问这些Pod。接下来,Kubernetes又提供了Secret对象在etcd中安全地保存键值对数据,并且在指定的Pod中自动将Secret数据以volume(卷)的形式挂载到容器上。为了适应应用的不同形态,Kubernetes还定义了新的基于Pod改进的对象,如图5-8所示。其中,Job对象用来描述一次性运行的 Pod;DaemonSet 对象用来描述每台宿主机上必须且只能运行一个副本的守护进程服务;CronJob对象用来描述定时任务等。
图 5-8
1.Pod
Pod是Kubernetes中最基础的对象,它是一个或者多个容器的组合,每个容器都负责应用的不同特性操作。Pod里的所有容器共享同一个network namespace,并且可以声明共享同一个存储卷,如图5-9所示。
图 5-9
Pod是Kubernetes中的原子调度单位,Kubernetes是统一按照Pod而非容器的资源需求进行计算的。凡是与容器的Linux namespace相关的属性一定是Pod级别的,这样就可以让容器尽量共享Linux namespace,仅保留必要的隔离和限制能力。
Pod的实现需要使用一个中间容器,这个中间容器被称作Infra容器。在Pod中,Infra容器永远都是第一个被创建的容器,而用户定义的其他容器都是通过join network namespace的方式与 Infra 容器关联在一起的。Infra容器占用极少的资源,所以它使用的是一个非常特殊的镜像——k8s.gcr.io/pause。该镜像是一个用汇编语言编写的、永远处于“暂停”状态的容器,其解压缩后的大小也只有100~200KB。
Pod网络的管理分为两步。
① 构建网络容器,包括创建网络容器、设置网络设备、设置设备IP地址和路由信息等。
② 启动业务容器,逐步启动业务容器并与网络容器共享 network namespace。比如,对于Pod里的容器A和容器B:
它们可以直接使用localhost进行通信。
它们看到的网络设备与Infra容器看到的完全一样。
一个Pod只有一个IP地址,也就是这个Pod的network namespace对应的IP地址。
其他所有网络资源都是一个Pod一份,并且被该Pod中的所有容器共享。
Pod的生命周期只与Infra容器一致,而与容器A和容器B无关。
2.存储
Kubernetes对数据的存储分为:在etcd中存储数据(etcd-based)和在volume中存储数据(volume-based)。
(1)etcd-based数据
Kubernetes中大部分涉及配置的数据都被存储在etcd中。Kubernetes的etcd-based数据既不用来存放容器里的数据,也不用来进行容器和宿主机之间的数据交换,它的作用是为容器提供预先定义好的配置参数等数据。Pod 中的容器可以通过环境变量或挂载 volume 的方式访问到etcd-based数据中的内容。与一般volume的最大区别在于,etcd-based volume不真实存在,它只是etcd中的一组数据,通过环境变量或volume的形式为Pod中的容器提供数据。将数据交给Kubernetes保存,而Pod通过volume的形式使用这些数据。在Kubernetes中,这样的数据对象是以key-value形式保存的,其中key是对象名,value是对象值。当对象以volume的形式被挂载到Pod上后,key对应文件名,value对应文件内容。一旦etcd里的数据被更新,这些被挂载的volume里的文件内容同样也会被实时更新。
总结一下,Kubernetes 的 etcd-based 数据包括 ConfigMap、Secret、Download API 和ServiceAccountToken,它们的核心功能是将配置信息通过API资源保存到etcd中,或者将配置信息以文件的形式挂载到Pod上,或者将配置信息以环境变量的形式注入Pod的容器中。
ConfigMap:它用于保存 Kubernetes 中的配置文件对象,可以保存单个属性、整个配置文件或者二进制文件。
Secret:它用于保存Pod想要访问的加密数据,然后Pod中的容器通过挂载volume的方式就可以访问到这些Secret里保存的信息了。不同于ConfigMap,Secret对象要求保存的数据必须经过Base64转码,以免出现明文密码的安全隐患。在volume被挂载之后,数据会经过反转码以明文的形式出现在容器中。
Download API:它让Pod中的容器能够直接通过Kubernetes的API Server,获取到这个Pod API对象的相关信息。
ServiceAccountToken:Kubernetes会为ServiceAccount自动创建并分配一个Secret对象,即ServiceAcount里的secrets。这个Secret与ServiceAccount相对应,是用来与API Server进行交互的授权文件,被称作ServiceAccountToken。它的内容一般是证书或者密码,以Secret对象的形式保存在etcd中。每个Pod都会默认绑定一个ServiceAccount,并为其自动声明一个类型为Secret、名为default-token-xxxx的volume,然后自动挂载到每个容器的/var/run/secrets/k8s.io/serviceaccount上,这正是默认ServiceAccount对应的ServiceAccountToken。应用程序只要直接加载该目录下的授权文件,就可以访问并操作Kubernetes的API Server了。
(2)volume-based数据
在Kubernetes中,volume分为本地volume和持久化volume,二者在存储数据时的机制是不同的。下面分别进行介绍。
a.本地volume
本地volume是指在创建Pod时临时创建的volume,其生命周期与Pod绑定。Pod中的容器重启后,数据依然存在,而当Pod被删除后,原则上本地volume也会被删除,但具体实现视volume plugin类型而定。Kubernetes的本地volume方案就是将一台宿主机上的目录与一个容器里的目录绑定挂载在一起,其实现是基于Docker的volume方案。
hostPath:将宿主机的文件系统中已经存在的某个目录挂载到Pod上,于是Pod中的容器可以使用宿主机上的文件,或者可以采集宿主机上的数据。
emptyDir:当 Pod 被调度到一个节点上时,在这个节点上会自动创建一个临时目录,并将其挂载到 Pod 上。当将 Pod 从节点上删除时,该目录也一并被删除,可以使用volume tmpfs类型,此类volume用于保存Pod中的临时数据。
rbd:调用Ceph创建存储块,并把它挂载到Pod上。
b.持久化volume
所谓持久化volume,就是指宿主机上的目录具有持久性,即该目录中的内容既不会因为容器的删除而被清理掉,也不会与当前宿主机绑定。当容器重启或者在其他节点上重建之后,仍然能够通过挂载这个volume访问到这些内容。
持久化 volume 的实现往往依赖于一个远程存储服务,比如远程文件存储(如 NFS、GlusterFS)、远程块存储(如公有云提供的远程磁盘)等。Kubernetes使用这些存储服务为Pod准备一个持久化的宿主机目录,以供将来进行绑定挂载时使用。而所谓持久化,指的是容器在这个目录中写入的文件都会被保存在远程存储中,从而使得这个目录具有持久性。
PersistentVolumeClaim(PVC,持久化存储数据卷声明):它描述的是Pod所希望使用的持久化存储的属性,比如volume大小、可读/写权限等。从Pod角度声明需要使用的volume,Kubernetes会自动为PVC寻找合适的PV进行绑定。
PersistentVolume(PV,持久化存储数据卷):它是持久化volume在Kubernetes中对应的 API 资源。这个 API 对象定义了一个持久化存储在宿主机上的目录,比如一个NFS的挂载目录,它与PVC的关系是一一绑定的。
StorageClass:StorageClass是创建PV的模板(类),其包括PV的属性(如存储类型、volume大小等)、创建这种PV需要用到的存储插件(如Ceph等)。有了这些信息,Kubernetes 就能够根据 PVC 找到一个对应的 StorageClass,然后调用该 StorageClass声明的存储插件创建所需的PV(存储插件需要支持dynamic provisioning)。
PVC可以被理解为持久化存储的“接口”,它提供了对某种持久化存储的描述,但不提供具体的实现,持久化存储的实现则由PV负责完成。这样做的好处是,应用开发者只需要跟PVC这个“接口”打交道,而不必关心具体的实现是NFS还是Ceph。PV与PVC的管理可分为静态和动态两类,其中在静态下由管理员手动创建PV,而在动态下则通过Kubernetes提供的自动创建PV机制——StorageClass根据PVC的需求动态创建PV。整个Kubernetes的存储分三个步骤演进:本地volume、静态PV、动态PV,如图5-10所示。
图 5-10
3.无状态workload
Kubernetes 中的对象有很多,业界通常将这些对象统称为 workload。这里主要介绍两种workload,分别是无状态workload和有状态workload。在Kubernetes的无状态workload中,最典型的就是ReplicaSet和Deployment。
(1)ReplicaSet和Deployment
Kubernetes ReplicaSet是由Pod副本数的定义和一个Pod模板组成的,负责通过控制器模式保证系统中 Pod 的个数永远等于指定的个数。这也正是 ReplicaSet 只允许设置容器的restartPolicy=Always 的主要原因——只有在容器能保证自己始终是 Running 状态的前提下,ReplicaSet调整Pod的个数才有意义;否则,即使Pod恢复了,但Pod中的容器却不能自启动,Pod也什么作用都没有。
Deployment 实现了 Kubernetes 中一个非常重要的功能:Pod 的水平扩展/收缩(horizontal scaling out/in)。Deployment控制器实际操纵的是ReplicaSet对象,而不是Pod对象。Deployment要实现Pod的水平扩展/收缩,只需要修改其所控制的ReplicaSet的Pod副本数就可以了,如图5-11所示。
图 5-11
Kubernetes的另一个功能“滚动更新”也是通过Deployment实现的,滚动更新是指在一个集群中运行的多个 Pod 版本交替逐一升级的过程。Deployment 滚动更新会创建一个新的ReplicaSet,这个新的ReplicaSet的初始Pod副本数是0,然后交替逐步替代现有ReplicaSet的旧Pod,如图5-12所示。
图 5-12
Deployment 的设计实际上实现了对无状态应用的抽象,它是一个两层控制器,先通过ReplicaSet的个数来描述应用的版本,再通过ReplicaSet的replicas值来保证Pod副本数。
(2)DaemonSet
一个 DaemonSet 对象能确保其创建的 Pod 在集群中的每一个(或指定)节点上都运行一个副本。如果在集群中动态加入了新的节点,DaemonSet中的Pod也会被添加到新加入的节点上运行。删除一个DaemonSet也会级联删除其创建的所有Pod。下面是一些典型的DaemonSet的使用场景。
在每个节点上都运行一个集群存储服务,例如glusterd、ceph。
在每个节点上都运行一个日志收集服务,例如fluentd、logstash。
在每个节点上都运行一个节点监控服务,例如 Prometheus Node Exporte、collectd、Datadog Agent、New Relic Agent或Ganglia gmond。
由DaemonSet创建的 Pod具有如下特征:
该Pod运行在Kubernetes集群的每一个节点上。
每个节点上都只有一个这样的Pod实例。
当有新的节点加入集群时,在新的节点上会自动创建出该 Pod;而在旧的节点被删除后,它上面的Pod也会被相应地回收。
(3)Job&CronJob
在Kubernetes中,Job和CronJob用于管理离线的、定时的无状态应用。Job负责批量处理短暂的一次性任务(short lived one-off task),即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束。CronJob即定时任务,其类似于Linux系统的crontab,在指定的时间周期内运行指定的任务。在Kubernetes 1.5中,使用CronJob需要开启batch/v2alpha1 API,即设置--runtime-config=batch/v2alpha1。其他相关内容,有兴趣的读者可以查阅更多资料。
通过上文介绍,我们知道了Kubernetes的ReplicaSet、Deployment、DaemonSet对象可以方便地实现无状态应用的弹性伸缩和滚动更新,而无状态应用发展的下一步就是Serverless。关于Serverless的内容,会在后文中进行更详细的描述。
4.有状态workload
有状态workload则主要有StatefulSet和Operator两种,下面分别进行介绍。
(1)StatefulSet
StatefulSet用于实现有状态应用,它把应用的状态抽象为三类。
拓扑状态:应用的多个实例之间不是完全对等的关系,有些应用实例必须按照某种顺序启动,比如应用的主节点A要先于从节点B启动。如果把A和B两个Pod删除,它们再次被创建出来后,也必须严格按照这种顺序启动才行。
网络状态:重启后的Pod的网络标识必须和原来Pod的网络标识一样,这样才能用原先的访问方法访问到这个新的Pod。
存储状态:应用的多个实例分别绑定了不同的存储数据,对于这些应用实例来说,Pod A读取到一份数据,即使在Pod A重启之后再次读取,也应该是同一份数据。
StatefulSet 的核心功能就是通过某种方式记录这些状态,然后在重新创建 Pod 后,能够为新的Pod恢复这些状态。恢复这些状态的具体方式如下。
拓扑状态:Pod的拓扑状态StatefulSet通过Pod命名解决,以0、1、2为后缀,并且按照次序逐一创建。StatefulSet 的每个 Pod 实例的名字里都携带了一个唯一且固定的编号,这个编号的顺序决定了Pod的拓扑关系,在启动Pod时也会按照编号逐一启动。
网络状态:通过Headless Service,直接以DNS记录的方式解析出被代理Pod的IP地址,然后把该Pod的IP地址注册给“my-pod.my-svc.my-namespace.svc.cluster.local”,这样就可以直接通过Pod的域名访问Pod,并且可以实时更新Pod中的主机名。
存储状态:使用与Pod相同的命名规则为Pod的每个PVC命名,从而确保PVC与Pod的一一绑定关系。当一个Pod被删除后,其对应的PVC和PV会被保留下来,所以当这个Pod被重新创建后,Kubernetes会根据命名规则为它找到同样编号的PVC,挂载这个PVC对应的PV,从而获取到以前保存在volume里的数据。
(2)Operator
不同workload之间的依赖问题,workload本身无法解决,而Operator正好可以解决该问题。
通过CRD(Custom Resource Definition),Kubernetes允许用户在Kubernetes中添加一种与Pod、节点类似的新的API资源类型,即自定义API资源(或自定义API对象)。也就是说,通过CRD可以在Kubernetes中注册一种新的资源类型。
对新的 CRD 资源的增加、删除、更新的业务逻辑,实际上就是为自定义 API 对象编写一个自定义控制器(Custom Controller)。“声明式API”并不像“命令式API”那样有着明显的执行逻辑,基于声明式API的业务功能实现,往往需要通过控制器模式来监视API对象的变化,然后以此来决定实际要执行的具体工作。自定义控制器获得的资源对象,正是API Server里保存的期望状态,而实际状态则通过实际业务的API获得。自定义控制器的无限循环,就是通过对比期望状态和实际状态的差异完成一次调谐的过程。
Operator的工作原理是利用Kubernetes的自定义API资源CRD,来描述想要部署的有状态应用,具体的部署和运维工作需要在Operator的自定义控制器里,根据自定义API对象的变化来完成。Operator其实就是一个Deployment/Pod,它首先会自动化注册CRD,同时Operator本身就是该CRD的自定义控制器,用于对相应的自定义资源进行生命周期的监控和管理。然后对自定义资源实例的创建都会通过Operator这个自定义控制器进行操作,从而实现管理有状态应用的生命周期。
5.Service对象和 Ingress对象
Service对象和Ingress对象的主要作用是将Pod中的服务提供给外界。Service对象通过规则定义出由多个Pod对象组成的逻辑组合,以及访问这组Pod的策略,Ingress对象则描述了不同资源之间的路由规则。
下面将详细介绍这两个对象。
(1)Service对象
简单来说,Service对象就是工作节点上的一组规则,用于将到达Service对象IP地址的流量调度转发至相应Pod的IP地址和端口之上。一方面,Service克服了Pod IP地址的动态性,使其可以被接入;另一方面,Service为Pod提供了对外入口,帮助容器被外部访问。此外,Service还提供了服务发现机制,方便把Service名转换为Service的VIP(ClusterIP)地址,如图5-13所示。
图 5-13
(2)Ingress对象
Service对象解决了Kubernetes集群内不同Pod之间通信的问题,而Ingress对象的主要任务是将服务暴露到 Kubernetes 集群之外,根据自定义的服务访问策略动态路由给各个 Service对象,如图5-14所示。
图 5-14