1 - 理解 Kubernetes 对象

理解 Kubernetes 对象

https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/kubernetes-objects/

理解 Kubernetes 对象

在 Kubernetes 系统中,Kubernetes 对象 是持久化的实体。 Kubernetes 使用这些实体去表示整个集群的状态。 比较特别地是,它们描述了如下信息:

  • 哪些容器化应用正在运行(以及在哪些节点上运行)
  • 可以被应用使用的资源
  • 关于应用运行时表现的策略,比如重启策略、升级策略以及容错策略

Kubernetes 对象是“目标性记录” —— 一旦创建该对象,Kubernetes 系统将不断工作以确保该对象存在。 通过创建对象,你就是在告知 Kubernetes 系统,你想要的集群工作负载状态看起来应是什么样子的, 这就是 Kubernetes 集群所谓的 期望状态(Desired State)

对象规约(Spec)与状态(Status)

几乎每个 Kubernetes 对象包含两个嵌套的对象字段,它们负责管理对象的配置: 对象 spec(规约) 和 对象 status(状态)。 对于具有 spec 的对象,你必须在创建对象时设置其内容,描述你希望对象所具有的特征: 期望状态(Desired State)。

status 描述了对象的当前状态(Current State),它是由 Kubernetes 系统和组件设置并更新的。 在任何时刻,Kubernetes 控制平面 都一直都在积极地管理着对象的实际状态,以使之达成期望状态。

例如,Kubernetes 中的 Deployment 对象能够表示运行在集群中的应用。 当创建 Deployment 时,可能会去设置 Deployment 的 spec,以指定该应用要有 3 个副本运行。 Kubernetes 系统读取 Deployment 的 spec, 并启动我们所期望的应用的 3 个实例 —— 更新状态以与规约相匹配。 如果这些实例中有的失败了(一种状态变更),Kubernetes 系统会通过执行修正操作来响应 spec 和状态间的不一致 —— 意味着它会启动一个新的实例来替换。

笔记:spec 必须在创建对象时设置,这是期望状态(Desired State)。status 是当前状态(Current State),由 Kubernetes 系统和组件设置并更新的。

描述 Kubernetes 对象

创建 Kubernetes 对象时,必须提供对象的 spec,用来描述该对象的期望状态, 以及关于对象的一些基本信息(例如名称)。 当使用 Kubernetes API 创建对象时(直接创建,或经由 kubectl), API 请求必须在请求本体中包含 JSON 格式的信息。 大多数情况下,你需要提供 .yaml 文件为 kubectl 提供这些信息kubectl 在发起 API 请求时,将这些信息转换成 JSON 格式。

必需字段

在想要创建的 Kubernetes 对象所对应的 .yaml 文件中,需要配置的字段如下:

  • apiVersion - 创建该对象所使用的 Kubernetes API 的版本
  • kind - 想要创建的对象的类别
  • metadata - 帮助唯一标识对象的一些数据,包括一个 name 字符串、UID 和可选的 namespace
  • spec - 你所期望的该对象的状态

笔记

kubernetes-object-properties

2 - Kubernetes 对象管理

kubectl 命令行工具支持多种不同的方式来创建和管理 Kubernetes 对象。

https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/object-management/

管理技巧

警告: 应该只使用一种技术来管理 Kubernetes 对象。混合和匹配技术作用在同一对象上将导致未定义行为。

管理技术 作用于 建议的环境 支持的写者 学习难度
指令式命令 活跃对象 开发项目 1+ 最低
指令式对象配置 单个文件 生产项目 1 中等
声明式对象配置 文件目录 生产项目 1+ 最高

指令式命令

使用指令式命令时,用户可以在集群中的活动对象上进行操作。用户将操作传给 kubectl 命令作为参数或标志。

指令式对象配置

在指令式对象配置中,kubectl 命令指定操作(创建,替换等),可选标志和 至少一个文件名。指定的文件必须包含 YAML 或 JSON 格式的对象的完整定义。

有关对象定义的详细信息,请查看 API 参考

