节点
Kubernetes 通过将容器放入 Pod 中运行在**节点(Node)**上来运行你的工作负载。节点可以是虚拟机或物理机,具体取决于集群。每个节点都由控制平面管理,并包含运行Pod所需的必要服务。
通常一个集群中包含若干个节点;在学习或资源受限的环境中,你可能只有一个节点。
节点上的组件包括 kubelet、容器运行时 和 kube-proxy。
管理
有两种主要方式将节点添加到API 服务器
- 节点上的 kubelet 自行向控制平面注册
- 你(或另一个用户)手动添加一个 Node 对象
在你创建 Node 对象后,或者节点上的 kubelet 自行注册后,控制平面会检查新的 Node 对象是否有效。例如,如果你尝试使用以下 JSON Manifest 创建一个 Node:
{
"kind": "Node",
"apiVersion": "v1",
"metadata": {
"name": "10.240.79.157",
"labels": {
"name": "my-first-k8s-node"
}
}
}
Kubernetes 内部会创建一个 Node 对象(它的表示)。Kubernetes 会检查是否有 kubelet 注册到与该 Node 的 metadata.name
字段匹配的 API 服务器。如果节点健康(即所有必要服务都在运行),那么它就有资格运行 Pod。否则,该节点将被忽略,直到它变得健康。
Node 对象的名称必须是合法的DNS 子域名。
节点名称的唯一性
名称标识一个节点。两个节点不能同时拥有相同的名称。Kubernetes 也假设名称相同的资源是同一个对象。对于节点来说,它隐式地假设使用相同名称的实例将具有相同的状态(例如网络设置、根磁盘内容)以及节点标签等属性。如果实例在未更改名称的情况下被修改,可能会导致不一致。如果需要替换或显著更新节点,则需要先从 API 服务器中移除现有的 Node 对象,然后在更新后再重新添加。
节点自注册
当 kubelet 的 --register-node
标志设置为 true(默认值)时,kubelet 会尝试向 API 服务器注册自己。这是大多数发行版使用的首选模式。
对于自注册,kubelet 以以下选项启动:
--kubeconfig
- 用于向 API 服务器进行自身认证的凭据路径。--cloud-provider
- 如何与云提供商通信以读取自身元数据。--register-node
- 自动向 API 服务器注册。--register-with-taints
- 使用给定的污点列表(逗号分隔的<key>=<value>:<effect>
)注册节点。如果
register-node
为 false,则该选项无效。--node-ip
- 可选的逗号分隔的节点 IP 地址列表。你只能为每个地址族指定一个地址。例如,在单栈 IPv4 集群中,你可以将此值设置为 kubelet 应为节点使用的 IPv4 地址。有关运行双栈集群的详细信息,请参见配置 IPv4/IPv6 双栈。如果你不提供此参数,kubelet 将使用节点的默认 IPv4 地址(如果存在);如果节点没有 IPv4 地址,则 kubelet 将使用节点的默认 IPv6 地址。
--node-labels
- 向集群注册节点时添加的标签(参见 NodeRestriction 准入插件强制执行的标签限制)。--node-status-update-frequency
- 指定 kubelet 向 API 服务器报告节点状态的频率。
当Node 授权模式和NodeRestriction 准入插件被启用时,kubelets 被授权仅创建/修改其自身的 Node 资源。
说明:
正如节点名称唯一性部分所述,当节点配置需要更新时,最佳实践是重新向 API 服务器注册节点。例如,如果 kubelet 使用一组新的 --node-labels
重新启动,但使用了相同的 Node 名称,则更改不会生效,因为标签只在向 API 服务器注册节点时设置(或修改)。
如果 kubelet 重启时 Node 配置发生变化,已调度到 Node 上的 Pod 可能会表现异常或导致问题。例如,已运行的 Pod 可能会因分配给 Node 的新标签而带有污点,而与该 Pod 不兼容的其他 Pod 将基于这个新标签进行调度。节点重新注册确保所有 Pod 都将被排空并正确地重新调度。
手动节点管理
你可以使用kubectl 创建和修改 Node 对象。
当你想手动创建 Node 对象时,将 kubelet 标志 --register-node
设置为 false。
无论 --register-node
设置如何,你都可以修改 Node 对象。例如,你可以在现有节点上设置标签或将其标记为不可调度。
你可以通过在节点上添加一个或多个 node-role.kubernetes.io/<role>: <role>
标签来为节点设置可选的节点角色。其中 <role>
中的字符受标签语法规则限制。
Kubernetes 忽略节点角色的标签值;按照惯例,你可以将其设置为与你在标签键中用于节点角的字符串相同。
你可以将节点上的标签与 Pod 上的节点选择器结合使用来控制调度。例如,你可以限制 Pod 只能在可用节点的子集上运行。
将节点标记为不可调度可阻止调度器将新的 Pod 放置在该节点上,但不会影响节点上已有的 Pod。这对于在节点重启或其他维护之前作为准备步骤很有用。
要将节点标记为不可调度,请运行
kubectl cordon $NODENAME
有关更多详细信息,请参阅安全排空节点。
说明:
作为DaemonSet一部分的 Pod 容忍在不可调度的节点上运行。DaemonSet 通常提供节点本地服务,即使节点正在被排空工作负载应用,这些服务也应该在节点上运行。节点状态
节点的 status 包含以下信息:
你可以使用 kubectl
来查看节点的状态及其他详细信息:
kubectl describe node <insert-node-name-here>
有关更多详细信息,请参见节点状态。
节点心跳
Kubernetes 节点发送的心跳帮助集群确定每个节点的可用性,并在检测到故障时采取措施。
对于节点,有两种形式的心跳:
节点控制器
节点控制器是 Kubernetes 控制平面组件,负责管理节点的各个方面。
节点控制器在节点的生命周期中扮演着多种角色。首先是在节点注册时为其分配一个 CIDR 块(如果启用了 CIDR 分配)。
其次是使节点控制器的内部节点列表与云提供商的可用机器列表保持同步。在云环境中运行且节点不健康时,节点控制器会询问云提供商该节点的 VM 是否仍然可用。如果不可用,节点控制器会从其节点列表中删除该节点。
第三是监视节点的健康状况。节点控制器负责:
- 如果节点变得不可达,则更新 Node 的
.status
字段中的Ready
条件。在这种情况下,节点控制器将Ready
条件设置为Unknown
。 - 如果节点仍然不可达:触发API 发起的驱逐,以驱逐不可达节点上的所有 Pod。默认情况下,节点控制器会等待 5 分钟,然后才将节点标记为
Unknown
并提交第一个驱逐请求。
默认情况下,节点控制器每 5 秒检查一次每个节点的状态。此周期可通过 kube-controller-manager
组件上的 --node-monitor-period
标志进行配置。
驱逐速率限制
在大多数情况下,节点控制器将驱逐速率限制在每秒 --node-eviction-rate
(默认为 0.1),这意味着它每 10 秒不会驱逐超过 1 个节点上的 Pod。
当给定可用区的节点变得不健康时,节点驱逐行为会发生变化。节点控制器会检查该可用区中同时不健康的节点百分比(Ready
条件为 Unknown
或 False
):
- 如果不健康节点的比例至少达到
--unhealthy-zone-threshold
(默认为 0.55),则会降低驱逐速率。 - 如果集群规模较小(即节点数小于或等于
--large-cluster-size-threshold
,默认为 50),则会停止驱逐。 - 否则,驱逐速率将降至每秒
--secondary-node-eviction-rate
(默认为 0.01)。
之所以按可用区实现这些策略,是因为一个可用区可能与控制平面断开连接,而其他可用区保持连接。如果你的集群不跨越多个云提供商可用区,则驱逐机制不会考虑按可用区的不可用性。
将节点分散到可用区的一个关键原因是为了在一个可用区完全宕机时,工作负载可以转移到健康的可用区。因此,如果一个可用区中的所有节点都不健康,则节点控制器将以正常的 --node-eviction-rate
速率进行驱逐。特殊情况是所有可用区都完全不健康(集群中没有健康的节点)。在这种情况下,节点控制器假定控制平面与节点之间的连接存在问题,并且不执行任何驱逐。(如果发生中断并且一些节点重新出现,节点控制器会驱逐剩余不健康或不可达节点上的 Pod)。
节点控制器还负责驱逐那些运行在带有 NoExecute
污点(taint)的节点上的 Pod,除非这些 Pod 容忍该污点。节点控制器还会添加与节点问题(如节点不可达或未就绪)对应的污点。这意味着调度器不会将 Pod 放置在不健康的节点上。
资源容量跟踪
Node 对象会跟踪节点资源容量的信息,例如可用内存量和 CPU 数量。那些自注册的节点会在注册期间报告它们的容量。如果你手动添加节点,则需要在添加时设置节点的容量信息。
Kubernetes 调度器确保节点上所有 Pod 都有足够的资源。调度器会检查节点上容器请求的总和不超过节点的容量。这个请求总和包括由 kubelet 管理的所有容器,但不包括容器运行时直接启动的任何容器,也不包括在 kubelet 控制之外运行的任何进程。
说明:
如果你想为非 Pod 进程显式预留资源,请参见为系统守护进程预留资源。节点拓扑
Kubernetes v1.27 [稳定]
(默认启用: true)如果你已经启用了 TopologyManager
特性门控,则 kubelet 在做资源分配决策时可以使用拓扑提示。有关更多信息,请参见控制节点上的拓扑管理策略。
Swap 内存管理
Kubernetes v1.30 [beta]
(默认启用: true)要在节点上启用 swap,必须在 kubelet 上启用 NodeSwap
特性门控(默认为 true),并且必须将 --fail-swap-on
命令行标志或 failSwapOn
配置设置设置为 false。为了让 Pod 能够利用 swap,kubelet 配置中的 swapBehavior
不应设置为 NoSwap
(这是默认行为)。
警告
当内存交换(Swap)功能开启时,诸如写入 tmpfs 的 Secret 对象内容等 Kubernetes 数据可能会被交换到磁盘。用户还可以选择在 memorySwap.swapBehavior
中配置节点如何使用 swap 内存。例如,
memorySwap:
swapBehavior: LimitedSwap
NoSwap
(默认):Kubernetes 工作负载将不使用 swap。LimitedSwap
:Kubernetes 工作负载对 swap 内存的使用受限。仅 Burstable QoS 的 Pod 允许使用 swap。
如果未指定 memorySwap
配置且特性门控已启用,则 kubelet 默认将应用与 NoSwap
设置相同的行为。
使用 LimitedSwap
时,不属于 Burstable QoS 类别的 Pod(即 BestEffort
/Guaranteed
Qos Pod)被禁止使用 swap 内存。为了保持前述的安全和节点健康保障,当 LimitedSwap
生效时,不允许这些 Pod 使用 swap 内存。
在详细介绍 swap 限制的计算之前,有必要定义以下术语:
nodeTotalMemory
: 节点上可用的物理内存总量。totalPodsSwapAvailable
: 节点上可供 Pod 使用的 swap 内存总量(部分 swap 内存可能被系统预留)。containerMemoryRequest
: 容器的内存请求。
Swap 限制配置为: (containerMemoryRequest / nodeTotalMemory) * totalPodsSwapAvailable
。
值得注意的是,对于 Burstable QoS Pod 中的容器,可以通过指定与内存限制相等的内存请求来选择不使用 swap。以这种方式配置的容器将无法访问 swap 内存。
Swap 仅支持 **cgroup v2**,cgroup v1 不支持。
欲了解更多信息,并协助测试和提供反馈,请参阅关于Kubernetes 1.28: NodeSwap 升级至 Beta1 的博文、KEP-2400 及其设计提案。
接下来
了解更多以下内容:
- 构成节点的组件。
- Node 的 API 定义.
- 架构设计文档的Node 部分。
- 优雅/非优雅节点关机.
- 节点自动扩缩容以管理集群中节点的数量和大小。
- 污点和容忍度.
- 节点资源管理器.
- Windows 节点的资源管理.