3.2 对象类资源格式
Kubernetes API仅接受及响应JSON格式的数据(JSON对象),同时,为了便于使用,它也允许用户提供YAML格式的POST对象,但API Server需要事先自行将其转换为JSON格式后方能提交。API Server接受和返回的所有JSON对象都遵循同一个模式,它们都具有kind和apiVersion字段,用于标识对象所属的资源类型、API群组及相关的版本。
进一步地,大多数的对象或列表类型的资源还需要具有三个嵌套型的字段metadata、spec和status。其中metadata字段为资源提供元数据信息,如名称、隶属的名称空间和标签等;spec则用于定义用户期望的状态,不同的资源类型,其状态的意义也各有不同,例如Pod资源最为核心的功能在于运行容器;而status则记录着活动对象的当前状态信息,它由Kubernetes系统自行维护,对用户来说为只读字段。
每个资源通常仅接受并返回单一类型的数据,而一种类型可以被多个反映特定用例的资源所接受或返回。例如对于Pod类型的资源来说,用户可创建、更新或删除Pod对象,然而,每个Pod对象的metadata、spec和status字段的值却又是各自独立的对象型数据,它们可被单独操作,尤其是status对象,是由Kubernetes系统单独进行自动更新,而不能由用户手动操作它。
3.2.1 资源配置清单
3.1节中曾使用curl命令通过代理的方式于API Server上请求到了kube-system这个Namespace活动对象的状态信息,事实上,用户也可以直接使用“kubectl get TYPE/NAME-o yaml”命令获取任何一个对象的YAML格式的配置清单,或者使用“kubectl get TYPE/NAME -o json”命令获取JSON格式的配置清单。例如,可使用下面的命令获取kube-system的状态:
~]$ kubectl get namespace kube-system -o yaml apiVersion: v1 kind: Namespace metadata: creationTimestamp: 2018-08-11T02:33:23Z name: kube-system resourceVersion: "33" selfLink: /api/v1/namespaces/kube-system uid: eb6bf659-9d0e-11e8-bf0d-000c29ab0f5b spec: finalizers: - kubernetes status: phase: Active
除了极少数的资源之外,Kubernetes系统上的绝大多数资源都是由其使用者所创建的。创建时,需要以与上述输出结果中类似的方式以YAML或JSON序列化方案定义资源的相关配置数据,即用户期望的目标状态,而后再由Kubernetes的底层组件确保活动对象的运行时状态与用户提供的配置清单中定义的状态无限接近。因此,资源的创建要通过用户提供的资源配置清单来进行,其格式类似于kubectl get命令获取到的YAML或JSON形式的输出结果。不过,status字段对用户来说为只读字段,它由Kubernetes集群自动维护。例如,下面就是一个创建Namespace资源时提供的资源配置清单示例,它仅提供了几个必要的字段:
apiVersion: v1 kind: Namespace metadata: name: dev spec: finalizers: - kubernetes
将如上所述配置清单中的内容保存于文件中,使用“kubectl create -f /PATH/TO/FILE”命令即可将其创建到集群中。创建完成后查看其YAML或JSON格式的输出结果,可以看到Kubernetes会补全其大部分的字段,并提供相应的数据。事实上,Kubernetes的大多数资源都能够以类似的方式进行创建和查看,而且它们几乎都遵循类似的组织结构,下面的命令显示了第2章中使用kubectl run命令创建的Deployment资源对象myapp的状态信息:
~]$ kubectl get deployment myapp -o yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: …… name: myapp spec: replicas: 3 selector: matchLabels: run: myapp …… status: ……
为了节约篇幅,上面的输出结果省去了大部分内容,仅保留了其主体结构。从命令结果可以看出,它也遵循Kubernetes API标准的资源组织格式,由apiVersion、kind、metadata、spec和status五个核心字段组成,只是spec字段中嵌套的内容与Namespace资源几乎完全不同。
事实上,对几乎所有的资源来说,apiVersion、kind和metadata字段的功能基本上都是相同的,但spec则用于资源的期望状态,而资源之所以存在类型上的不同,也在于它们的嵌套属性存在显著差别,它由用户定义和维护。而status字段则用于记录活动对象的当前状态,它要与用户在spec中定义的期望状态相同,或者正处于转换为与其相同的过程中。
3.2.2 metadata嵌套字段
metadata字段用于描述对象的属性信息,其内嵌多个字段用于定义资源的元数据,例如name和labels等,这些字段大体可分为必要字段和可选字段两大类。名称空间级别的资源的必选字段包括如下三项。
□namespace:指定当前对象隶属的名称空间,默认值为default。
□name:设定当前对象的名称,在其所属的名称空间的同一类型中必须唯一。
□uid:当前对象的唯一标识符,其唯一性仅发生在特定的时间段和名称空间中;此标识符主要是用于区别拥有同样名字的“已删除”和“重新创建”的同一个名称的对象。
可选字段通常是指由Kubernetes系统自行维护和设置,或者存在默认,或者本身允许使用空值等类型的字段,常用的有如下几个:
□labels:设定用于标识当前对象的标签,键值数据,常被用作挑选条件。
□annotations:非标识型键值数据,用来作为挑选条件,用于labels的补充。
□resourceVersion:当前对象的内部版本标识符,用于让客户端确定对象变动与否。
□generation:用于标识当前对象目标状态的代别。
□creationTimestamp:当前对象创建日期的时间戳。
□deletionTimestamp:当前对象删除日期的时间戳。
此外,用户通过配置清单创建资源时,通常仅需要给出必选字段,可选字段可按需指定,对于用户未明确定义的嵌套字段,则需要由一系列的finalizer组件自动予以填充。而用户需要对资源创建的目标资源对象进行强制校验,或者在修改时需要用到initializer组件完成,例如,为每个待创建的Pod对象添加一个Sidecar容器等。不同的资源类型也会存在一些专有的嵌套字段,例如,ConfigMap资源还支持使用clusterName等。
3.2.3 spec和status字段
Kubernetes用spec来描述所期望的对象应该具有的状态,而用status字段来记录对象在系统上的当前状态,因此status字段仅对活动对象才有意义。这两个字段都属于嵌套类型的字段。在定义资源配置清单时,spec是必须定义的字段,用于描述对象的目标状态,即用户期望对象需要表现出来的特征。status字段则记录了对象的当前状态(或实际状态),此字段值由Kubernetes系统负责填充或更新,用户不能手动进行定义。Master的controller-manager通过相应的控制器组件动态管理并确保对象的实际状态匹配用户所期望的状态,它是一种调和(reconciliation)配置系统。
例如,Deployment是一种用于描述集群中运行的应用的对象,因此,创建Deployment类型的对象时,需要为目标Deployment对象设定spec,指定期望需要运行的Pod副本数量、使用的标签选择器以及Pod模板等。Kubernetes系统读取待创建的Deployment对象的spec以及系统上相应的活动对象的当前状态,必要时进行对象更新以确保status字段吻合spec字段中期望的状态。如果这其中任一实例出现问题(status字段值发生了变化),那么Kubernetes系统则需要及时对spec和status字段的差异做出响应,例如,补足缺失的Pod副本数目等。
spec字段嵌套的字段对于不同的对象类型来说各不相同,具体需要参照Kubernetes API参考手册中的说明分别进行获取,核心资源对象的常用配置字段将会在本书后面的章节中进行讲解。
3.2.4 资源配置清单格式文档
定义资源配置清单时,尽管apiVersion、kind和metadata有章可循,但spec字段对不同的资源来说却是千差万别的,因此用户需要参考Kubernetes API的参考文档来了解各种可用属性字段。好在,Kubernetes在系统上内建了相关的文档,用户可以使用“kubectl explain”命令直接获取相关的使用帮助,它将根据给出的对象类型或相应的嵌套字段来显示相关的下一级文档。例如,要了解Pod资源的一级字段,可以使用类似如下的命令,命令结果会输出支持使用的各一组字段及其说明:
~]$ kubectl explain pods
需要了解某一级字段表示的对象之下的二级对象字段时,只需要指定其一级字段的对象名称即可,三级和四级字段对象等的查看方式依此类推。例如查看Pod资源的Spec对象支持嵌套使用的二级字段,可使用类似如下的命令:
~]$ kubectl explain pods.spec RESOURCE: spec <Object> DESCRIPTION: Specification of the desired behavior of the pod. …… PodSpec is a description of a pod. FIELDS: activeDeadlineSeconds <integer> Optional duration in seconds the pod may be active on the node relative to StartTime before the system will actively try to mark it failed and kill associated containers. Value must be a positive integer. …… containers <[]Object> -required- List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated. ……
对象的spec字段的文档通常包含RESOURCE、DESCRIPTION和FIELDS几节,其中FIELDS节中给出了可嵌套使用的字段、数据类型及功能描述。例如,上面命令的结果显示在FIELDS中的containers字段的数据类型是一个对象列表([]Object),而且是一个必选字段。任何值为对象类型数据的字段都会嵌套一到多个下一级字段,例如,Pod对象中的每个容器也是对象类型数据,它同样包含嵌套字段,但容器不支持单独创建,而是要包含于Pod对象的上下文中,其详细信息可通过三级字段来获取,命令及其结果示例如下:
~]$ kubectl explain pods.spec.containers RESOURCE: containers <[]Object> DESCRIPTION: List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated. A single application container that you want to run within a pod. FIELDS: args <[]string> Arguments to the entrypoint. The docker image's CMD is used if this is not provided. …… command <[]string> Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. …… env <[]Object> List of environment variables to set in the container. Cannot be updated. ……
内建文档大大降低了用户手动创建资源配置清单的难度,尝试使用某个资源类型时,explain也的确是用户的常用命令之一。熟悉各常用字段的功用之后,以同类型的现有活动对象的清单为模板可以更快地生成目标资源的配置文件,命令格式为“kubectl get TYPE NAME -o yaml --export”,其中--export选项用于省略输出由系统生成的信息。例如,基于现在的Deployment资源对象myapp生成配置模板deploy-demo.yaml文件,可以使用如下命令:
~]$ kubectl get deployment myapp -o yaml --export > deploy-demo.yaml
通过资源清单文件管理资源对象较之直接通过命令行进行操作有着诸多优势,具体包括命令行的操作方式仅支持部分资源对象的部分属性,而资源清单支持配置资源的所有属性字段,而且使用配置清单文件还能够进行版本追踪、复审等高级功能的操作。本书后续章节中的大部分资源管理操作都会借助于资源配置文件进行。
3.2.5 资源对象管理方式
Kubernetes的API Server遵循声明式编程(declarative programming)范式而设计,侧重于构建程序程序逻辑而无须用户描述其实现流程,用户只需要设定期望的状态,系统即能自行确定需要执行的操作以确保达到用户期望的状态。例如,期望某Deployment控制器管理三个Pod资源对象时,而系统观察到的当前数量却是两个,于是系统就会知道需要创建一个新的Pod资源来满足此期望。Kubernetes的自愈、自治等功能都依赖于其声明式机制。
与此对应的另一种范式称为陈述式编程(imperative programming),代码侧重于通过创建一种告诉计算机如何执行操作的算法来更改程序状态的语句来完成,它与硬件的工作方式密切相关,通常,代码将使用条件语句、循环和类继承等控制结构。为了便于用户使用,Kubernetes的API Server也支持陈述式范式,它直接通过命令及其选项完成对象的管理操作,前面用到的run、expose、delete和get等命令都属于此类,执行时用户需要告诉系统要做什么,例如,使用run命令创建一个有着3个Pod对象副本的Deployment对象,或者通过delete命令删除一个名为myapp的Service对象。
Kubernetes系统的大部分API对象都有着spec和status两个字段,其中,spec用于让用户定义所期望的状态,系统从中读出相关的定义;而status则是系统观察并负责写入的当前状态,用户可以从中获取相关的信息。Kubernetes系统通过控制器监控着系统对象,由其负责让系统当前的状态无限接近用户所期望的状态。
kubectl的命令由此可以分为三类:陈述式命令(imperative command)、陈述式对象配置(imperative object configuration)和声明式对象配置(declarative object configuration)。第一种方式即此前用到的run、expose、delete和get等命令,它们直接作用于Kubernetes系统上的活动对象,简单易用,但不支持代码复用、修改复审及审计日志等功能,这些功能的使用通常要依赖于资源配置文件,这些文件也称为资源清单。在这种模式下,用户可以访问每个对象的完整模式,但用户还需要深入学习Kubernetes API。
如3.2.4节所述,资源清单本质上是一个JSON或YAML格式的文本文件,由资源对象的配置信息组成,支持使用Git等进行版本控制。而用户可以资源清单为基础,在Kubernetes系统上以陈述式或声明式进行资源对象管理,如图3-4所示。
图3-4 基于资源配置清单管理对象
陈述式管理方式包括create、delete、get和replace等命令,与陈述式命令的不同之处在于,它通过资源配置清单读取需要管理的目标资源对象。陈述式对象配置的管理操作直接作用于活动对象,即便仅修改配置清单中的极小一部分内容,使用replace命令进行的对象更新也将会导致整个对象被替换。进一步地,混合使用陈述式命令进行清单文件带外修改时,必然会导致用户丢失活动对象的当前状态。
声明式对象配置并不直接指明要进行的对象管理操作,而是提供配置清单文件给Kubernetes系统,并委托系统跟踪活动对象的状态变动。资源对象的创建、删除及修改操作全部通过唯一的命令apply来完成,并且每次操作时,提供给命令的配置信息都将保存于对象的注解信息(kubectl.kubernetes.io/last-applied-configuration)中,并通过对比检查活动对象的当前状态、注解中的配置信息及资源清单中的配置信息三方进行变更合并,从而实现仅修改变动字段的高级补丁机制。
陈述式对象配置相较于声明式对象配置来说,其缺点在于同一目录下的配置文件必须同时进行同一种操作,例如,要么都创建,要么都更新等,而且其他用户的更新也必须反映在配置文件中,不然其更新在一下次的更新中将会被覆盖。因此,声明式对象配置是优先推荐给用户使用的管理机制。
然而,对于新手来说,陈述式命令的配置方式最易于上手,对系统有所了解后易于切换为使用陈述式对象配置管理方式。因此,若推荐给高级用户则推荐使用声明式配置,并建议同时使用版本控制系统存储所期望的状态,以及跨对象的历史信息,并启用变更复审机制。另外,推荐使用借助于kube-applier等一类的项目实现自动化声明式配置,用户将配置推送到Git仓库中,然后借助此类工具即能将其自动同步于Kubernetes集群上。