警告: replace 指令式命令将现有规范替换为新提供的规范,并放弃对配置文件中 缺少的对象的所有更改。此方法不应与对象规约被独立于配置文件进行更新的 资源类型一起使用。比如类型为 LoadBalancer 的服务,它的 externalIPs 字段就是独立于集群配置进行更新。

声明式对象配置

使用声明式对象配置时,用户对本地存储的对象配置文件进行操作,但是用户 未定义要对该文件执行的操作。 kubectl 会自动检测每个文件的创建、更新和删除操作。 这使得配置可以在目录上工作,根据目录中配置文件对不同的对象执行不同的操作。

说明: 声明式对象配置保留其他编写者所做的修改,即使这些更改并未合并到对象配置文件中。 可以通过使用 patch API 操作仅写入观察到的差异,而不是使用 replace API 操作来替换整个对象配置来实现。

3 - 对象名称和 ID

对象名称和 ID

https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/

集群中的每一个对象都有一个名称来标识在同类资源中的唯一性。

每个 Kubernetes 对象也有一个 UID 来标识在整个集群中的唯一性。

比如,在同一个名字空间 中只能有一个名为 myapp-1234 的 Pod,但是可以命名一个 Pod 和一个 Deployment 同为 myapp-1234。

对于用户提供的非唯一性的属性,Kubernetes 提供了 标签(Labels)和 注解(Annotation)机制。

名称

客户端提供的字符串,引用资源 URL 中的对象,如/api/v1/pods/some name

某一时刻,只能有一个给定类型的对象具有给定的名称。但是,如果删除该对象,则可以创建同名的新对象。

说明:

当对象所代表的是一个物理实体(例如代表一台物理主机的 Node)时, 如果在 Node 对象未被删除并重建的条件下,重新创建了同名的物理主机, 则 Kubernetes 会将新的主机看作是老的主机,这可能会带来某种不一致性。

DNS 子域名

很多资源类型需要可以用作 DNS 子域名的名称。

RFC 1123 标签名

某些资源类型需要其名称遵循 RFC 1123 所定义的 DNS 标签标准。

RFC 1035 标签名

某些资源类型需要其名称遵循 RFC 1035 所定义的 DNS 标签标准。

路径分段名称

某些资源类型要求名称能被安全地用作路径中的片段。 换句话说,其名称不能是 ...,也不可以包含 /% 这些字符。

UID

Kubernetes 系统生成的字符串,唯一标识对象。

在 Kubernetes 集群的整个生命周期中创建的每个对象都有一个不同的 UID,它旨在区分类似实体的历史事件。

Kubernetes UID 是全局唯一标识符(也叫 UUID)。 UUID 是标准化的,见 ISO/IEC 9834-8 和 ITU-T X.667。

深入学习

Kubernetes 标识符和名称的设计文档

https://github.com/kubernetes/design-proposals-archive/blob/main/architecture/identifiers.md

对Kubernetes中标识符的目标和建议进行了总结。

定义

UID:一个非空的、不透明的、系统生成的值,保证在时间和空间上是唯一的;旨在区分类似实体的历史出现。

name: 一个非空的字符串,保证在特定时间的特定范围内是唯一的;在资源URL中使用;由客户在创建时提供,并鼓励对人友好;旨在促进单子对象的创建同位性和空间唯一性,区分不同的实体,并在操作中引用特定的实体。

rfc1035/rfc1123 label(DNS_LABEL)。一个字母数字(a-z和0-9)字符串,最大长度为63个字符,除第一个或最后一个字符外,任何地方都允许使用’-‘字符,适合作为主机名或域名中的段。

rfc1035/rfc1123 subdomain(DNS_SUBDOMAIN)。一个或多个小写的rfc1035/rfc1123标签,用’.‘分隔,最大长度为253个字符。

rfc4122 通用唯一标识符(universally unique identifier / UUID)。一个128位的生成值,跨时空碰撞的可能性极低,不需要中央协调。

rfc6335 port name(IANA_SVC_NAME)。一个字母数字(a-z和0-9)字符串,最大长度为15个字符,除了第一个或最后一个字符或与另一个’-‘字符相邻外,允许在任何地方使用’-‘字符,它必须至少包含一个(a-z)字符。

