Kubernetes集群架构-节点
- [Kubernetes 集群架构 - 节点](#Kubernetes 集群架构 - 节点)
- 链接
Kubernetes 集群架构 - 节点
Kubernetes 通过将容器打包到 Pods 中以在节点上运行用户工作负载。根据不同的集群,节点可以是虚拟机或者物理机。所有节点都由控制平面进行管理,节点上包含运行 Pod 所必需的所有服务。
一般情况下,集群中包含多个节点,但在学习或者资源受限的情况下,一个集群可能只有一个节点。
节点上的组件包括 kubelete、容器运行时和 kube-proxy。
管理
有两种主要方法可以将节点添加到 API 服务中:
- 部署在节点上的 kubelet 组件自注册到控制平面
- 人工手动添加节点对象
在手动或者 kubelet 自动注册节点后,控制平面会检查新的节点对象是否有效。比如,考虑尝试使用如下的 JSON manifest 创建节点:
json
{
"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,否则,直到它变得正常之前,所有集群活动都会忽略该节点。
Kubernetes 会保留无效的 Node 对象,并持续检查它的健康状态。
只有通过手动或者控制器来明确删除 Node 对象,才能停止对该对象的健康检查。
Node 对象的名称必须是有效的 DNS 子域名。
节点名称唯一性
Kubernetes 使用名称来标识节点对象,两个节点不能同时拥有相同的名称,Kubernetes 假设具有相同名称的资源是相同的对象。对于节点来说,系统默认使用相同名称的实例拥有相同的状态(例如网络设置、根磁盘内容)和属性(如节点标签)。如果在没有更改节点名称的情况下修改了实例,可能会带来一致性问题。如果需要对节点进行大量替换或更新,则需要先将现有的节点对象从 API 服务中删除,更新后再重新添加。
节点自注册
当 kubelet 的标志符号 --register-node
为 true(默认值)时,kubelet 会尝试向 API 服务注册自身。这是大多数发行版使用的首选模式。
对于自注册,kubelet 可以使用以下的选项启动:
--kubeconfig
- 用于向 API 服务器验证自身身份的凭证路径。--cloud-provider
- 如何与云供应商交互以读取有关自身的元数据。--register-node
- 自动向 API 服务器注册。--register-with-taints
- 使用给定的污点列表(逗号分隔<key>=<value>:<effect>
注册节点。如果register-node
为 false,则为 No-op。--node-ip
- 节点的 IP 地址的可选列表,用逗号分隔。只能为每个地址簇指定一个地址。例如,在单栈 IPv4 集群中,将此值设置为 kubelet 应该用于节点的 IPv4 地址。有关运行双堆栈集群的详细信息,请参阅配置 IPv4/IPv6 双堆栈。如果不提供此参数,kubelet 将使用节点的默认 IPv4 地址(如果有);如果节点没有 IPv4 地址,则 kubelet 使用节点的默认 IPv6 地址。--node-labels
- 在集群中注册节点时要添加的标签(请参阅NodeRestriction 准入插件强制执行的标签限制)。--node-status-update-frequency
- 指定 kubelet 将其节点状态发布到 API 服务的频率。
当启用 Node 授权模式和NodeRestriction 准入插件时,kubelet 只能创建/修改自己的 Node 资源。
如 节点名称唯一性 部分所述,当需要更新 Node 配置时,最好向 API 服务重新注册节点。例如,如果使用一组新的
--node-labels
重新启动kubelet
,但使用相同的节点名称,则更改不会生效,因为标签仅在 API 服务器注册节点时设置(或修改)。
如果在 kubelet 重启时更改节点配置,则可能会导致已在节点上调度的 Pod 行为异常的问题。例如,已经运行 Pod 可能会受到分配给 Node 的新标签的污染,而与该 Pod 不兼容的其他 Pod 将基于这个新标签进行调度。节点重新注册可确保所有 Pod 都被清空并能正确地重新调度。
手动节点管理
可以使用 kubectl 创建和修改 Node 对象。当您想手动创建 Node 对象时,请设置 kubelet 标志 --register-node=false
。无论--register-node
设置为什么值,都可以修改Node对象。例如,可以在现有节点上设置标签或将其标记为不可调度。
可以将节点上的标签与 Pods上的节点选择器结合使用来控制调度。例如,可以将 Pod 限制运行在可用节点的符合要求的一个子集上。
将节点标记为 unschedulable 可以防止调度器将新的 Pod 放置到该节点上,但不会影响节点上的现有 Pod。这在节点重启或进行其他维护之前的准备步骤使用。
要将 Node 标记为 unschedulable,请运行:
bash
kubectl cordon $NODENAME
有关更多详细信息,请参阅 Safely Drain a Node^1^。
被 DaemonSet^2^ 控制器创建的 Pod 允许在不可调度的节点上运行。DaemonSet 通常提供节点本地服务,即使 Node 上的工作负载应用程序被排空,这些服务也必须在 Node 上运行。
节点状态
节点的状态包含以下信息:
可以使用 kubectl
查看节点的状态和其他详细信息:
bash
kubectl describe node <insert-node-name-here>
有关详细信息,请参阅节点状态。
节点心跳
由 Kubernetes 节点发送的心跳用于确定集群中每个节点的可用性,并在检测到故障时采取行动。
对于节点,有两种检测信号的形式:
- 对节点的 .status 进行更新。
-kube-node-lease
命名空间中的 Lease(租约)对象。每个 Node 都有一个关联的 Lease 对象。
节点控制器
节点控制器是Kubernetes 控制平面的一个组件,用于管理节点的方方面面。
节点控制器在节点的生命周期中扮演多个角色。第一个是在节点注册时(如果打开 CIDR 分配)为节点分配一个CIDR 块。
第二个是使节点控制器的内部节点列表与云供应商的可用机器列表保持最新。当在云环境中运行时,每当节点不健康时,节点控制器都会询问云供应商该节点的 VM 是否仍然可用。如果不是,节点控制器会从其节点列表中删除该节点。
三是监控节点的健康状况。节点控制器负责:
- 在节点变得不可达的情况下,更新节点的
.status
字段中的Ready
条件。在这种情况下,节点控制器将Ready
条件设置为Unknown
。 - 如果节点仍然不可达:为不可达节点上的所有 Pod 触发 API 发起的逐出。默认情况下,节点控制器在将节点标记为 Unknown 和提交第一个逐出请求之间等待5分钟。
默认情况下,节点控制器每 5 秒检查一次每个节点的状态。这个周期可以使用 kube-controller-manager
组件上的 --node-monitor-period
标志进行配置。
逐出速率限制
在大多数情况下,节点控制器将逐出率限制为每秒 --node-eviction-rate
个(默认为0.1),这意味着它每10秒至多从1个节点上驱逐pods。
当给定可用性区域(availability zone)中的节点变得不健康时,节点驱逐行为会发生变化。节点控制器同时检查区域中有多少百分比的节点不健康(Ready
条件为 Unknown
或 False
):
- 如果不健康节点的比例大于等于
--unhealthy-zone-threshold
(默认为0.55),则降低逐出率。 - 如果集群很小(即具有小于或等于
--large-cluster-size-threshold
节点-默认值50),则停止驱逐。 - 其他情况下,逐出率将降低到每秒
--secondary-node-eviction-rate
个(默认为0.01)。
按可用区实施这些策略的原因是,一个可用区可能会从控制平面脱离,而其他可用区可能仍然保持连接。如果集群没有跨云供应商的多个可用区,则被视为只有一个可用区。
将节点跨多个可用区域部署的一个关键原因是,当一个区域出现故障时,工作负载可以转移到运行状况良好的区域。因此,如果区域中的所有节点都运行状况不佳,则节点控制器会以 --node-eviction-rate
的正常速率驱逐。极端情况是所有区域都完全不正常(集群中的所有节点都不正常)。在这种情况下,节点控制器会假设控制平面和节点之间的连接存在一些问题,并且不会执行任何驱逐。(如果发生中断并且某些节点再次出现,则节点控制器会从运行状况不佳或无法访问的剩余节点中驱逐 Pod)。
节点控制器还负责驱逐在具有 NoExecute
污点的节点上运行的 Pod,除非这些 Pod 容忍该污点。节点控制器还会添加与节点问题相对应的污点,例如节点无法访问或未就绪。这意味着调度器不会将 Pod 放置在运行状况不佳的节点上。
资源容量跟踪
Node 对象跟踪与 Node 有关的资源容量的信息:例如,可用内存量和 CPU 数量。自注册的节点会在自注册时报告其容量。如果是手动添加的节点,则需要在添加节点时设置节点的容量信息。
Kubernetes 调度器确保节点上的所有 Pod 都有足够的资源。调度器检查节点上容器的请求之和是否不大于节点的容量。该请求总和包括 kubelet 管理的所有容器,但不包括由容器运行时直接启动的任何容器,也不排除在 kubelet 控制之外运行的任何进程。
如果想显式地为非 Pod 进程预留资源,请参阅为系统守护进程预留资源。
节点拓扑
特性状态: Kubernetes v1.27 [stable](默认启用:true)
如果启用了 TopologyManager
特性门控^3^,那么 kubelet 可以在做出资源分配决策时使用拓扑提示。有关更多信息,请参阅控制节点上的拓扑管理策略。
交换内存管理
特性状态: Kubernetes v1.30 [stable](默认启用:true)
要在节点上启用交换内存,必须在 kubelet 中启用 NodeSwap
特性门控(默认为 true),并且 --fail-swap-on
命令行标志或 failSwapOn
配置设置必须为 false。如果要允许 Pod 使用交换内存,则不应在 kubelet 配置中将 swapBehavior
设置为 NoSwap
(这是默认行为)。
开启交换内存功能后,写入 tmpfs 的 Kubernetes 数据(例如 Secret 对象的内容)现在可以被交换到磁盘。
用户还可以选择配置 memorySwap.swapBehavior
以指定节点将如何使用交换内存。例如
yaml
memorySwap:
swapBehavior: LimitedSwap
- NoSwap(默认):Kubernetes 工作负载不会使用交换。
LimitedSwap
:Kubernetes 工作负载对交换内存的利用受到限制。只允许 Burstable QoS 的 Pod 使用交换内存。
如果未指定 memorySwap
的配置并且启用了功能门控,则默认情况下,kubelet 将应用与 NoSwap
设置相同的行为。
使用 LimitedSwap
时,不属于 Burstable QoS 分类的 Pod(即 BestEffort/Guaranteed
Qos Pods)将被禁止使用交换内存。为了维护上述安全和节点健康保证,当 LimitedSwap
生效时,不允许这些 Pod 使用交换内存。
在详细说明交换限制的计算之前,有必要定义以下术语:
nodeTotalMemory
:节点上可用的物理内存总量。totalPodsSwapAvailable
:节点上可供 Pod 使用的交换内存总量(一些交换内存可能会保留供系统使用)。containerMemoryRequest
:容器的内存请求。
交换限制配置为:(containerMemoryRequest / nodeTotalMemory) * totalPodsSwapAvailable
。
需要注意的是,对于突发 QoS Pod 中的容器,可以通过指定等于内存限制的内存请求来选择退出交换使用。以这种方式配置的容器将无权访问交换内存。
只有 cgroup v2 支持交换,cgroup v1 不受支持。
链接
- Kubernetes Cluster Architecture Node
- Kubernetes 对象名称和 ID
- 污点列表
- IPv4/IPv6 双堆栈
- Kubernetes 准入控制
- Node 授权模式
- Safely Drain a Node
- 节点状态
- API 发起的逐出
- 为系统守护进程预留资源
- 特性门控
- 控制节点上的拓扑管理策略
-
在Kubernetes(k8s)中,"safely drain a node"是指一种操作,通过优雅地逐出(eviction)该节点上的Pod,将工作负载从指定节点安全地转移到其他节点,同时尽量避免中断服务,通常用于节点维护、升级或退役场景。 ↩︎
-
Kubernetes 中的 DaemonSet 是一种确保集群中的每个节点(包括新加入的节点)都运行一个特定Pod 副本的资源对象,主要用于在每个节点上运行如日志收集、监控代理这样的系统级守护进程。 ↩︎
-
在 Kubernetes(k8s)中,TopologyManager 是一个用于管理和优化容器资源分配的组件。它主要关注的是 NUMA(Non - Uniform Memory Access,非统一内存访问)架构下的资源分配,目的是提高资源的使用效率和应用程序的性能。在 NUMA 架构的系统中,处理器和内存被划分成多个节点(NUMA 节点),每个节点有自己的本地内存,访问本地内存比访问其他节点的内存速度更快。TopologyManager 通过协调 CPU 和内存分配,使得容器能够更好地利用这种架构优势。 ↩︎