节点
Kubernetes 中添加节点到 API 服务器的两种主要方式:
- 节点上的 kubelet 自动向控制平面注册。
- 您(或其他人)手动添加一个 Node 对象。
当您创建一个 Node 对象,或者节点上的 kubelet 自动注册时,控制平面会检查新的 Node 对象是否有效。例如,当您尝试使用以下 JSON 配置文件创建一个 Node:
json
{
"kind": "Node",
"apiVersion": "v1",
"metadata": {
"name": "10.240.79.157",
"labels": {
"name": "my-first-k8s-node"
}
}
}
Kubernetes 会在内部创建一个 Node 对象(表示形式)。Kubernetes 检查是否有与 Node 的 metadata.name 字段匹配的 kubelet 已向 API 服务器注册。如果节点是健康的(即所有必要的服务都在运行),则该节点有资格运行 Pod。否则,该节点将被忽略,直到它变为健康状态,不参与任何集群活动。
节点名称的唯一性
在同一时间内,两个节点不能具有相同的名称。Kubernetes 还假设具有相同名称的资源是同一个对象。对于节点而言,假定使用相同名称的实例将具有相同的状态(例如,网络设置、根磁盘内容)和节点标签等属性。如果在不更改名称的情况下修改了实例,可能会导致不一致性。如果节点需要被替换或进行重大更新,首先需要从 API 服务器中删除现有的 Node 对象,然后在更新后重新添加。
例如,如果使用相同的节点名称重新启动具有新的 --node-labels 设置的 kubelet,更改将不会生效,因为标签是在节点注册时设置的。
如果在 kubelet 重新启动时更改节点配置,已经在节点上调度的 Pod 可能会出现问题或引发其他问题。例如,已经在运行的 Pod 可能会对分配给节点的新标签进行标记,而与该 Pod 不兼容的其他 Pod 将基于这个新标签进行调度。重新注册节点可以确保所有的 Pod 都会被驱逐并正确重新调度。
节点的自注册
当 kubelet 的标志 "--register-node" 为 true(默认情况下),kubelet 将尝试与 API 服务器自行注册。这是首选的模式,大多数发行版都使用这种模式。
对于自注册,kubelet 在启动时带有以下选项:
-
--kubeconfig:用于对自身进行身份验证以连接 API 服务器的凭据的路径。
-
--cloud-provider:如何与云提供商交互以读取有关自身的元数据。
-
--register-node:自动向 API 服务器注册。
-
--register-with-taints:使用给定的污点列表(逗号分隔的 =:)注册节点。
如果 "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 服务器发送节点状态的频率。
当启用节点授权模式和 NodeRestriction 准入插件时,kubelet 只有在自己的 Node 资源上有创建/修改的权限。
创建和修改 Node 对象
当您希望手动创建 Node 对象时,请将 kubelet 标志 "--register-node" 设置为 false。
无论 --register-node 的设置如何,您都可以修改 Node 对象。
例如,您可以在现有的 Node 上设置标签或将其标记为不可调度。
您可以在 Pod 上使用节点标签和节点选择器结合来控制调度。例如,您可以将 Pod 限制为仅在可用节点的子集上运行。
将节点标记为不可调度会阻止调度程序将新的 Pod 放入该节点,但不会影响节点上的现有 Pod。这在节点重新启动或进行其他维护之前作为准备步骤非常有用。
要将节点标记为不可调度,请运行以下命令:
bash
kubectl cordon $NODENAME
有关更多详细信息,请参阅 Safely Drain a Node。
注:
作为 DaemonSet 的一部分的 Pod 可以容忍在不可调度的节点上运行。DaemonSet 通常用于在节点上提供本地服务。本地服务是指以节点为单位,在每个节点上独立运行的服务。与负载应用程序不同,本地服务通常独立于负载,并且即使节点上有负载被下线,这些服务也应该持续在节点上提供。
即使节点被标记为不可调度(unschedulable),DaemonSet 中的 Pod 仍然会在该节点上运行,以提供节点级别的服务。这允许在执行节点维护或排空节点的同时,仍然保持 DaemonSet Pod 的功能和可用性。
查看节点信息
bash
kubectl describe node <insert-node-name-here>
该命令是使用kubectl工具,用于查看指定节点(Node)的状态和其他相关信息。
-
"Addresses"字段显示该节点的网络地址信息,例如IP地址等。
-
"Conditions"字段显示节点的健康状态,包括各种条件,如Ready、DiskPressure、OutOfDisk等。
-
"Capacity and Allocatable"字段显示节点的资源容量和可分配的资源情况,例如CPU、内存、磁盘等。
-
"Info"字段提供了一些关于节点的其他信息,如操作系统、内核版本、Docker版本等。
节点相关信息
节点控制器
是Kubernetes控制平面组件之一,负责管理节点的各个方面。
节点控制器在节点生命周期中扮演多个角色。首先,在节点注册时为其分配一个CIDR块(如果已启用CIDR分配)。
其次,节点控制器会根据云提供商的可用机器列表,随时更新其内部节点列表。当在云环境中运行并且节点出现故障时,节点控制器会询问云提供商该节点的VM是否仍然可用。如果不可用,节点控制器将从其节点列表中删除该节点。
第三,节点控制器会监测节点的健康状况。节点控制器负责以下任务:
- 如果节点无法访问,更新Node的.status字段中的Ready条件。在这种情况下,节点控制器将Ready条件设置为Unknown。
- 如果节点仍然无法访问:触发API发起的针对无法访问的节点上的所有Pod的驱逐操作。默认情况下,节点控制器在将节点标记为Unknown和提交第一个驱逐请求之间等待5分钟。
- 默认情况下,节点控制器每5秒检查每个节点的状态一次。这个时间间隔可以通过使用kube-controller-manager组件的--node-monitor-period标志进行配置。
节点的VM
在Kubernetes中,节点的VM(Virtual Machine)是指运行节点代理的计算节点 。每个节点都需要运行一个计算节点代理(Compute Node Agent),该代理负责将该节点抽象为VM,使得Kubernetes的控制器可以管理这些节点上的资源。
节点控制器对节点驱逐的限制和行为。
在大多数情况下,节点控制器将驱逐速率限制为每秒--node-eviction-rate(默认为0.1),这意味着它每10秒不会从多个节点驱逐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标记的节点上运行的Pod,除非这些Pod会容忍该标记。节点控制器还会添加与节点问题相对应的标记,如节点不可访问或未就绪。这意味着调度器不会将Pod放置在不健康的节点上。
可用区(Availability Zone)
可用区(Availability Zone)是云计算服务提供商(如亚马逊AWS、微软Azure、谷歌云等)在一个地域(Region)内划分的物理隔离区域 。每个可用区都是独立的数据中心,具备独立的电源、网络和故障隔离能力。
在一个地域内,可用区通常位于不同的地理位置,以提供更高的容灾能力和可用性。通过将应用程序或数据分布在不同的可用区,可以避免单一点故障导致的服务中断,并提供更好的容错和可恢复性。
可用区之间有低延迟和高带宽的互联网络连接,同时它们也受到相对独立的风险影响。因此,通过将负载均衡、存储副本、数据库备份等部署到不同的可用区,可以提供更高级别的冗余和容错能力,确保服务的持续可用性和数据的安全性。
注意,可用区的概念是特定于云服务提供商的。不同的云提供商可能有不同的术语来描述类似的概念,比如微软Azure中的区域(Region)和区域对等性(Regional Pairing)等。
资源容量跟踪
节点对象会跟踪关于节点资源容量的信息,例如可用内存量和CPU数量。节点会在注册过程中报告其容量,这适用于自注册的节点 。如果您手动添加节点,则需要在添加节点时设置节点的容量信息。
Kubernetes调度器会确保节点上有足够的资源来满足所有Pod的需求。调度器会检查节点上容器的资源请求总和是否不大于节点的容量。这个请求总和包括kubelet管理的所有容器,但不包括由容器运行时直接启动的容器,也不包括在kubelet控制之外运行的任何进程。
请注意:如果您希望明确保留资源给非Pod进程,请参阅"reserve resources for system daemons"(为系统守护进程预留资源)。
节点正常关闭
Kubelet会尝试检测节点系统关机,并终止在该节点上运行的Pod。
在节点关机过程中,Kubelet会确保Pod按照正常的终止流程进行 。
在节点关机期间 ,即使某些Pod已经绑定到节点上,Kubelet也不会接受新的Pod。
优雅节点关机功能依赖于systemd ,因为它利用systemd的inhibitor锁来延迟节点关机的指定持续时间。
优雅节点关机是通过GracefulNodeShutdown功能门控制的,在1.21版本中默认启用。
请注意,默认情况下
,下面描述的两个配置选项shutdownGracePeriod和shutdownGracePeriodCriticalPods都设置为零,因此不激活优雅节点关机功能。要激活此功能,应适当配置并设置两个kubelet配置设置为非零值。
一旦systemd检测到或通知节点关机,Kubelet会在节点上设置一个NotReady状态,原因为"node is shutting down"。kube-scheduler会尊重此状态,并且不会将任何Pod调度到受影响的节点上;其他第三方调度器应遵循相同的逻辑。这意味着新的Pod不会被调度到该节点上,因此不会有新的Pod启动。
如果已检测到正在进行的节点关机,Kubelet在PodAdmission阶段也会拒绝Pod的请求,因此即使Pod对于node.kubernetes.io/not-ready:NoSchedule具有容忍标记,也不会在该节点上启动。
与此同时,当Kubelet通过API在其节点上设置该条件时,它也会开始终止任何正在本地运行的Pod。
- 在优雅关机期间,Kubelet分两个阶段终止Pod:
- 终止在节点上运行的常规Pod。
- 终止在节点上运行的关键Pod。
- 优雅节点关机功能通过两个KubeletConfiguration选项进行配置:
- shutdownGracePeriod :
指定节点应延迟关机的总持续时间。这是常规Pod和关键Pod的总优雅终止期限。 - shutdownGracePeriodCriticalPods :
指定在节点关机期间终止关键Pod所使用的持续时间。该值应小于shutdownGracePeriod
。
注意
在某些情况下,节点终止会被系统取消(或者可能由管理员手动取消)。在任何一种情况下,节点都会返回到Ready状态。但是,已经启动终止过程的 Pod 不会被 kubelet 恢复,需要重新调度。
关于Kubernetes中的节点(Node)关机优雅处理的配置参数。当设置了shutdownGracePeriod
为30秒,shutdownGracePeriodCriticalPods
为10秒时,Kubelet(节点上的一个组件)会延迟节点关机30秒。在关机期间,前20秒(30-10)将用于优雅地终止普通的Pod,而最后的10秒将用于终止关键的Pod。
简单来说,Kubelet在节点关机前会预留一定时间用于优雅地终止Pod。在优雅终止期间,Kubelet会依次发送Termination信号给每个Pod,以便它们能够完成正在运行的任务、保存状态并正常退出。这有助于避免数据丢失或服务中断。不过,有些Pod可能被标记为关键(Critical),它们可能需要更短的终止时间以确保系统的稳定性。因此,Kubelet会在最后的几秒中专门处理这些关键Pod的终止。
总结来说,给定的配置参数指定了Kubelet在节点关机时的优雅终止处理策略,确保Pod能够安全地完成任务并退出。
笔记:
当 pod 在节点正常关闭期间被逐出时,它们会被标记为关闭。运行时kubectl get pods显示被驱逐 Pod 的状态为Terminated。并kubectl describe pod表示 pod 由于节点关闭而被驱逐:
Reason: Terminated
Message: Pod was terminated in response to imminent node shutdown.
Kubernetes中的Pod优先级(Priority)与节点(Node)优雅关机的功能
这个功能从Kubernetes v1.24版本开始,处于beta状态。通过启用该功能,管理员可以根据Pod的优先级类别明确定义节点优雅关机期间Pod的顺序。
在节点优雅关机期间,根据描述的Graceful Node Shutdown功能,会分两个阶段关闭Pod:
首先 是非关键(non-critical)Pod
然后 是关键(critical)Pod。
但是,如果需要更精细地定义关机期间Pod的顺序,可以使用基于Pod优先级的优雅关机功能。
当节点优雅关机考虑到Pod的优先级时,可以实现多个阶段的优雅关机,其中每个阶段关闭特定优先级类别的Pod。管理员可以为每个阶段配置kubelet,确定准确的阶段和每个阶段的关机时间。
总结
来说,这个功能允许管理员根据Pod的优先级类别来控制节点优雅关机期间Pod的顺序。这样可以更加灵活地配置节点关机的阶段与时间,以满足不同的需求。
关于在Kubernetes集群中的自定义Pod优先级类别和关机期间的配置示例。
假设在集群中有以下自定义的Pod优先级类别和对应的优先级值:
- custom-class-a: 100,000
- custom-class-b: 10,000
- custom-class-c: 1,000
- regular/unset: 0
那么在kubelet的配置中,可以通过设置shutdownGracePeriodByPodPriority
来指定每个优先级类别的关机期间。
根据上述配置,不同优先级类别的关机期间如下所示:
- 优先级值为100,000的Pod:将有10秒的时间来停止
- 优先级值为10,000的Pod:将有180秒的时间来停止
- 优先级值为1,000的Pod:将有120秒的时间来停止
- 优先级值为0的Pod(regular/unset):将有60秒的时间来停止
对应的kubelet配置YAML如下所示:
shutdownGracePeriodByPodPriority:
- priority: 100000
shutdownGracePeriodSeconds: 10
- priority: 10000
shutdownGracePeriodSeconds: 180
- priority: 1000
shutdownGracePeriodSeconds: 120
- priority: 0
shutdownGracePeriodSeconds: 60
这个配置表示具有不同优先级值的Pod将有不同的关机期间。Kubelet将根据Pod的优先级值来确定每个优先级类别的关机时间,以确保在节点关机期间能够有序地终止Pod的运行。
您可以根据需要指定不同优先级类别的值,不一定要为所有类别都指定值。例如,您可以使用以下设置:
- 优先级值为100,000的Pod:关机期间为300秒
- 优先级值为1,000的Pod:关机期间为120秒
- 优先级值为0的Pod:关机期间为60秒
在上述情况下,具有custom-class-b
的Pod将与custom-class-c
的Pod一起被划分到相同的关机时间段内。
如果在特定的范围内没有Pod,则kubelet不会等待该优先级范围内的Pod。相反,kubelet会立即转到下一个优先级类别的范围。
如果启用了该功能但没有提供配置,则不会采取任何排序操作。
使用此功能需要启用GracefulNodeShutdownBasedOnPodPriority功能开关,并在kubelet配置中设置ShutdownGracePeriodByPodPriority为所需的配置,其中包含Pod优先级类别的值及其各自的关机时间。
注意:在Kubernetes v1.23中,考虑Pod优先级的节点优雅关机功能作为Alpha功能引入。在Kubernetes 1.28中,该功能已升级为Beta,并默认启用。
度量指标graceful_shutdown_start_time_seconds
和graceful_shutdown_end_time_seconds
会在kubelet子系统下发出,用于监控节点的关机过程。
非优雅的节点关机处理
功能状态:Kubernetes v1.28 [稳定版]
kubelet的节点关机管理器 可能无法检测到节点的关机操作,可能是因为:
- 执行的命令不会触发kubelet使用的抑制锁机制
- 由于用户错误,即ShutdownGracePeriod和ShutdownGracePeriodCriticalPods没有正确配置。
当节点关机但kubelet的节点关机管理器无法检测到时 ,属于StatefulSet的Pod 将被卡在关机节点上的终止状态,无法转移到新的运行节点。这是因为关机节点上的kubelet不可用以删除这些Pod,因此StatefulSet无法创建具有相同名称的新Pod。如果Pod使用了卷 ,那么卷附件将不会从原始关机节点上删除,因此这些Pod使用的卷无法附加到新的运行节点上。结果,运行在StatefulSet上的应用程序无法正常运行。
如果原始关机节点重新启动,kubelet将删除这些Pod,并在不同的运行节点上创建新的Pod。
如果原始关机节点没有重新启动,这些Pod将永远卡在终止状态的关机节点上。
为了缓解上述情况,用户可以手动向节点添加taint node.kubernetes.io/out-of-service
,并使用NoExecute或NoSchedule效果将其标记为停机状态。如果在kube-controller-manager上启用了NodeOutOfServiceVolumeDetach
功能开关,并且一个节点被标记为停机状态并且带有此污点,那么如果节点上没有匹配的容忍度,节点上的Pod将被强制删除,并且正在终止节点上的Pod的卷分离操作将立即进行。这使得停机状态的节点上的Pod能够快速地恢复到不同的节点上。
在非优雅关机期间,Pod将分为两个阶段终止:
- 强制删除没有匹配的停机状态容忍度的Pod。
- 立即执行这些Pod的卷分离操作。
注意:
在添加taint node.kubernetes.io/out-of-service
之前,应验证节点已经处于关机或关闭状态(不处于重新启动中)。
用户需要在Pod移动到新节点并检查关机节点已恢复正常后,手动删除停机状态的taint,因为用户最初是添加这个污点的人。
这种做法有助于处理非优雅的节点关机情况,以确保Pod能够在其他节点上快速恢复。
交换内存管理(Swap 内存管理)
要在节点上启用交换空间 ,kubelet上必须启用NodeSwap功能开关 ,并且--fail-swap-on命令行标志或failSwapOn配置设置必须设置为false
。
警告
:当打开内存交换功能时,Kubernetes数据(例如写入tmpfs的Secret对象的内容)现在可能会被交换到磁盘上
。
指定节点如何使用交换内存:可以选择配置memorySwap.swapBehavior。例如,
yaml
memorySwap:
swapBehavior: UnlimitedSwap
- UnlimitedSwap(默认):Kubernetes工作负载可以使用其请求的交换内存,最多达到系统限制。
- LimitedSwap:Kubernetes工作负载使用交换内存受到限制。只有突发性QoS的Pod才能使用交换空间。
如果未指定memorySwap的配置并且启用了功能开关,默认情况下kubelet将应用与UnlimitedSwap设置相同的行为。
使用LimitedSwap时,不属于突发性QoS分类(即BestEffort/Guaranteed QoS的Pod)的Pod被禁止使用交换内存。为了保持上述安全性和节点健康性的保证,当LimitedSwap生效时,这些Pod不被允许使用交换内存。
在详细说明交换限制的计算之前,有必要定义以下术语:
- nodeTotalMemory:节点上可用的物理内存总量。
- totalPodsSwapAvailable:可供Pod使用的节点上的交换内存总量(某些交换内存可能被保留供系统使用)。
- containerMemoryRequest:容器的内存请求。
交换限制配置为:(containerMemoryRequest / nodeTotalMemory) * totalPodsSwapAvailable。
需要注意的是,对于突发性QoS的Pod中的容器,可以通过将内存请求设置为与内存限制相等来选择不使用交换空间。以这种方式配置的容器将无法访问交换内存。
Swap仅支持cgroup v2,不支持cgroup v1。
官方文档