这是本节的多页打印视图。
点击此处打印 .
返回本页常规视图 .
概述
Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,方便进行声明式配置和自动化。Kubernetes 拥有一个庞大且快速增长的生态系统,其服务、支持和工具的使用范围广泛。
此页面是 Kubernetes 的概述。
Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。
Kubernetes 拥有一个庞大且快速增长的生态,其服务、支持和工具的使用范围相当广泛。
Kubernetes 这个名字源于希腊语,意为“舵手”或“飞行员”。K8s 这个缩写是因为 K 和 s 之间有 8 个字符的关系。
Google 在 2014 年开源了 Kubernetes 项目。
Kubernetes 建立在 Google 大规模运行生产工作负载十几年经验 的基础上,
结合了社区中最优秀的想法和实践。
时光回溯
让我们回顾一下为何 Kubernetes 能够裨益四方。
传统部署时代:
早期,各个组织是在物理服务器上运行应用程序。
由于无法限制在物理服务器中运行的应用程序资源使用,因此会导致资源分配问题。
例如,如果在同一台物理服务器上运行多个应用程序,
则可能会出现一个应用程序占用大部分资源的情况,而导致其他应用程序的性能下降。
一种解决方案是将每个应用程序都运行在不同的物理服务器上,
但是当某个应用程序资源利用率不高时,剩余资源无法被分配给其他应用程序,
而且维护许多物理服务器的成本很高。
虚拟化部署时代:
因此,虚拟化技术被引入了。虚拟化技术允许你在单个物理服务器的 CPU 上运行多台虚拟机(VM)。
虚拟化能使应用程序在不同 VM 之间被彼此隔离,且能提供一定程度的安全性,
因为一个应用程序的信息不能被另一应用程序随意访问。
虚拟化技术能够更好地利用物理服务器的资源,并且因为可轻松地添加或更新应用程序,
而因此可以具有更高的可扩缩性,以及降低硬件成本等等的好处。
通过虚拟化,你可以将一组物理资源呈现为可丢弃的虚拟机集群。
每个 VM 是一台完整的计算机,在虚拟化硬件之上运行所有组件,包括其自己的操作系统。
容器部署时代:
容器类似于 VM,但是更宽松的隔离特性,使容器之间可以共享操作系统(OS)。
因此,容器比起 VM 被认为是更轻量级的。且与 VM 类似,每个容器都具有自己的文件系统、CPU、内存、进程空间等。
由于它们与基础架构分离,因此可以跨云和 OS 发行版本进行移植。
容器因具有许多优势而变得流行起来,例如:
敏捷应用程序的创建和部署:与使用 VM 镜像相比,提高了容器镜像创建的简便性和效率。
持续开发、集成和部署:通过快速简单的回滚(由于镜像不可变性),
提供可靠且频繁的容器镜像构建和部署。
关注开发与运维的分离:在构建、发布时创建应用程序容器镜像,而不是在部署时,
从而将应用程序与基础架构分离。
可观察性:不仅可以显示 OS 级别的信息和指标,还可以显示应用程序的运行状况和其他指标信号。
跨开发、测试和生产的环境一致性:在笔记本计算机上也可以和在云中运行一样的应用程序。
跨云和操作系统发行版本的可移植性:可在 Ubuntu、RHEL、CoreOS、本地、
Google Kubernetes Engine 和其他任何地方运行。
以应用程序为中心的管理:提高抽象级别,从在虚拟硬件上运行 OS 到使用逻辑资源在 OS 上运行应用程序。
松散耦合、分布式、弹性、解放的微服务:应用程序被分解成较小的独立部分,
并且可以动态部署和管理 - 而不是在一台大型单机上整体运行。
资源隔离:可预测的应用程序性能。
资源利用:高效率和高密度。
为什么需要 Kubernetes,它能做什么?
容器是打包和运行应用程序的好方式。在生产环境中,
你需要管理运行着应用程序的容器,并确保服务不会下线。
例如,如果一个容器发生故障,则你需要启动另一个容器。
如果此行为交由给系统处理,是不是会更容易一些?
这就是 Kubernetes 要来做的事情!
Kubernetes 为你提供了一个可弹性运行分布式系统的框架。
Kubernetes 会满足你的扩展要求、故障转移你的应用、提供部署模式等。
例如,Kubernetes 可以轻松管理系统的 Canary (金丝雀) 部署。
Kubernetes 为你提供:
批处理执行
除了服务外,Kubernetes 还可以管理你的批处理和 CI(持续集成)工作负载,如有需要,可以替换失败的容器。
水平扩缩
使用简单的命令、用户界面或根据 CPU 使用率自动对你的应用进行扩缩。
IPv4/IPv6 双栈
为 Pod(容器组)和 Service(服务)分配 IPv4 和 IPv6 地址。
为可扩展性设计
在不改变上游源代码的情况下为你的 Kubernetes 集群添加功能。
Kubernetes 不是什么
Kubernetes 不是传统的、包罗万象的 PaaS(平台即服务)系统。
由于 Kubernetes 是在容器级别运行,而非在硬件级别,它提供了 PaaS 产品共有的一些普遍适用的功能,
例如部署、扩展、负载均衡,允许用户集成他们的日志记录、监控和警报方案。
但是,Kubernetes 不是单体式(monolithic)系统,那些默认解决方案都是可选、可插拔的。
Kubernetes 为构建开发人员平台提供了基础,但是在重要的地方保留了用户选择权,能有更高的灵活性。
Kubernetes:
不限制支持的应用程序类型。
Kubernetes 旨在支持极其多种多样的工作负载,包括无状态、有状态和数据处理工作负载。
如果应用程序可以在容器中运行,那么它应该可以在 Kubernetes 上很好地运行。
不部署源代码,也不构建你的应用程序。
持续集成(CI)、交付和部署(CI/CD)工作流取决于组织的文化和偏好以及技术要求。
不提供应用程序级别的服务作为内置服务,例如中间件(例如消息中间件)、
数据处理框架(例如 Spark)、数据库(例如 MySQL)、缓存、集群存储系统
(例如 Ceph)。这样的组件可以在 Kubernetes 上运行,并且/或者可以由运行在
Kubernetes 上的应用程序通过可移植机制(例如开放服务代理 )来访问。
不是日志记录、监视或警报的解决方案。
它集成了一些功能作为概念证明,并提供了收集和导出指标的机制。
不提供也不要求配置用的语言、系统(例如 jsonnet),它提供了声明性 API,
该声明性 API 可以由任意形式的声明性规范所构成。
不提供也不采用任何全面的机器配置、维护、管理或自我修复系统。
此外,Kubernetes 不仅仅是一个编排系统,实际上它消除了编排的需要。
编排的技术定义是执行已定义的工作流程:首先执行 A,然后执行 B,再执行 C。
而 Kubernetes 包含了一组独立可组合的控制过程,可以持续地将当前状态驱动到所提供的预期状态。
你不需要在乎如何从 A 移动到 C,也不需要集中控制,这使得系统更易于使用且功能更强大、
系统更健壮,更为弹性和可扩展。
接下来
1 - Kubernetes 对象
Kubernetes 对象是 Kubernetes 系统中的持久性实体。 Kubernetes 使用这些实体表示你的集群状态。 了解 Kubernetes 对象模型以及如何使用这些对象。
本页说明了在 Kubernetes API 中是如何表示 Kubernetes 对象的,
以及如何使用 .yaml
格式的文件表示 Kubernetes 对象。
理解 Kubernetes 对象
在 Kubernetes 系统中,Kubernetes 对象 是持久化的实体。
Kubernetes 使用这些实体去表示整个集群的状态。
具体而言,它们描述了如下信息:
哪些容器化应用正在运行(以及在哪些节点上运行)
可以被应用使用的资源
关于应用运行时行为的策略,比如重启策略、升级策略以及容错策略
Kubernetes 对象是一种“意向表达(Record of Intent)”。一旦创建该对象,
Kubernetes 系统将不断工作以确保该对象存在。通过创建对象,你本质上是在告知
Kubernetes 系统,你想要的集群工作负载状态看起来应是什么样子的,
这就是 Kubernetes 集群所谓的期望状态(Desired State) 。
操作 Kubernetes 对象 —— 无论是创建、修改或者删除 —— 需要使用
Kubernetes API 。
比如,当使用 kubectl
命令行接口(CLI)时,CLI 会调用必要的 Kubernetes API;
也可以在程序中使用客户端库 ,
来直接调用 Kubernetes API。
对象规约(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
和 status
间的不一致 —— 意味着它会启动一个新的实例来替换。
关于对象 spec、status 和 metadata 的更多信息,可参阅
Kubernetes API 约定 。
描述 Kubernetes 对象
创建 Kubernetes 对象时,必须提供对象的 spec
,用来描述该对象的期望状态,
以及关于对象的一些基本信息(例如名称)。
当使用 Kubernetes API 创建对象时(直接创建或经由 kubectl
创建),
API 请求必须在请求主体中包含 JSON 格式的信息。
大多数情况下,你会通过 清单(Manifest) 文件为 kubectl
提供这些信息。
按照惯例,清单是 YAML 格式的(你也可以使用 JSON 格式)。
像 kubectl
这样的工具在通过 HTTP 进行 API 请求时,
会将清单中的信息转换为 JSON 或其他受支持的序列化格式。
这里有一个清单示例文件,展示了 Kubernetes Deployment 的必需字段和对象 spec
:
apiVersion : apps/v1
kind : Deployment
metadata :
name : nginx-deployment
spec :
selector :
matchLabels :
app : nginx
replicas : 2 # 告知 Deployment 运行 2 个与该模板匹配的 Pod
template :
metadata :
labels :
app : nginx
spec :
containers :
- name : nginx
image : nginx:1.14.2
ports :
- containerPort : 80
与上面使用清单文件来创建 Deployment 类似,另一种方式是使用 kubectl
命令行接口(CLI)的
kubectl apply
命令,
将 .yaml
文件作为参数。下面是一个示例:
kubectl apply -f https://k8s.io/examples/application/deployment.yaml
输出类似下面这样:
deployment.apps/nginx-deployment created
必需字段
在想要创建的 Kubernetes 对象所对应的清单(YAML 或 JSON 文件)中,需要配置的字段如下:
apiVersion
- 创建该对象所使用的 Kubernetes API 的版本
kind
- 想要创建的对象的类别
metadata
- 帮助唯一标识对象的一些数据,包括一个 name
字符串、UID
和可选的 namespace
spec
- 你所期望的该对象的状态
对每个 Kubernetes 对象而言,其 spec
之精确格式都是不同的,包含了特定于该对象的嵌套字段。
Kubernetes API 参考 可以帮助你找到想要使用
Kubernetes 创建的所有对象的规约格式。
例如,参阅 Pod API 参考文档中
spec
字段 。
对于每个 Pod,其 .spec
字段设置了 Pod 及其期望状态(例如 Pod 中每个容器的容器镜像名称)。
另一个对象规约的例子是 StatefulSet API 中的
spec
字段 。
对于 StatefulSet 而言,其 .spec
字段设置了 StatefulSet 及其期望状态。
在 StatefulSet 的 .spec
内,有一个为 Pod 对象提供的模板 。
该模板描述了 StatefulSet 控制器为了满足 StatefulSet 规约而要创建的 Pod。
不同类型的对象可以有不同的 .status
信息。API 参考页面给出了 .status
字段的详细结构,
以及针对不同类型 API 对象的具体内容。
说明:
请查看配置最佳实践 来获取有关编写 YAML 配置文件的更多信息。
服务器端字段验证
从 Kubernetes v1.25 开始,API
服务器提供了服务器端字段验证 ,
可以检测对象中未被识别或重复的字段。它在服务器端提供了 kubectl --validate
的所有功能。
kubectl
工具使用 --validate
标志来设置字段验证级别。它接受值
ignore
、warn
和 strict
,同时还接受值 true
(等同于 strict
)和
false
(等同于 ignore
)。kubectl
的默认验证设置为 --validate=true
。
Strict
严格的字段验证,验证失败时会报错
Warn
执行字段验证,但错误会以警告形式提供而不是拒绝请求
Ignore
不执行服务器端字段验证
当 kubectl
无法连接到支持字段验证的 API 服务器时,它将回退为使用客户端验证。
Kubernetes 1.27 及更高版本始终提供字段验证;较早的 Kubernetes 版本可能没有此功能。
如果你的集群版本低于 v1.27,可以查阅适用于你的 Kubernetes 版本的文档。
接下来
如果你刚开始学习 Kubernetes,可以进一步阅读以下信息:
Kubernetes 对象管理
介绍了如何使用 kubectl
来管理对象。
如果你还没有安装 kubectl
,你可能需要安装 kubectl 。
从总体上了解 Kubernetes API,可以查阅:
若要更深入地了解 Kubernetes 对象,可以阅读本节的其他页面:
1.1 - Kubernetes 对象管理
kubectl
命令行工具支持多种不同的方式来创建和管理 Kubernetes
对象 。
本文档概述了不同的方法。
阅读 Kubectl book 来了解 kubectl
管理对象的详细信息。
管理技巧
警告:
应该只使用一种技术来管理 Kubernetes 对象。混合和匹配技术作用在同一对象上将导致未定义行为。
管理技术
作用于
建议的环境
支持的写者
学习难度
指令式命令
活跃对象
开发项目
1+
最低
指令式对象配置
单个文件
生产项目
1
中等
声明式对象配置
文件目录
生产项目
1+
最高
指令式命令
使用指令式命令时,用户可以在集群中的活动对象上进行操作。用户将操作传给
kubectl
命令作为参数或标志。
这是开始或者在集群中运行一次性任务的推荐方法。因为这个技术直接在活跃对象
上操作,所以它不提供以前配置的历史记录。
例子
通过创建 Deployment 对象来运行 nginx 容器的实例:
kubectl create deployment nginx --image nginx
权衡
与对象配置相比的优点:
命令用单个动词表示。
命令仅需一步即可对集群进行更改。
与对象配置相比的缺点:
命令不与变更审查流程集成。
命令不提供与更改关联的审核跟踪。
除了实时内容外,命令不提供记录源。
命令不提供用于创建新对象的模板。
指令式对象配置
在指令式对象配置中,kubectl 命令指定操作(创建,替换等),可选标志和
至少一个文件名。指定的文件必须包含 YAML 或 JSON 格式的对象的完整定义。
有关对象定义的详细信息,请查看
API 参考 。
警告:
replace
指令式命令将现有规范替换为新提供的规范,并放弃对配置文件中
缺少的对象的所有更改。此方法不应与对象规约被独立于配置文件进行更新的
资源类型一起使用。比如类型为 LoadBalancer
的服务,它的 externalIPs
字段就是独立于集群配置进行更新。
例子
创建配置文件中定义的对象:
kubectl create -f nginx.yaml
删除两个配置文件中定义的对象:
kubectl delete -f nginx.yaml -f redis.yaml
通过覆盖活动配置来更新配置文件中定义的对象:
kubectl replace -f nginx.yaml
权衡
与指令式命令相比的优点:
对象配置可以存储在源控制系统中,比如 Git。
对象配置可以与流程集成,例如在推送和审计之前检查更新。
对象配置提供了用于创建新对象的模板。
与指令式命令相比的缺点:
对象配置需要对对象架构有基本的了解。
对象配置需要额外的步骤来编写 YAML 文件。
与声明式对象配置相比的优点:
指令式对象配置行为更加简单易懂。
从 Kubernetes 1.5 版本开始,指令对象配置更加成熟。
与声明式对象配置相比的缺点:
指令式对象配置更适合文件,而非目录。
对活动对象的更新必须反映在配置文件中,否则会在下一次替换时丢失。
声明式对象配置
使用声明式对象配置时,用户对本地存储的对象配置文件进行操作,但是用户
未定义要对该文件执行的操作。
kubectl
会自动检测每个文件的创建、更新和删除操作。
这使得配置可以在目录上工作,根据目录中配置文件对不同的对象执行不同的操作。
说明:
声明式对象配置保留其他编写者所做的修改,即使这些更改并未合并到对象配置文件中。
可以通过使用 patch
API 操作仅写入观察到的差异,而不是使用 replace
API
操作来替换整个对象配置来实现。
例子
处理 configs
目录中的所有对象配置文件,创建并更新活跃对象。
可以首先使用 diff
子命令查看将要进行的更改,然后在进行应用:
kubectl diff -f configs/
kubectl apply -f configs/
递归处理目录:
kubectl diff -R -f configs/
kubectl apply -R -f configs/
权衡
与指令式对象配置相比的优点:
对活动对象所做的更改即使未合并到配置文件中,也会被保留下来。
声明性对象配置更好地支持对目录进行操作并自动检测每个文件的操作类型(创建,修补,删除)。
与指令式对象配置相比的缺点:
声明式对象配置难于调试并且出现异常时结果难以理解。
使用 diff 产生的部分更新会创建复杂的合并和补丁操作。
接下来
1.2 - 对象名称和 ID
集群中的每一个对象 都有一个名称 来标识在同类资源中的唯一性。
每个 Kubernetes 对象也有一个 UID 来标识在整个集群中的唯一性。
比如,在同一个名字空间
中只能有一个名为 myapp-1234
的 Pod,但是可以命名一个 Pod 和一个 Deployment 同为 myapp-1234
。
对于用户提供的非唯一性的属性,Kubernetes
提供了标签(Label) 和
注解(Annotation) 机制。
名称
客户端提供的字符串,引用资源 URL 中的对象,如/api/v1/pods/some name
。
某一时刻,只能有一个给定类型的对象具有给定的名称。但是,如果删除该对象,则可以创建同名的新对象。
名称在同一资源的所有
API 版本 中必须是唯一的。
这些 API 资源通过各自的 API 组、资源类型、名字空间(对于划分名字空间的资源)和名称来区分。
换言之,API 版本在此上下文中是不相关的。
说明:
当对象所代表的是一个物理实体(例如代表一台物理主机的 Node)时,
如果在 Node 对象未被删除并重建的条件下,重新创建了同名的物理主机,
则 Kubernetes 会将新的主机看作是老的主机,这可能会带来某种不一致性。
以下是比较常见的四种资源命名约束。
DNS 子域名
很多资源类型需要可以用作 DNS 子域名的名称。
DNS 子域名的定义可参见 RFC 1123 。
这一要求意味着名称必须满足如下规则:
不能超过 253 个字符
只能包含小写字母、数字,以及 '-' 和 '.'
必须以字母数字开头
必须以字母数字结尾
RFC 1123 标签名
某些资源类型需要其名称遵循 RFC 1123
所定义的 DNS 标签标准。也就是命名必须满足如下规则:
最多 63 个字符
只能包含小写字母、数字,以及 '-'
必须以字母数字开头
必须以字母数字结尾
RFC 1035 标签名
某些资源类型需要其名称遵循 RFC 1035
所定义的 DNS 标签标准。也就是命名必须满足如下规则:
最多 63 个字符
只能包含小写字母、数字,以及 '-'
必须以字母开头
必须以字母数字结尾
说明:
RFC 1035 和 RFC 1123 标签标准之间的唯一区别是 RFC 1123
标签允许以数字开头,而 RFC 1035 标签只能以小写字母字符开头。
路径分段名称
某些资源类型要求名称能被安全地用作路径中的片段。
换句话说,其名称不能是 .
、..
,也不可以包含 /
或 %
这些字符。
下面是一个名为 nginx-demo
的 Pod 的配置清单:
apiVersion : v1
kind : Pod
metadata :
name : nginx-demo
spec :
containers :
- name : nginx
image : nginx:1.14.2
ports :
- containerPort : 80
UID
Kubernetes 系统生成的字符串,唯一标识对象。
在 Kubernetes 集群的整个生命周期中创建的每个对象都有一个不同的 UID,它旨在区分类似实体的历史事件。
Kubernetes UID 是全局唯一标识符(也叫 UUID)。
UUID 是标准化的,见 ISO/IEC 9834-8 和 ITU-T X.667。
接下来
1.3 - 标签和选择算符
标签(Labels) 是附加到 Kubernetes
对象 (比如 Pod)上的键值对。
标签旨在用于指定对用户有意义且相关的对象的标识属性,但不直接对核心系统有语义含义。
标签可以用于组织和选择对象的子集。标签可以在创建时附加到对象,随后可以随时添加和修改。
每个对象都可以定义一组键/值标签。每个键对于给定对象必须是唯一的。
"metadata" : {
"labels" : {
"key1" : "value1" ,
"key2" : "value2"
}
}
标签能够支持高效的查询和监听操作,对于用户界面和命令行是很理想的。
应使用注解 记录非识别信息。
动机
标签使用户能够以松散耦合的方式将他们自己的组织结构映射到系统对象,而无需客户端存储这些映射。
服务部署和批处理流水线通常是多维实体(例如,多个分区或部署、多个发行序列、多个层,每层多个微服务)。
管理通常需要交叉操作,这打破了严格的层次表示的封装,特别是由基础设施而不是用户确定的严格的层次结构。
示例标签:
"release" : "stable"
, "release" : "canary"
"environment" : "dev"
, "environment" : "qa"
, "environment" : "production"
"tier" : "frontend"
, "tier" : "backend"
, "tier" : "cache"
"partition" : "customerA"
, "partition" : "customerB"
"track" : "daily"
, "track" : "weekly"
有一些常用标签 的例子;你可以任意制定自己的约定。
请记住,标签的 Key 对于给定对象必须是唯一的。
语法和字符集
标签 是键值对。有效的标签键有两个段:可选的前缀和名称,用斜杠(/
)分隔。
名称段是必需的,必须小于等于 63 个字符,以字母数字字符([a-z0-9A-Z]
)开头和结尾,
带有破折号(-
),下划线(_
),点( .
)和之间的字母数字。
前缀是可选的。如果指定,前缀必须是 DNS 子域:由点(.
)分隔的一系列 DNS 标签,总共不超过 253 个字符,
后跟斜杠(/
)。
如果省略前缀,则假定标签键对用户是私有的。
向最终用户对象添加标签的自动系统组件(例如 kube-scheduler
、kube-controller-manager
、
kube-apiserver
、kubectl
或其他第三方自动化工具)必须指定前缀。
kubernetes.io/
和 k8s.io/
前缀是为 Kubernetes 核心组件保留的 。
有效标签值:
必须为 63 个字符或更少(可以为空)
除非标签值为空,必须以字母数字字符([a-z0-9A-Z]
)开头和结尾
包含破折号(-
)、下划线(_
)、点(.
)和字母或数字
例如,以下是一个清单 (manifest),适用于具有 environment: production
和 app: nginx
这两个标签的 Pod:
apiVersion : v1
kind : Pod
metadata :
name : label-demo
labels :
environment : production
app : nginx
spec :
containers :
- name : nginx
image : nginx:1.14.2
ports :
- containerPort : 80
标签选择算符
与名称和 UID 不同,
标签不支持唯一性。通常,我们希望许多对象携带相同的标签。
通过标签选择算符 ,客户端/用户可以识别一组对象。标签选择算符是 Kubernetes 中的核心分组原语。
API 目前支持两种类型的选择算符:基于等值的 和基于集合的 。
标签选择算符可以由逗号分隔的多个需求 组成。
在多个需求的情况下,必须满足所有要求,因此逗号分隔符充当逻辑与 (&&
)运算符。
空标签选择算符或者未指定的选择算符的语义取决于上下文,
支持使用选择算符的 API 类别应该将算符的合法性和含义用文档记录下来。
说明:
对于某些 API 类别(例如 ReplicaSet)而言,两个实例的标签选择算符不得在命名空间内重叠,
否则它们的控制器将互相冲突,无法确定应该存在的副本个数。
注意:
对于基于等值的和基于集合的条件而言,不存在逻辑或(||
)操作符。
你要确保你的过滤语句按合适的方式组织。
基于等值的 需求
基于等值 或基于不等值 的需求允许按标签键和值进行过滤。
匹配对象必须满足所有指定的标签约束,尽管它们也可能具有其他标签。
可接受的运算符有 =
、==
和 !=
三种。
前两个表示相等 (并且是同义词),而后者表示不相等 。例如:
environment = production
tier != frontend
前者选择所有资源,其键名等于 environment
,值等于 production
。
后者选择所有资源,其键名等于 tier
,值不同于 frontend
,所有资源都没有带有 tier
键的标签。
可以使用逗号运算符来过滤 production
环境中的非 frontend
层资源:environment=production,tier!=frontend
。
基于等值的标签要求的一种使用场景是 Pod 要指定节点选择标准。
例如,下面的示例 Pod 选择存在 accelerator
标签且值为 nvidia-tesla-p100
的节点。
apiVersion : v1
kind : Pod
metadata :
name : cuda-test
spec :
containers :
- name : cuda-test
image : "registry.k8s.io/cuda-vector-add:v0.1"
resources :
limits :
nvidia.com/gpu : 1
nodeSelector :
accelerator : nvidia-tesla-p100
基于集合 的需求
基于集合 的标签需求允许你通过一组值来过滤键。
支持三种操作符:in
、notin
和 exists
(只可以用在键标识符上)。例如:
environment in (production, qa)
tier notin (frontend, backend)
partition
!partition
第一个示例选择了所有键等于 environment
并且值等于 production
或者 qa
的资源。
第二个示例选择了所有键等于 tier
并且值不等于 frontend
或者 backend
的资源,以及所有没有 tier
键标签的资源。
第三个示例选择了所有包含了有 partition
标签的资源;没有校验它的值。
第四个示例选择了所有没有 partition
标签的资源;没有校验它的值。
类似地,逗号分隔符充当与 运算符。因此,使用 partition
键(无论为何值)和
environment
不同于 qa
来过滤资源可以使用 partition, environment notin (qa)
来实现。
基于集合 的标签选择算符是相等标签选择算符的一般形式,因为 environment=production
等同于 environment in (production)
;!=
和 notin
也是类似的。
基于集合 的要求可以与基于相等 的要求混合使用。例如:partition in (customerA, customerB),environment!=qa
。
API
LIST 和 WATCH 过滤
LIST 和 WATCH 操作可以使用查询参数指定标签选择算符过滤一组对象。
两种需求都是允许的。(这里显示的是它们出现在 URL 查询字符串中)
基于等值 的需求:?labelSelector=environment%3Dproduction,tier%3Dfrontend
基于集合 的需求:?labelSelector=environment+in+%28production%2Cqa%29%2Ctier+in+%28frontend%29
两种标签选择算符都可以通过 REST 客户端用于 list 或者 watch 资源。
例如,使用 kubectl
定位 apiserver
,可以使用基于等值 的标签选择算符可以这么写:
kubectl get pods -l environment = production,tier= frontend
或者使用基于集合的 需求:
kubectl get pods -l 'environment in (production),tier in (frontend)'
正如刚才提到的,基于集合 的需求更具有表达力。例如,它们可以实现值的或 操作:
kubectl get pods -l 'environment in (production, qa)'
或者通过notin 运算符限制不匹配:
kubectl get pods -l 'environment,environment notin (frontend)'
在 API 对象中设置引用
一些 Kubernetes 对象,例如 services
和 replicationcontrollers
,
也使用了标签选择算符去指定了其他资源的集合,例如
pods 。
Service 和 ReplicationController
一个 Service
指向的一组 Pod 是由标签选择算符定义的。同样,一个 ReplicationController
应该管理的 Pod 的数量也是由标签选择算符定义的。
两个对象的标签选择算符都是在 json
或者 yaml
文件中使用映射定义的,并且只支持
基于等值 需求的选择算符:
"selector" : {
"component" : "redis" ,
}
或者
selector :
component : redis
这个选择算符(分别在 json
或者 yaml
格式中)等价于 component=redis
或 component in (redis)
。
支持基于集合需求的资源
比较新的资源,例如 Job
、
Deployment
、
ReplicaSet
和
DaemonSet
,
也支持基于集合的 需求。
selector :
matchLabels :
component : redis
matchExpressions :
- { key: tier, operator: In, values : [cache] }
- { key: environment, operator: NotIn, values : [dev] }
matchLabels
是由 {key,value}
对组成的映射。
matchLabels
映射中的单个 {key,value}
等同于 matchExpressions
的元素,
其 key
字段为 "key",operator
为 "In",而 values
数组仅包含 "value"。
matchExpressions
是 Pod 选择算符需求的列表。
有效的运算符包括 In
、NotIn
、Exists
和 DoesNotExist
。
在 In
和 NotIn
的情况下,设置的值必须是非空的。
来自 matchLabels
和 matchExpressions
的所有要求都按逻辑与的关系组合到一起
-- 它们必须都满足才能匹配。
选择节点集
通过标签进行选择的一个用例是确定节点集,方便 Pod 调度。
有关更多信息,请参阅选择节点 文档。
有效地使用标签
到目前为止我们使用的示例中的资源最多使用了一个标签。
在许多情况下,应使用多个标签来区分不同集合。
例如,不同的应用可能会为 app
标签设置不同的值。
但是,类似 guestbook 示例
这样的多层应用,还需要区分每一层。前端可能会带有以下标签:
labels :
app : guestbook
tier : frontend
Redis 的主从节点会有不同的 tier
标签,甚至还有一个额外的 role
标签:
labels :
app : guestbook
tier : backend
role : master
以及
labels :
app : guestbook
tier : backend
role : replica
标签使得我们能够按照所指定的任何维度对我们的资源进行切片和切块:
kubectl apply -f examples/guestbook/all-in-one/guestbook-all-in-one.yaml
kubectl get pods -Lapp -Ltier -Lrole
NAME READY STATUS RESTARTS AGE APP TIER ROLE
guestbook-fe-4nlpb 1/1 Running 0 1m guestbook frontend <none>
guestbook-fe-ght6d 1/1 Running 0 1m guestbook frontend <none>
guestbook-fe-jpy62 1/1 Running 0 1m guestbook frontend <none>
guestbook-redis-master-5pg3b 1/1 Running 0 1m guestbook backend master
guestbook-redis-replica-2q2yf 1/1 Running 0 1m guestbook backend replica
guestbook-redis-replica-qgazl 1/1 Running 0 1m guestbook backend replica
my-nginx-divi2 1/1 Running 0 29m nginx <none> <none>
my-nginx-o0ef1 1/1 Running 0 29m nginx <none> <none>
kubectl get pods -lapp= guestbook,role= replica
NAME READY STATUS RESTARTS AGE
guestbook-redis-replica-2q2yf 1/1 Running 0 3m
guestbook-redis-replica-qgazl 1/1 Running 0 3m
更新标签
有时需要要在创建新资源之前对现有的 Pod 和其它资源重新打标签。
这可以用 kubectl label
完成。
例如,如果想要将所有 NGINX Pod 标记为前端层,运行:
kubectl label pods -l app = nginx tier = fe
pod/my-nginx-2035384211-j5fhi labeled
pod/my-nginx-2035384211-u2c7e labeled
pod/my-nginx-2035384211-u3t6x labeled
首先用标签 "app=nginx" 过滤所有的 Pod,然后用 "tier=fe" 标记它们。
想要查看你刚设置了标签的 Pod,请运行:
kubectl get pods -l app = nginx -L tier
NAME READY STATUS RESTARTS AGE TIER
my-nginx-2035384211-j5fhi 1/1 Running 0 23m fe
my-nginx-2035384211-u2c7e 1/1 Running 0 23m fe
my-nginx-2035384211-u3t6x 1/1 Running 0 23m fe
此命令将输出所有 "app=nginx" 的 Pod,并有一个额外的描述 Pod 所在分层的标签列
(用参数 -L
或者 --label-columns
标明)。
想要了解更多信息,请参考标签 和
kubectl label
命令文档。
接下来
1.4 - 名字空间
在 Kubernetes 中,名字空间(Namespace) 提供一种机制,将同一集群中的资源划分为相互隔离的组。
同一名字空间内的资源名称要唯一,但跨名字空间时没有这个要求。
名字空间作用域仅针对带有名字空间的对象 ,
(例如 Deployment、Service 等),这种作用域对集群范围的对象
(例如 StorageClass、Node、PersistentVolume 等)不适用。
何时使用多个名字空间
名字空间适用于存在很多跨多个团队或项目的用户的场景。对于只有几到几十个用户的集群,根本不需要创建或考虑名字空间。当需要名字空间提供的功能时,请开始使用它们。
名字空间为名称提供了一个范围。资源的名称需要在名字空间内是唯一的,但不能跨名字空间。
名字空间不能相互嵌套,每个 Kubernetes 资源只能在一个名字空间中。
名字空间是在多个用户之间划分集群资源的一种方法(通过资源配额 )。
不必使用多个名字空间来分隔仅仅轻微不同的资源,例如同一软件的不同版本:
应该使用标签 来区分同一名字空间中的不同资源。
说明:
对于生产集群,请考虑不要 使用 default
名字空间,而是创建其他名字空间来使用。
初始名字空间
Kubernetes 启动时会创建四个初始名字空间:
default
Kubernetes 包含这个名字空间,以便于你无需创建新的名字空间即可开始使用新集群。
kube-node-lease
该名字空间包含用于与各个节点关联的 Lease(租约) 对象。
节点租约允许 kubelet 发送心跳 ,
由此控制面能够检测到节点故障。
kube-public
所有 的客户端(包括未经身份验证的客户端)都可以读取该名字空间。
该名字空间主要预留为集群使用,以便某些资源需要在整个集群中可见可读。
该名字空间的公共属性只是一种约定而非要求。
kube-system
该名字空间用于 Kubernetes 系统创建的对象。
使用名字空间
名字空间的创建和删除在名字空间的管理指南文档 描述。
说明:
避免使用前缀 kube-
创建名字空间,因为它是为 Kubernetes 系统名字空间保留的。
查看名字空间
你可以使用以下命令列出集群中现存的名字空间:
NAME STATUS AGE
default Active 1d
kube-node-lease Active 1d
kube-public Active 1d
kube-system Active 1d
为请求设置名字空间
要为当前请求设置名字空间,请使用 --namespace
参数。
例如:
kubectl run nginx --image= nginx --namespace= <名字空间名称>
kubectl get pods --namespace= <名字空间名称>
设置名字空间偏好
你可以永久保存名字空间,以用于对应上下文中所有后续 kubectl 命令。
kubectl config set-context --current --namespace= <名字空间名称>
# 验证
kubectl config view --minify | grep namespace:
名字空间和 DNS
当你创建一个服务 时,
Kubernetes 会创建一个相应的 DNS 条目 。
该条目的形式是 <服务名称>.<名字空间名称>.svc.cluster.local
,这意味着如果容器只使用
<服务名称>
,它将被解析到本地名字空间的服务。这对于跨多个名字空间(如开发、测试和生产)
使用相同的配置非常有用。如果你希望跨名字空间访问,则需要使用完全限定域名(FQDN)。
因此,所有的名字空间名称都必须是合法的
RFC 1123 DNS 标签 。
警告:
通过创建与公共顶级域名 同名的名字空间,
这些名字空间中的服务可以拥有与公共 DNS 记录重叠的、较短的 DNS 名称。
所有名字空间中的负载在执行 DNS 查找时,
如果查找的名称没有尾部句点 ,
就会被重定向到这些服务上,因此呈现出比公共 DNS 更高的优先序。
为了缓解这类问题,需要将创建名字空间的权限授予可信的用户。
如果需要,你可以额外部署第三方的安全控制机制,
例如以准入 Webhook
的形式,阻止用户创建与公共 TLD
同名的名字空间。
并非所有对象都在名字空间中
大多数 kubernetes 资源(例如 Pod、Service、副本控制器等)都位于某些名字空间中。
但是名字空间资源本身并不在名字空间中。而且底层资源,
例如节点 和持久化卷 不属于任何名字空间。
查看哪些 Kubernetes 资源在名字空间中,哪些不在名字空间中:
# 位于名字空间中的资源
kubectl api-resources --namespaced= true
# 不在名字空间中的资源
kubectl api-resources --namespaced= false
自动打标签
特性状态: Kubernetes 1.22 [stable]
Kubernetes 控制面会为所有名字空间设置一个不可变更的标签
kubernetes.io/metadata.name
。
标签的值是名字空间的名称。
接下来
1.5 - 注解
你可以使用 Kubernetes 注解为对象 附加任意的非标识的元数据。
客户端程序(例如工具和库)能够获取这些元数据信息。
为对象附加元数据
你可以使用标签或注解将元数据附加到 Kubernetes 对象。
标签可以用来选择对象和查找满足某些条件的对象集合。 相反,注解不用于标识和选择对象。
注解中的元数据,可以很小,也可以很大,可以是结构化的,也可以是非结构化的,能够包含标签不允许的字符。
可以在同一对象的元数据中同时使用标签和注解。
注解和标签一样,是键/值对:
"metadata" : {
"annotations" : {
"key1" : "value1" ,
"key2" : "value2"
}
}
说明:
Map 中的键和值必须是字符串。
换句话说,你不能使用数字、布尔值、列表或其他类型的键或值。
以下是一些例子,用来说明哪些信息可以使用注解来记录:
由声明性配置所管理的字段。
将这些字段附加为注解,能够将它们与客户端或服务端设置的默认值、
自动生成的字段以及通过自动调整大小或自动伸缩系统设置的字段区分开来。
构建、发布或镜像信息(如时间戳、发布 ID、Git 分支、PR 数量、镜像哈希、仓库地址)。
指向日志记录、监控、分析或审计仓库的指针。
可用于调试目的的客户端库或工具信息:例如,名称、版本和构建信息。
用户或者工具/系统的来源信息,例如来自其他生态系统组件的相关对象的 URL。
轻量级上线工具的元数据信息:例如,配置或检查点。
负责人员的电话或呼机号码,或指定在何处可以找到该信息的目录条目,如团队网站。
从用户到最终运行的指令,以修改行为或使用非标准功能。
你可以将这类信息存储在外部数据库或目录中而不使用注解,
但这样做就使得开发人员很难生成用于部署、管理、自检的客户端共享库和工具。
语法和字符集
注解(Annotations) 存储的形式是键/值对。有效的注解键分为两部分:
可选的前缀和名称,以斜杠(/
)分隔。
名称段是必需项,并且必须在 63 个字符以内,以字母数字字符([a-z0-9A-Z]
)开头和结尾,
并允许使用破折号(-
),下划线(_
),点(.
)和字母数字。
前缀是可选的。如果指定,则前缀必须是 DNS 子域:一系列由点(.
)分隔的 DNS 标签,
总计不超过 253 个字符,后跟斜杠(/
)。
如果省略前缀,则假定注解键对用户是私有的。 由系统组件添加的注解
(例如,kube-scheduler
,kube-controller-manager
,kube-apiserver
,kubectl
或其他第三方组件),必须为终端用户添加注解前缀。
kubernetes.io/
和 k8s.io/
前缀是为 Kubernetes 核心组件保留的。
例如,下面是一个 Pod 的清单,其注解中包含 imageregistry: https://hub.docker.com/
:
apiVersion : v1
kind : Pod
metadata :
name : annotations-demo
annotations :
imageregistry : "https://hub.docker.com/"
spec :
containers :
- name : nginx
image : nginx:1.14.2
ports :
- containerPort : 80
接下来
1.6 - 字段选择器
“字段选择器(Field selectors)”允许你根据一个或多个资源字段的值筛选
Kubernetes 对象 。
下面是一些使用字段选择器查询的例子:
metadata.name=my-service
metadata.namespace!=default
status.phase=Pending
下面这个 kubectl
命令将筛选出
status.phase
字段值为 Running
的所有 Pod:
kubectl get pods --field-selector status.phase= Running
说明:
字段选择器本质上是资源“过滤器(Filters)”。默认情况下,字段选择器/过滤器是未被应用的,
这意味着指定类型的所有资源都会被筛选出来。
这使得 kubectl get pods
和 kubectl get pods --field-selector ""
这两个 kubectl
查询是等价的。
支持的字段
不同的 Kubernetes 资源类型支持不同的字段选择器。
所有资源类型都支持 metadata.name
和 metadata.namespace
字段。
使用不被支持的字段选择器会产生错误。例如:
kubectl get ingress --field-selector foo.bar= baz
Error from server (BadRequest): Unable to find "ingresses" that match label selector "", field selector "foo.bar=baz": "foo.bar" is not a known field selector: only "metadata.name", "metadata.namespace"
支持字段列表
类别
字段
Pod
spec.nodeName
spec.restartPolicy
spec.schedulerName
spec.serviceAccountName
spec.hostNetwork
status.phase
status.podIP
status.nominatedNodeName
Event
involvedObject.kind
involvedObject.namespace
involvedObject.name
involvedObject.uid
involvedObject.apiVersion
involvedObject.resourceVersion
involvedObject.fieldPath
reason
reportingComponent
source
type
Secret
type
Namespace
status.phase
ReplicaSet
status.replicas
ReplicationController
status.replicas
Job
status.successful
Node
spec.unschedulable
CertificateSigningRequest
spec.signerName
支持的操作符
你可在字段选择器中使用 =
、==
和 !=
(=
和 ==
的意义是相同的)操作符。
例如,下面这个 kubectl
命令将筛选所有不属于 default
命名空间的 Kubernetes 服务:
kubectl get services --all-namespaces --field-selector metadata.namespace!= default
说明:
基于集合的操作符
(in
、notin
、exists
)不支持字段选择算符。
链式选择器
同标签 和其他选择器一样,
字段选择器可以通过使用逗号分隔的列表组成一个选择链。
下面这个 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
1.7 - Finalizers
Finalizer 是带有命名空间的键,告诉 Kubernetes 等到特定的条件被满足后,
再完全删除被标记为删除的资源。
Finalizer 提醒控制器 清理被删除的对象拥有的资源。
当你告诉 Kubernetes 删除一个指定了 Finalizer 的对象时,
Kubernetes API 通过填充 .metadata.deletionTimestamp
来标记要删除的对象,
并返回 202
状态码(HTTP "已接受") 使其进入只读状态。
此时控制平面或其他组件会采取 Finalizer 所定义的行动,
而目标对象仍然处于终止中(Terminating)的状态。
这些行动完成后,控制器会删除目标对象相关的 Finalizer。
当 metadata.finalizers
字段为空时,Kubernetes 认为删除已完成并删除对象。
你可以使用 Finalizer 控制资源的垃圾收集 。
例如,你可以定义一个 Finalizer,在删除目标资源前清理相关资源或基础设施。
你可以使用 Finalizers 来控制对象 的垃圾回收 ,
方法是在删除目标资源之前提醒控制器 执行特定的清理任务。
Finalizers 通常不指定要执行的代码。
相反,它们通常是特定资源上的键的列表,类似于注解。
Kubernetes 自动指定了一些 Finalizers,但你也可以指定你自己的。
Finalizers 如何工作
当你使用清单文件创建资源时,你可以在 metadata.finalizers
字段指定 Finalizers。
当你试图删除该资源时,处理删除请求的 API 服务器会注意到 finalizers
字段中的值,
并进行以下操作:
修改对象,将你开始执行删除的时间添加到 metadata.deletionTimestamp
字段。
禁止对象被删除,直到其 metadata.finalizers
字段内的所有项被删除。
返回 202
状态码(HTTP "Accepted")。
管理 finalizer 的控制器注意到对象上发生的更新操作,对象的 metadata.deletionTimestamp
被设置,意味着已经请求删除该对象。然后,控制器会试图满足资源的 Finalizers 的条件。
每当一个 Finalizer 的条件被满足时,控制器就会从资源的 finalizers
字段中删除该键。
当 finalizers
字段为空时,deletionTimestamp
字段被设置的对象会被自动删除。
你也可以使用 Finalizers 来阻止删除未被管理的资源。
一个常见的 Finalizer 的例子是 kubernetes.io/pv-protection
,
它用来防止意外删除 PersistentVolume
对象。
当一个 PersistentVolume
对象被 Pod 使用时,
Kubernetes 会添加 pv-protection
Finalizer。
如果你试图删除 PersistentVolume
,它将进入 Terminating
状态,
但是控制器因为该 Finalizer 存在而无法删除该资源。
当 Pod 停止使用 PersistentVolume
时,
Kubernetes 清除 pv-protection
Finalizer,控制器就会删除该卷。
说明:
当你 DELETE
一个对象时,Kubernetes 为该对象增加删除时间戳,然后立即开始限制
对这个正处于待删除状态的对象的 .metadata.finalizers
字段进行修改。
你可以删除现有的 finalizers (从 finalizers
列表删除条目),但你不能添加新的 finalizer。
对象的 deletionTimestamp
被设置后也不能修改。
删除请求已被发出之后,你无法复活该对象。唯一的方法是删除它并创建一个新的相似对象。
属主引用、标签和 Finalizers
与标签 类似,
属主引用
描述了 Kubernetes 中对象之间的关系,但它们作用不同。
当一个控制器
管理类似于 Pod 的对象时,它使用标签来跟踪相关对象组的变化。
例如,当 Job 创建一个或多个 Pod 时,
Job 控制器会给这些 Pod 应用上标签,并跟踪集群中的具有相同标签的 Pod 的变化。
Job 控制器还为这些 Pod 添加了“属主引用”,指向创建 Pod 的 Job。
如果你在这些 Pod 运行的时候删除了 Job,
Kubernetes 会使用属主引用(而不是标签)来确定集群中哪些 Pod 需要清理。
当 Kubernetes 识别到要删除的资源上的属主引用时,它也会处理 Finalizers。
在某些情况下,Finalizers 会阻止依赖对象的删除,
这可能导致目标属主对象被保留的时间比预期的长,而没有被完全删除。
在这些情况下,你应该检查目标属主和附属对象上的 Finalizers 和属主引用,来排查原因。
说明:
在对象卡在删除状态的情况下,要避免手动移除 Finalizers,以允许继续删除操作。
Finalizers 通常因为特殊原因被添加到资源上,所以强行删除它们会导致集群出现问题。
只有了解 finalizer 的用途时才能这样做,并且应该通过一些其他方式来完成
(例如,手动清除其余的依赖对象)。
接下来
1.8 - 属主与附属
在 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 不允许跨名字空间指定属主。
名字空间范围的附属可以指定集群范围的或者名字空间范围的属主。
名字空间范围的属主必须 和该附属处于相同的名字空间。
如果名字空间范围的属主和附属不在相同的名字空间,那么该属主引用就会被认为是缺失的,
并且当附属的所有属主引用都被确认不再存在之后,该附属就会被删除。
集群范围的附属只能指定集群范围的属主。
在 v1.20+ 版本,如果一个集群范围的附属指定了一个名字空间范围类型的属主,
那么该附属就会被认为是拥有一个不可解析的属主引用,并且它不能够被垃圾回收。
在 v1.20+ 版本,如果垃圾收集器检测到无效的跨名字空间的属主引用,
或者一个集群范围的附属指定了一个名字空间范围类型的属主,
那么它就会报告一个警告事件。该事件的原因是 OwnerRefInvalidNamespace
,
involvedObject
属性中包含无效的附属。
你可以运行 kubectl get events -A --field-selector=reason=OwnerRefInvalidNamespace
来获取该类型的事件。
属主关系与 Finalizer
当你告诉 Kubernetes 删除一个资源,API 服务器允许管理控制器处理该资源的任何
Finalizer 规则 。
Finalizer
防止意外删除你的集群所依赖的、用于正常运作的资源。
例如,如果你试图删除一个仍被 Pod 使用的 PersistentVolume
,该资源不会被立即删除,
因为 PersistentVolume 有
kubernetes.io/pv-protection
Finalizer。
相反,数据卷 将进入 Terminating
状态,
直到 Kubernetes 清除这个 Finalizer,而这种情况只会发生在 PersistentVolume
不再被挂载到 Pod 上时。
当你使用前台或孤立级联删除 时,
Kubernetes 也会向属主资源添加 Finalizer。
在前台删除中,会添加 foreground
Finalizer,这样控制器必须在删除了拥有
ownerReferences.blockOwnerDeletion=true
的附属资源后,才能删除属主对象。
如果你指定了孤立删除策略,Kubernetes 会添加 orphan
Finalizer,
这样控制器在删除属主对象后,会忽略附属资源。
接下来
1.9 - 推荐使用的标签
除了 kubectl 和 dashboard 之外,你还可以使用其他工具来可视化和管理 Kubernetes 对象。
一组通用的标签可以让多个工具之间相互操作,用所有工具都能理解的通用方式描述对象。
除了支持工具外,推荐的标签还以一种可以查询的方式描述了应用程序。
元数据围绕 应用(application) 的概念进行组织。Kubernetes
不是平台即服务(PaaS),没有或强制执行正式的应用程序概念。
相反,应用程序是非正式的,并使用元数据进行描述。应用程序包含的定义是松散的。
说明:
这些是推荐的标签。它们使管理应用程序变得更容易但不是任何核心工具所必需的。
共享标签和注解都使用同一个前缀:app.kubernetes.io
。没有前缀的标签是用户私有的。
共享前缀可以确保共享标签不会干扰用户自定义的标签。
标签
为了充分利用这些标签,应该在每个资源对象上都使用它们。
键
描述
示例
类型
app.kubernetes.io/name
应用程序的名称
mysql
字符串
app.kubernetes.io/instance
用于唯一确定应用实例的名称
mysql-abcxyz
字符串
app.kubernetes.io/version
应用程序的当前版本(例如语义版本 1.0 、修订版哈希等)
5.7.21
字符串
app.kubernetes.io/component
架构中的组件
database
字符串
app.kubernetes.io/part-of
此级别的更高级别应用程序的名称
wordpress
字符串
app.kubernetes.io/managed-by
用于管理应用程序的工具
Helm
字符串
为说明这些标签的实际使用情况,请看下面的 StatefulSet 对象:
# 这是一段节选
apiVersion : apps/v1
kind : StatefulSet
metadata :
labels :
app.kubernetes.io/name : mysql
app.kubernetes.io/instance : mysql-abcxyz
app.kubernetes.io/version : "5.7.21"
app.kubernetes.io/component : database
app.kubernetes.io/part-of : wordpress
app.kubernetes.io/managed-by : Helm
应用和应用实例
应用可以在 Kubernetes 集群中安装一次或多次。在某些情况下,可以安装在同一命名空间中。
例如,可以不止一次地为不同的站点安装不同的 WordPress。
应用的名称和实例的名称是分别记录的。例如,WordPress 应用的
app.kubernetes.io/name
为 wordpress
,而其实例名称
app.kubernetes.io/instance
为 wordpress-abcxyz
。
这使得应用和应用的实例均可被识别,应用的每个实例都必须具有唯一的名称。
示例
为了说明使用这些标签的不同方式,以下示例具有不同的复杂性。
一个简单的无状态服务
考虑使用 Deployment
和 Service
对象部署的简单无状态服务的情况。
以下两个代码段表示如何以最简单的形式使用标签。
下面的 Deployment
用于监督运行应用本身的那些 Pod。
apiVersion : apps/v1
kind : Deployment
metadata :
labels :
app.kubernetes.io/name : myservice
app.kubernetes.io/instance : myservice-abcxyz
...
下面的 Service
用于暴露应用。
apiVersion : v1
kind : Service
metadata :
labels :
app.kubernetes.io/name : myservice
app.kubernetes.io/instance : myservice-abcxyz
...
带有一个数据库的 Web 应用程序
考虑一个稍微复杂的应用:一个使用 Helm 安装的 Web 应用(WordPress),
其中使用了数据库(MySQL)。以下代码片段说明用于部署此应用程序的对象的开始。
以下 Deployment
的开头用于 WordPress:
apiVersion : apps/v1
kind : Deployment
metadata :
labels :
app.kubernetes.io/name : wordpress
app.kubernetes.io/instance : wordpress-abcxyz
app.kubernetes.io/version : "4.9.4"
app.kubernetes.io/managed-by : Helm
app.kubernetes.io/component : server
app.kubernetes.io/part-of : wordpress
...
这个 Service
用于暴露 WordPress:
apiVersion : v1
kind : Service
metadata :
labels :
app.kubernetes.io/name : wordpress
app.kubernetes.io/instance : wordpress-abcxyz
app.kubernetes.io/version : "4.9.4"
app.kubernetes.io/managed-by : Helm
app.kubernetes.io/component : server
app.kubernetes.io/part-of : wordpress
...
MySQL 作为一个 StatefulSet
暴露,包含它和它所属的较大应用程序的元数据:
apiVersion : apps/v1
kind : StatefulSet
metadata :
labels :
app.kubernetes.io/name : mysql
app.kubernetes.io/instance : mysql-abcxyz
app.kubernetes.io/version : "5.7.21"
app.kubernetes.io/managed-by : Helm
app.kubernetes.io/component : database
app.kubernetes.io/part-of : wordpress
...
Service
用于将 MySQL 作为 WordPress 的一部分暴露:
apiVersion : v1
kind : Service
metadata :
labels :
app.kubernetes.io/name : mysql
app.kubernetes.io/instance : mysql-abcxyz
app.kubernetes.io/version : "5.7.21"
app.kubernetes.io/managed-by : Helm
app.kubernetes.io/component : database
app.kubernetes.io/part-of : wordpress
...
使用 MySQL StatefulSet
和 Service
,你会注意到有关 MySQL 和 WordPress 的信息,包括更广泛的应用程序。
2 - Kubernetes 组件
Kubernetes 集群由控制平面的组件和一组称为节点的机器组成。
当你部署完 Kubernetes,便拥有了一个完整的集群。
一组工作机器,称为节点 ,
会运行容器化应用程序。每个集群至少有一个工作节点。
工作节点会托管 Pod ,而 Pod 就是作为应用负载的组件。
控制平面 管理集群中的工作节点和 Pod。
在生产环境中,控制平面通常跨多台计算机运行,
一个集群通常运行多个节点,提供容错性和高可用性。
本文档概述了一个正常运行的 Kubernetes 集群所需的各种组件。
Kubernetes 集群的组件
控制平面组件(Control Plane Components)
控制平面组件会为集群做出全局决策,比如资源的调度。
以及检测和响应集群事件,例如当不满足部署的 replicas
字段时,要启动新的 Pod )。
控制平面组件可以在集群中的任何节点上运行。
然而,为了简单起见,设置脚本通常会在同一个计算机上启动所有控制平面组件,
并且不会在此计算机上运行用户容器。
请参阅使用 kubeadm 构建高可用性集群
中关于跨多机器控制平面设置的示例。
kube-apiserver
API 服务器是 Kubernetes 控制平面 的组件,
该组件负责公开了 Kubernetes API,负责处理接受请求的工作。
API 服务器是 Kubernetes 控制平面的前端。
Kubernetes API 服务器的主要实现是 kube-apiserver 。
kube-apiserver
设计上考虑了水平扩缩,也就是说,它可通过部署多个实例来进行扩缩。
你可以运行 kube-apiserver
的多个实例,并在这些实例之间平衡流量。
etcd
一致且高可用的键值存储,用作 Kubernetes 所有集群数据的后台数据库。
如果你的 Kubernetes 集群使用 etcd 作为其后台数据库,
请确保你针对这些数据有一份
备份 计划。
你可以在官方文档 中找到有关 etcd 的深入知识。
kube-scheduler
kube-scheduler
是控制平面 的组件,
负责监视新创建的、未指定运行节点(node) 的 Pods ,
并选择节点来让 Pod 在上面运行。
调度决策考虑的因素包括单个 Pod 及 Pods 集合的资源需求、软硬件及策略约束、
亲和性及反亲和性规范、数据位置、工作负载间的干扰及最后时限。
kube-controller-manager
kube-controller-manager
是控制平面 的组件,
负责运行控制器 进程。
从逻辑上讲,
每个控制器 都是一个单独的进程,
但是为了降低复杂性,它们都被编译到同一个可执行文件,并在同一个进程中运行。
有许多不同类型的控制器。以下是一些例子:
节点控制器(Node Controller):负责在节点出现故障时进行通知和响应
任务控制器(Job Controller):监测代表一次性任务的 Job 对象,然后创建 Pod 来运行这些任务直至完成
端点分片控制器(EndpointSlice controller):填充端点分片(EndpointSlice)对象(以提供 Service 和 Pod 之间的链接)。
服务账号控制器(ServiceAccount controller):为新的命名空间创建默认的服务账号(ServiceAccount)。
以上并不是一个详尽的列表。
cloud-controller-manager
一个 Kubernetes
控制平面 组件,
嵌入了特定于云平台的控制逻辑。
云控制器管理器(Cloud Controller Manager)允许将你的集群连接到云提供商的 API 之上,
并将与该云平台交互的组件同与你的集群交互的组件分离开来。
cloud-controller-manager
仅运行特定于云平台的控制器。
因此如果你在自己的环境中运行 Kubernetes,或者在本地计算机中运行学习环境,
所部署的集群不需要有云控制器管理器。
与 kube-controller-manager
类似,cloud-controller-manager
将若干逻辑上独立的控制回路组合到同一个可执行文件中,
供你以同一进程的方式运行。
你可以对其执行水平扩容(运行不止一个副本)以提升性能或者增强容错能力。
下面的控制器都包含对云平台驱动的依赖:
节点控制器(Node Controller):用于在节点终止响应后检查云提供商以确定节点是否已被删除
路由控制器(Route Controller):用于在底层云基础架构中设置路由
服务控制器(Service Controller):用于创建、更新和删除云提供商负载均衡器
Node 组件
节点组件会在每个节点上运行,负责维护运行的 Pod 并提供 Kubernetes 运行环境。
kubelet
kubelet
会在集群中每个节点(node) 上运行。
它保证容器(containers) 都运行在
Pod 中。
kubelet
接收一组通过各类机制提供给它的 PodSpec,确保这些 PodSpec 中描述的容器处于运行状态且健康。
kubelet 不会管理不是由 Kubernetes 创建的容器。
kube-proxy
kube-proxy
是集群中每个节点(node) 上所运行的网络代理,
实现 Kubernetes 服务(Service) 概念的一部分。
kube-proxy 维护节点上的一些网络规则,
这些网络规则会允许从集群内部或外部的网络会话与 Pod 进行网络通信。
如果操作系统提供了可用的数据包过滤层,则 kube-proxy 会通过它来实现网络规则。
否则,kube-proxy 仅做流量转发。
容器运行时(Container Runtime)
这个基础组件使 Kubernetes 能够有效运行容器。
它负责管理 Kubernetes 环境中容器的执行和生命周期。
Kubernetes 支持许多容器运行环境,例如
containerd 、
CRI-O
以及 Kubernetes CRI (容器运行环境接口)
的其他任何实现。
插件(Addons)
插件使用 Kubernetes 资源(DaemonSet 、
Deployment 等)实现集群功能。
因为这些插件提供集群级别的功能,插件中命名空间域的资源属于 kube-system
命名空间。
下面描述众多插件中的几种。有关可用插件的完整列表,请参见
插件(Addons) 。
DNS
尽管其他插件都并非严格意义上的必需组件,但几乎所有 Kubernetes
集群都应该有集群 DNS ,
因为很多示例都需要 DNS 服务。
集群 DNS 是一个 DNS 服务器,和环境中的其他 DNS 服务器一起工作,它为 Kubernetes 服务提供 DNS 记录。
Kubernetes 启动的容器自动将此 DNS 服务器包含在其 DNS 搜索列表中。
Web 界面(仪表盘)
Dashboard
是 Kubernetes 集群的通用的、基于 Web 的用户界面。
它使用户可以管理集群中运行的应用程序以及集群本身,
并进行故障排除。
容器资源监控
容器资源监控
将关于容器的一些常见的时间序列度量值保存到一个集中的数据库中,
并提供浏览这些数据的界面。
集群层面日志
集群层面日志 机制负责将容器的日志数据保存到一个集中的日志存储中,
这种集中日志存储提供搜索和浏览接口。
网络插件
网络插件
是实现容器网络接口(CNI)规范的软件组件。它们负责为 Pod 分配 IP 地址,并使这些 Pod 能在集群内部相互通信。
接下来
进一步了解以下内容:
3 - Kubernetes API
Kubernetes API 使你可以查询和操纵 Kubernetes 中对象的状态。 Kubernetes 控制平面的核心是 API 服务器和它暴露的 HTTP API。 用户、集群的不同部分以及外部组件都通过 API 服务器相互通信。
Kubernetes 控制面 的核心是
API 服务器 。
API 服务器负责提供 HTTP API,以供用户、集群中的不同部分和集群外部组件相互通信。
Kubernetes API 使你可以在 Kubernetes 中查询和操纵 API 对象
(例如 Pod、Namespace、ConfigMap 和 Event)的状态。
大部分操作都可以通过 kubectl 命令行接口或类似
kubeadm 这类命令行工具来执行,
这些工具在背后也是调用 API。不过,你也可以使用 REST 调用来访问这些 API。
Kubernetes 为那些希望使用 Kubernetes API
编写应用的开发者提供一组客户端库 。
每个 Kubernetes 集群都会发布集群所使用的 API 规范。
Kubernetes 使用两种机制来发布这些 API 规范;这两种机制都有助于实现自动互操作。
例如,kubectl
工具获取并缓存 API 规范,以实现命令行补全和其他特性。所支持的两种机制如下:
发现 API 提供有关 Kubernetes API 的信息:API 名称、资源、版本和支持的操作。
此 API 是特定于 Kubernetes 的一个术语,因为它是一个独立于 Kubernetes OpenAPI 的 API。
其目的是为可用的资源提供简要总结,不详细说明资源的具体模式。有关资源模式的参考,请参阅 OpenAPI 文档。
Kubernetes OpenAPI 文档 为所有 Kubernetes API 端点提供(完整的)
OpenAPI v2.0 和 v3.0 模式 。OpenAPI v3 是访问 OpenAPI 的首选方法,
因为它提供了更全面和准确的 API 视图。其中包括所有可用的 API 路径,以及每个端点上每个操作所接收和生成的所有资源。
它还包括集群支持的所有可扩展组件。这些数据是完整的规范,比 Discovery API 提供的规范要大得多。
Discovery API
Kubernetes 通过 Discovery API 发布集群所支持的所有组版本和资源列表。对于每个资源,包括以下内容:
名称
集群作用域还是名字空间作用域
端点 URL 和所支持的动词
别名
组、版本、类别
API 以聚合和非聚合形式提供。聚合的发现提供两个端点,而非聚合的发现为每个组版本提供单独的端点。
聚合的发现
特性状态: Kubernetes v1.30 [stable]
Kubernetes 为聚合的发现 提供了 Beta 支持,通过两个端点(/api
和 /apis
)发布集群所支持的所有资源。
请求这个端点会大大减少从集群获取发现数据时发送的请求数量。你可以通过带有
Accept
头(Accept: application/json;v=v2beta1;g=apidiscovery.k8s.io;as=APIGroupDiscoveryList
)
的请求发送到不同端点,来指明聚合发现的资源。
如果没有使用 Accept
头指示资源类型,对于 /api
和 /apis
端点的默认响应将是一个非聚合的发现文档。
内置资源的发现文档 可以在
Kubernetes GitHub 代码仓库中找到。如果手头没有 Kubernetes 集群可供查询,
此 Github 文档可用作可用资源的基础集合的参考。端点还支持 ETag 和 protobuf 编码。
非聚合的发现
在不使用聚合发现的情况下,发现 API 以不同级别发布,同时根端点为下游文档发布发现信息。
集群支持的所有组版本列表发布在 /api
和 /apis
端点。例如:
{
"kind": "APIGroupList",
"apiVersion": "v1",
"groups": [
{
"name": "apiregistration.k8s.io",
"versions": [
{
"groupVersion": "apiregistration.k8s.io/v1",
"version": "v1"
}
],
"preferredVersion": {
"groupVersion": "apiregistration.k8s.io/v1",
"version": "v1"
}
},
{
"name": "apps",
"versions": [
{
"groupVersion": "apps/v1",
"version": "v1"
}
],
"preferredVersion": {
"groupVersion": "apps/v1",
"version": "v1"
}
},
...
}
用户需要发出额外的请求才能在 /apis/<group>/<version>
(例如 /apis/rbac.authorization.k8s.io/v1alpha1
)
获取每个组版本的发现文档。这些发现文档会公布在特定组版本下所提供的资源列表。
kubectl 使用这些端点来获取某集群所支持的资源列表。
OpenAPI 接口定义
有关 OpenAPI 规范的细节,参阅 OpenAPI 文档 。
Kubernetes 同时提供 OpenAPI v2.0 和 OpenAPI v3.0。OpenAPI v3 是访问 OpenAPI 的首选方法,
因为它提供了对 Kubernetes 资源更全面(无损)的表示。由于 OpenAPI v2 的限制,
所公布的 OpenAPI 中会丢弃掉一些字段,包括但不限于 default
、nullable
、oneOf
。
OpenAPI v2
Kubernetes API 服务器通过 /openapi/v2
端点提供聚合的 OpenAPI v2 规范。
你可以按照下表所给的请求头部,指定响应的格式:
OpenAPI v2 查询请求的合法头部值
头部
可选值
说明
Accept-Encoding
gzip
不指定此头部也是可以的
Accept
application/com.github.proto-openapi.spec.v2@v1.0+protobuf
主要用于集群内部
application/json
默认值
*
提供 application/json
OpenAPI v3
特性状态: Kubernetes v1.27 [stable]
Kubernetes 支持将其 API 的描述以 OpenAPI v3 形式发布。
发现端点 /openapi/v3
被提供用来查看可用的所有组、版本列表。
此列表仅返回 JSON。这些组、版本以下面的格式提供:
{
"paths": {
...,
"api/v1": {
"serverRelativeURL": "/openapi/v3/api/v1?hash=CC0E9BFD992D8C59AEC98A1E2336F899E8318D3CF4C68944C3DEC640AF5AB52D864AC50DAA8D145B3494F75FA3CFF939FCBDDA431DAD3CA79738B297795818CF"
},
"apis/admissionregistration.k8s.io/v1": {
"serverRelativeURL": "/openapi/v3/apis/admissionregistration.k8s.io/v1?hash=E19CC93A116982CE5422FC42B590A8AFAD92CDE9AE4D59B5CAAD568F083AD07946E6CB5817531680BCE6E215C16973CD39003B0425F3477CFD854E89A9DB6597"
},
....
}
}
为了改进客户端缓存,相对的 URL 会指向不可变的 OpenAPI 描述。
为了此目的,API 服务器也会设置正确的 HTTP 缓存标头
(Expires
为未来 1 年,和 Cache-Control
为 immutable
)。
当一个过时的 URL 被使用时,API 服务器会返回一个指向最新 URL 的重定向。
Kubernetes API 服务器会在端点 /openapi/v3/apis/<group>/<version>?hash=<hash>
发布一个 Kubernetes 组版本的 OpenAPI v3 规范。
请参阅下表了解可接受的请求头部。
OpenAPI v3 查询的合法请求头部值
头部
可选值
说明
Accept-Encoding
gzip
不提供此头部也是可接受的
Accept
application/com.github.proto-openapi.spec.v3@v1.0+protobuf
主要用于集群内部使用
application/json
默认
*
以 application/json
形式返回
k8s.io/client-go/openapi3
包中提供了获取 OpenAPI v3 的 Golang 实现。
Kubernetes 1.30 发布了 OpenAPI v2.0 和 v3.0;
近期没有支持 v3.1 的计划。
Protobuf 序列化
Kubernetes 为 API 实现了一种基于 Protobuf 的序列化格式,主要用于集群内部通信。
关于此格式的详细信息,可参考
Kubernetes Protobuf 序列化 设计提案。
每种模式对应的接口描述语言(IDL)位于定义 API 对象的 Go 包中。
持久化
Kubernetes 通过将序列化状态的对象写入到 etcd 中完成存储操作。
API 组和版本控制
为了更容易消除字段或重组资源的呈现方式,Kubernetes 支持多个 API 版本,每个版本位于不同的 API 路径,
例如 /api/v1
或 /apis/rbac.authorization.k8s.io/v1alpha1
。
版本控制是在 API 级别而不是在资源或字段级别完成的,以确保 API 呈现出清晰、一致的系统资源和行为视图,
并能够控制对生命结束和/或实验性 API 的访问。
为了更容易演进和扩展其 API,Kubernetes 实现了 API 组 ,
这些 API 组可以被启用或禁用 。
API 资源通过其 API 组、资源类型、名字空间(用于名字空间作用域的资源)和名称来区分。
API 服务器透明地处理 API 版本之间的转换:所有不同的版本实际上都是相同持久化数据的呈现。
API 服务器可以通过多个 API 版本提供相同的底层数据。
例如,假设针对相同的资源有两个 API 版本:v1
和 v1beta1
。
如果你最初使用其 API 的 v1beta1
版本创建了一个对象,
你稍后可以使用 v1beta1
或 v1
API 版本来读取、更新或删除该对象,
直到 v1beta1
版本被废弃和移除为止。此后,你可以使用 v1
API 继续访问和修改该对象。
API 变更
任何成功的系统都要随着新的使用案例的出现和现有案例的变化来成长和变化。
为此,Kubernetes 已设计了 Kubernetes API 来持续变更和成长。
Kubernetes 项目的目标是不要 给现有客户端带来兼容性问题,并在一定的时期内维持这种兼容性,
以便其他项目有机会作出适应性变更。
一般而言,新的 API 资源和新的资源字段可以被频繁地添加进来。
删除资源或者字段则要遵从
API 废弃策略 。
Kubernetes 对维护达到正式发布(GA)阶段的官方 API 的兼容性有着很强的承诺,通常这一 API 版本为 v1
。
此外,Kubernetes 保持与 Kubernetes 官方 API 的 Beta API 版本持久化数据的兼容性,
并确保在该功能特性已进入稳定期时数据可以通过 GA API 版本进行转换和访问。
如果你采用一个 Beta API 版本,一旦该 API 进阶,你将需要转换到后续的 Beta 或稳定的 API 版本。
执行此操作的最佳时间是 Beta API 处于弃用期,因为此时可以通过两个 API 版本同时访问那些对象。
一旦 Beta API 结束其弃用期并且不再提供服务,则必须使用替换的 API 版本。
说明:
尽管 Kubernetes 也努力为 Alpha API 版本维护兼容性,在有些场合兼容性是无法做到的。
如果你使用了任何 Alpha API 版本,需要在升级集群时查看 Kubernetes 发布说明,
如果 API 确实以不兼容的方式发生变更,则需要在升级之前删除所有现有的 Alpha 对象。
关于 API 版本分级的定义细节,请参阅
API 版本参考 页面。
API 扩展
有两种途径来扩展 Kubernetes API:
你可以使用自定义资源 来以声明式方式定义
API 服务器如何提供你所选择的资源 API。
你也可以选择实现自己的聚合层 来扩展
Kubernetes API。
接下来