名称和UID的目标

  1. 跨越空间和时间唯一地识别(通过UID)一个对象。
  2. 跨越空间唯一地命名(通过 name)一个对象。
  3. 在API操作和/或配置文件中提供人性化的名称。
  4. 允许幂等的创建API资源,并执行单子对象的空间唯一性。
  5. 允许为某些对象自动生成DNS名称。

一般设计

  1. 当通过API创建一个对象时,必须指定一个Name字符串(一个DNS_SUBDOMAIN)。名称必须是非空的,并且在 apiserver 中是唯一的。这可以实现幂等和空间唯一的创建操作。系统的某些部分(如副本控制器)可以连接字符串(如基本名称和随机后缀)来创建一个唯一的Name。对于生成名字不切实际的情况,一些或所有对象可以支持一个参数来自动生成名字。生成随机的名字会破坏同位素。

    例子。“guestbook.user”, “backend-x4eb1”

  2. 当一个对象通过API被创建时,可以指定一个命名空间字符串(DNS_LABEL)。根据API接收器,命名空间可能被验证(例如,apiserver可能确保命名空间实际存在)。如果没有指定命名空间,API接收器将分配一个命名空间。这种分配策略在不同的API接收器中可能有所不同(例如,apiserver可能有一个默认值,kubelet可能会生成一些半随机的东西)。

    例如。“api.k8s.example.com”

  3. 在通过API接受一个对象时,该对象被分配了一个UID(一个UUID)。UID必须是非空的,并且是跨空间和时间的唯一。

    例如。“01234567-89ab-cdef-0123-456789abcdef”

4 - 标签和选择算符

标签和选择算符

https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/labels/

标签(Labels) 是附加到 Kubernetes 对象(比如 Pod)上的键值对。

标签旨在用于指定对用户有意义且相关的对象的标识属性,但不直接对核心系统有语义含义。 标签可以用于组织和选择对象的子集。标签可以在创建时附加到对象,随后可以随时添加和修改。 每个对象都可以定义一组键/值标签。每个键对于给定对象必须是唯一的。

标签能够支持高效的查询和监听操作,对于用户界面和命令行是很理想的。 应使用注解记录非识别信息。

动机

标签使用户能够以松散耦合的方式将他们自己的组织结构映射到系统对象,而无需客户端存储这些映射。

标签选择算符

名称和 UID 不同, 标签不支持唯一性。通常,我们希望许多对象携带相同的标签。

通过标签选择算符,客户端/用户可以识别一组对象。标签选择算符是 Kubernetes 中的核心分组原语。

API 目前支持两种类型的选择算符:基于等值的基于集合的。 标签选择算符可以由逗号分隔的多个需求组成。 在多个需求的情况下,必须满足所有要求,因此逗号分隔符充当逻辑&&)运算符。

基于等值的需求

基于等值基于不等值的需求允许按标签键和值进行过滤。 匹配对象必须满足所有指定的标签约束,尽管它们也可能具有其他标签。 可接受的运算符有 ===!= 三种。

基于集合的需求

基于集合的标签需求允许你通过一组值来过滤键。 支持三种操作符:innotinexists

5 - 名字空间

名字空间

https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/namespaces/

在 Kubernetes 中,名字空间(Namespace) 提供一种机制,将同一集群中的资源划分为相互隔离的组。

何时使用多个名字空间

名字空间适用于存在很多跨多个团队或项目的用户的场景。对于只有几到几十个用户的集群,根本不需要创建或考虑名字空间。当需要名字空间提供的功能时,请开始使用它们。

名字空间为名称提供了一个范围。资源的名称需要在名字空间内是唯一的,但不能跨名字空间。 名字空间不能相互嵌套,每个 Kubernetes 资源只能在一个名字空间中。

不必使用多个名字空间来分隔仅仅轻微不同的资源,例如同一软件的不同版本: 应该使用标签 来区分同一名字空间中的不同资源。

使用名字空间

查看名字空间

Kubernetes 会创建四个初始名字空间:

  • default 没有指明使用其它名字空间的对象所使用的默认名字空间
  • kube-system Kubernetes 系统创建对象所使用的名字空间
  • kube-public 这个名字空间是自动创建的,所有用户(包括未经过身份验证的用户)都可以读取它。 这个名字空间主要用于集群使用,以防某些资源在整个集群中应该是可见和可读的。 这个名字空间的公共方面只是一种约定,而不是要求。
  • kube-node-lease 此名字空间用于与各个节点相关的 租约(Lease)对象。 节点租期允许 kubelet 发送心跳,由此控制面能够检测到节点故障。

设置名字空间偏好

你可以永久保存名字空间,以用于对应上下文中所有后续 kubectl 命令。

kubectl config set-context --current --namespace=<名字空间名称>
# 验证
kubectl config view --minify | grep namespace:

笔记:这个功能很实用,有时长期使用一个命名空间,每次输入 -n xxxx 好烦。

并非所有对象都在名字空间中

大多数 kubernetes 资源(例如 Pod、Service、副本控制器等)都位于某些名字空间中。 但是名字空间资源本身并不在名字空间中。而且底层资源, 例如节点和持久化卷不属于任何名字空间。

查看哪些 Kubernetes 资源在名字空间中,哪些不在名字空间中:

# 位于名字空间中的资源
kubectl api-resources --namespaced=true

# 不在名字空间中的资源
kubectl api-resources --namespaced=false

6 - 注解

使用 Kubernetes 注解为对象附加任意的非标识的元数据

https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/annotations/

你可以使用 Kubernetes 注解为对象附加任意的非标识的元数据。

为对象附加元数据

你可以使用标签或注解将元数据附加到 Kubernetes 对象。

  • 标签可以用来选择对象和查找满足某些条件的对象集合。

  • 相反,注解不用于标识和选择对象。

注解中的元数据,可以很小,也可以很大,可以是结构化的,也可以是非结构化的,能够包含标签不允许的字符。

注解和标签一样,是键/值对:

"metadata": {
  "annotations": {
    "key1" : "value1",
    "key2" : "value2"
  }
}

说明: Map 中的键和值必须是字符串。 换句话说,你不能使用数字、布尔值、列表或其他类型的键或值。

语法和字符集

注解(Annotations) 存储的形式是键/值对。有效的注解键分为两部分: 可选的前缀和名称,以斜杠(/)分隔。 名称段是必需项,并且必须在 63 个字符以内,以字母数字字符([a-z0-9A-Z])开头和结尾, 并允许使用破折号(-),下划线(_),点(.)和字母数字。 前缀是可选的。如果指定,则前缀必须是 DNS 子域:一系列由点(.)分隔的 DNS 标签, 总计不超过 253 个字符,后跟斜杠(/)。 如果省略前缀,则假定注解键对用户是私有的。 由系统组件添加的注解 (例如,kube-schedulerkube-controller-managerkube-apiserverkubectl 或其他第三方组件),必须为终端用户添加注解前缀。

kubernetes.io/k8s.io/ 前缀是为 Kubernetes 核心组件保留的。

7 - 字段选择器

字段选择器(Field selectors)允许你根据一个或多个资源字段的值筛选 Kubernetes 资源。

https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/field-selectors/

kubectl 命令将筛选出 status.phase 字段值为 Running 的所有 Pod:

kubectl get pods --field-selector status.phase=Running

说明: 字段选择器本质上是资源“过滤器(Filters)”。默认情况下,字段选择器/过滤器是未被应用的, 这意味着指定类型的所有资源都会被筛选出来。 这使得以下的两个 kubectl 查询是等价的:

kubectl get pods
kubectl get pods --field-selector ""

链式选择器

标签和其他选择器一样, 字段选择器可以通过使用逗号分隔的列表组成一个选择链。 下面这个 kubectl 命令将筛选 status.phase 字段不等于 Running 同时 spec.restartPolicy 字段等于 Always 的所有 Pod:

kubectl get pods --field-selector=status.phase!=Running,spec.restartPolicy=Always

多种资源类型

你能够跨多种资源类型来使用字段选择器。 下面这个 kubectl 命令将筛选出所有不在 default 命名空间中的 StatefulSet 和 Service:

kubectl get statefulsets,services --all-namespaces --field-selector metadata.namespace!=default

笔记:原来还能这么用,汗,之前不知道,我都是一个一个查的。

8 - Finalizers

Finalizer 是带有命名空间的键,告诉 Kubernetes 等到特定的条件被满足后, 再完全删除被标记为删除的资源。

https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/finalizers/

TBD: 稍后再看。

9 - 属主与附属

在 Kubernetes 中,一些对象是其他对象的“属主(Owner)”。

https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/owners-dependents/

在 Kubernetes 中,一些对象是其他对象的“属主(Owner)”。 例如,ReplicaSet 是一组 Pod 的属主。 具有属主的对象是属主的“附属(Dependent)”。

属主关系不同于一些资源使用的标签和选择算符机制。 例如,有一个创建 EndpointSlice 对象的 Service, 该 Service 使用标签来让控制平面确定,哪些 EndpointSlice 对象属于该 Service。 除开标签,每个代表 Service 所管理的 EndpointSlice 都有一个属主引用。 属主引用避免 Kubernetes 的不同部分干扰到不受它们控制的对象。

对象规约中的属主引用

附属对象有一个 metadata.ownerReferences 字段,用于引用其属主对象。 一个有效的属主引用,包含与附属对象同在一个命名空间下的对象名称和一个 UID。 Kubernetes 自动为一些对象的附属资源设置属主引用的值, 这些对象包含 ReplicaSet、DaemonSet、Deployment、Job、CronJob、ReplicationController 等。 你也可以通过改变这个字段的值,来手动配置这些关系。 然而,通常不需要这么做,你可以让 Kubernetes 自动管理附属关系。

附属对象还有一个 ownerReferences.blockOwnerDeletion 字段,该字段使用布尔值, 用于控制特定的附属对象是否可以阻止垃圾收集删除其属主对象。 如果控制器(例如 Deployment 控制器) 设置了 metadata.ownerReferences 字段的值,Kubernetes 会自动设置 blockOwnerDeletion 的值为 true。 你也可以手动设置 blockOwnerDeletion 字段的值,以控制哪些附属对象会阻止垃圾收集。

Kubernetes 准入控制器根据属主的删除权限控制用户访问,以便为附属资源更改此字段。 这种控制机制可防止未经授权的用户延迟属主对象的删除。

说明:

根据设计,kubernetes 不允许跨名字空间指定属主。 名字空间范围的附属可以指定集群范围的或者名字空间范围的属主。 名字空间范围的属主必须和该附属处于相同的名字空间。 如果名字空间范围的属主和附属不在相同的名字空间,那么该属主引用就会被认为是缺失的, 并且当附属的所有属主引用都被确认不再存在之后,该附属就会被删除。

笔记

dapr在创建 service 对象时,会同时创建 xxx 和 xxx-dapr 两个service,其中 xxx-dapr 就会有 ownerReferences 的设置,指向 xxx deployment:

apiVersion: v1
kind: Service
metadata:
  labels:
    dapr.io/enabled: "true"
  name: service-a-dapr
  namespace: dapr-tests
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: Deployment
    name: service-a
    uid: b7ffded5-c8ab-4316-a535-5d29a6ceae18 # 对应到 owner deployment 的 uid

xxx deployment 的内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  generation: 1
  name: service-a
  namespace: dapr-tests
  uid: b7ffded5-c8ab-4316-a535-5d29a6ceae18

10 - 推荐使用的标签

推荐的标签使管理应用程序变得更容易但不是任何核心工具所必需的。

https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/common-labels/

一组通用的标签可以让多个工具之间相互操作,用所有工具都能理解的通用方式描述对象。

除了支持工具外,推荐的标签还以一种可以查询的方式描述了应用程序。

共享标签和注解都使用同一个前缀:app.kubernetes.io。没有前缀的标签是用户私有的。 共享前缀可以确保共享标签不会干扰用户自定义的标签。