From 09f1e5021e4b008abd3f81c5a47bca7cdc645b93 Mon Sep 17 00:00:00 2001 From: isno Date: Sat, 1 Feb 2025 17:01:37 +0800 Subject: [PATCH] fix typo --- Observability/logging.md | 2 +- consensus/Basic-Paxos.md | 7 ++-- container/Extended-Resource.md | 4 +-- container/auto-scaling.md | 18 +++++----- container/kube-scheduler.md | 65 +++++++++++++++------------------- container/storage.md | 6 ++-- 6 files changed, 45 insertions(+), 57 deletions(-) diff --git a/Observability/logging.md b/Observability/logging.md index 9856e446..155b6629 100644 --- a/Observability/logging.md +++ b/Observability/logging.md @@ -135,7 +135,7 @@ ORDER BY id; 作为一款分布式数据系统,ClickHouse 自然支持“分片”(Sharding)技术。 -ClickHouse 将海量数据切分成多个较小的部分,并分布到多个物理节点。也就是说,只要有足够多的硬件资源,ClickHouse 就能处理数万亿条记录、PB 级别规模的数据量。根据 Yandex 内部的基准测试结果来看(图 9-9),ClickHouse 性能表现遥遥领先对手,比 Vertica(一款商业 OLAP 软件)快约 5 倍、比 Hive 快 279 倍、比 InfiniDB 快 31 倍。 +ClickHouse 将数据切分成多个部分,并分布到不同的物理节点。也就是说,只要有足够多的硬件资源,ClickHouse 就能处理数万亿条记录、PB 级别规模的数据量。根据 Yandex 公布的测试结果来看(图 9-9),ClickHouse 性能表现遥遥领先对手,比 Vertica(一款商业 OLAP 软件)快约 5 倍、比 Hive 快 279 倍、比 InfiniDB 快 31 倍。 :::center ![](../assets/ClickHouse-benchmark.jpeg)
diff --git a/consensus/Basic-Paxos.md b/consensus/Basic-Paxos.md index 8801fd5f..7e1603bf 100644 --- a/consensus/Basic-Paxos.md +++ b/consensus/Basic-Paxos.md @@ -40,10 +40,9 @@ WHERE id = ? AND version = ?; 我们可以借鉴“乐观锁”的思路,尝试解决图 6-5 中的并发冲突问题。请看下面的操作: -首先,S~1~ 发起提案,S~3~ 收到 S~1~ 提案时,应该意识到 S~5~ 发起的提案(blue)的 Quorum 已经达成,S~1~ 提案(red)已经失效。根据先到先得原则,S~1~ 应该更新自己的提案值(red 替换为 blue),这个操作相当于对提案编号(乐观锁中的 version)“锁定”,防止在之后出现多个冲突的提案编号。 - -了解哪些编号被接受后,接下来的处理就变得简单了。现在,我们可以直面 Paxos 算法的细节了。 +首先,S~1~ 发起提案,S~3~ 收到 S~1~ 提案时,应该意识到 S~5~ 发起的提案(blue)的 Quorum 已经达成,S~1~ 提案(red)已经失效。根据先到先得原则,S~1~ 应该更新自己的提案值(red 替换为 blue),这个操作相当于对提案编号(乐观锁中的 version)“锁定”,防止之后出现多个冲突的提案编号。 +了解接受了哪些编,接下来的处理就简单了。现在,我们可以直面 Paxos 算法的细节了。 ## 2. Paxos 算法描述 @@ -98,7 +97,7 @@ Paxos 算法的第二个阶段称“批准阶段”(Accept)。提议者向 **情况四:从情况三可以推导出另一种极端的情况**,多个提议者同时发起提案,在准备阶段互相抢占,反复刷新决策者上的提案编号,导致任何一方都无法达到多数派决议,这个过程理论上可以无限持续下去,形成“活锁”(livelock)。 -解决这个问题并复杂,把重试时间随机化,就能减少这种巧合发生。 +解决这个问题并复杂,随机重试时间,就能减少这种巧合发生。 :::center ![](../assets/paxos-p4.png)
图 6-10 出现活锁问题 diff --git a/container/Extended-Resource.md b/container/Extended-Resource.md index 64a28258..e82695f8 100644 --- a/container/Extended-Resource.md +++ b/container/Extended-Resource.md @@ -1,4 +1,4 @@ -# 7.7.2 扩展资源与 Device Plugin +# 7.7.2 扩展资源与设备插件 在 Kubernetes 中,节点的标准资源(如 CPU、内存和存储)由 Kubelet 自动报告,但节点内的异构硬件资源(如 GPU、FPGA、RDMA 或硬件加速器),Kubernetes 并未识别和管理。 @@ -88,4 +88,4 @@ service DevicePlugin { 最后,再来看扩展资源和设备插件的问题。Pod 只能通过类似“nvidia.com/gpu:2”的计数方式申请 2 个 GPU,但这些 GPU 的具体型号、拓扑结构、是否共享等属性并不明确。也就是说,设备插件仅实现了基本的入门级功能,能用,但不好用。 -在“成本要省”、“资源利用率要高”背景推动下,Nvidia、Intel 等头部厂商联合推出了“动态资源分配”(Dynamic Resource Allocation,DRA)机制,允许用户以更复杂的方式描述异构资源,而不仅仅是简单的计数形式。DRA 属于较新的机制,具体的接口规范可能因硬件供应商和 Kubernetes 版本不同而有所变化。限于篇幅,笔者就不再扩展讨论了,有兴趣的读者可以查阅其他资料。 \ No newline at end of file +在“成本要省”、“资源利用率要高”背景推动下,Nvidia、Intel 等头部厂商联合推出了“动态资源分配”(Dynamic Resource Allocation,DRA)机制,允许用户以更复杂的方式描述异构资源,而不仅仅是简单的计数形式。DRA 属于较新的机制,具体的接口规范因硬件供应商和 Kubernetes 版本不同而有所变化。限于篇幅,笔者就不再扩展讨论了,有兴趣的读者请查阅其他资料。 \ No newline at end of file diff --git a/container/auto-scaling.md b/container/auto-scaling.md index 1be64095..71434a77 100644 --- a/container/auto-scaling.md +++ b/container/auto-scaling.md @@ -1,6 +1,6 @@ # 7.8 资源弹性伸缩 -为了平衡服务负载的巨大波动及资源预估与实际使用之间的差距,Kubernetes 提供了三种资源自动扩缩(autoscaling)解决方案:HPA、VPA 以及节点自动伸缩(Cluster Autoscaler)组件。 +为了平衡服务负载的巨大波动及资源预估与实际使用之间的差距,Kubernetes 提供了 HPA、VPA 以及 CA 三种资源自动扩缩(autoscaling)机制。 ## 7.8.1 Pod 水平自动伸缩 @@ -10,7 +10,7 @@ HPA 的原理很简单,即监控资源使用程度做出相应的调整: - 当负载较高时,增加工作负载的 Pod 副本数量; - 当负载减少时,缩减工作负载的 Pod 副本数量。 -实现自动扩缩关键是,如何准确识别资源耗用程度?为此,Kubernetes 提供了一种称为 Metrics API 的指标接口,用于获取节点、Pod 资源情况。来看一个 Metrics API 的响应示例,输出了 cpu、memory 资源耗用情况。 +实现自动扩缩的关键在于如何准确识别资源的使用情况。为此,Kubernetes 提供了 Metrics API,用于获取节点和 Pod 的资源信息。以下是 Metrics API 的响应示例,展示了 CPU 和内存的资源使用情况。 ```bash $ kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes/minikube" | jq '.' @@ -48,9 +48,7 @@ $ kubectl autoscale deployment foo --cpu-percent=70 --min=1 --max=10 VPA 全称是 Vertical Pod Autoscaler(Pod 垂直自动伸缩)。它的实现思路与 HPA 基本一致,两者都是通过 Metrics 接口获取指标,评估后做出相应的调整。不同的是,VPA 调整的是工作负载的资源配额(如 Pod 的 CPU 和内存的 request 和 limit)。 -值得注意的是,VPA 是 Kubernetes 的附加组件,需要安装和配置后才能为工作负载(如 Deployment)定义资源调整策略。 - -以下是一个 VPA 配置示例,供读者参考: +值得注意的是,VPA 是 Kubernetes 的附加组件,需要安装和配置后才能为工作负载(如 Deployment)定义资源调整策略。以下是一个 VPA 配置示例,供读者参考: ```yaml apiVersion: autoscaling.k8s.io/v1 @@ -66,7 +64,7 @@ spec: updatePolicy: updateMode: Auto # 决定 VPA 如何应用推荐的资源调整,也可以设置为 "Off" 或 "Initial" 来控制更新策略 ``` -将上述 YAML 文件提交到 Kubernetes 集群后,通过 kubectl describe vpa 命令查看 VPA 推荐的资源策略。 +将上述 YAML 文件提交到 Kubernetes 集群后,通过 kubectl describe vpa 命令查看 VPA 推荐的资源策略: ```bash $ kubectl describe vpa example-app-vpa @@ -89,7 +87,7 @@ Recommendation: ... ``` -相比于 HPA,VPA 适用于负载动态变化较大、资源需求不确定的场景,尤其是在无法精确预估应用资源需求的情况下。 +可以看出,VPA 更适用于负载变化较大、资源需求不确定的场景,尤其在无法精确预估应用资源需求时。 ## 7.8.3 基于事件驱动的伸缩 @@ -140,9 +138,9 @@ spec: 业务的增长(也有可能萎缩),会导致集群资源不足或者过度冗余。如果能根据集群资情况自动增/减节点,保证集群可用性的同时,还能最大程度降低资源成本! -Cluster AutoScaler 是专门用于调整节点的组件,它的功能如下: -- **自动扩展(Scale Up)**:当节点资源不能满足 Pod 需求时,Cluster AutoScaler 向云服务提供商(如 GCE、GKE、Azure、AKS、AWS 等)请求创建新的节点,扩展集群容量,确保业务能够获得所需的资源。 -- **自动缩减(Scale Down)**:当节点资源利用率长期处于低水平(如低于 50%),Cluster AutoScaler 将该节点上的 Pod 调度到其他节点,然后将节点从集群中移除,避免资源浪费。 +CA(Cluster AutoScaler)就是专门用于调整节点的组件,它的功能如下: +- **自动扩展**(Scale Up):当节点资源不能满足 Pod 需求时,Cluster AutoScaler 向云服务提供商(如 GCE、GKE、Azure、AKS、AWS 等)请求创建新的节点,扩展集群容量,确保业务能够获得所需的资源。 +- **自动缩减**(Scale Down):当节点资源利用率长期处于低水平(如低于 50%),Cluster AutoScaler 将该节点上的 Pod 调度到其他节点,然后将节点从集群中移除,避免资源浪费。 :::center ![](../assets/Cluster-AutoScaler.png)
diff --git a/container/kube-scheduler.md b/container/kube-scheduler.md index 9b90b2a7..dc3228a8 100644 --- a/container/kube-scheduler.md +++ b/container/kube-scheduler.md @@ -1,6 +1,6 @@ # 7.7.3 默认调度器及扩展设计 -如果集群中节点的数量只有几十个,为新建的 Pod 找到合适的节点并不困难。但当节点的数量扩大到几千台甚至更多时,情况就复杂了: +如果节点只有几十个,为新建的 Pod 找到合适的节点并不困难。但当节点的数量扩大到几千台甚至更多时,情况就复杂了: - 首先,节点资源无时无刻不在变化,如果每次调度都需要数千次远程请求获取信息,势必因耗时过长,增加调度失败的风险。 - 其次,调度器频繁发起网络请求,极容易成为集群的性能瓶颈,影响整个集群的运行。 @@ -13,7 +13,7 @@ —— from Omega 论文 ::: -Omega 论文中提出了一种基于共享状态(图 7-36 中的 Scheduler Cache)的双循环调度机制,用来解决大规模集群的调度效率问题。双循环的调度机制不仅应用在 Google 的 Omega 系统中,也被 Kubernetes 继承下来。 +Omega 论文中提出了一种基于“共享状态”(Scheduler Cache)的双循环调度机制,用来解决大规模集群的调度效率问题。双循环的调度机制不仅应用在 Google 的 Omega 系统中,也被 Kubernetes 继承下来。 Kubernetes 默认调度器(kube-scheduler)双循环调度机制如图 7-36 所示。 @@ -24,30 +24,21 @@ Kubernetes 默认调度器(kube-scheduler)双循环调度机制如图 7-36 根据图 7-36 可以看出,Kubernetes 调度的核心在于两个互相独立的控制循环。 -第一个控制循环称为“Informer 循环”。该循环的主要逻辑是启动一系列 Informer 监听(Watch)API 资源(主要是 Pod 和 Node)状态的变化。当 API 资源变化时,触发 Informer 回调函数进一步处理。如一个待调度 Pod 被创建后,触发 Pod Informer 回调函数,该回调函数将 Pod 入队到调度队列中(PriorityQueue),待下一阶段处理。 +第一个控制循环被称为“Informer 循环”。其主要逻辑是启动多个 Informer 来监听 API 资源(主要是 Pod 和 Node)状态的变化。一旦资源发生变化,Informer 会触发回调函数进行进一步处理。例如,当一个待调度的 Pod 被创建时,Pod Informer 会触发回调,将 Pod 入队到调度队列(PriorityQueue),以便在下一阶段处理。 -当 API 资源变化时,Informer 的回调函数还承担更新调度器缓存(即 Scheduler Cache)职责,以便尽可能将 Pod、Node 的信息缓存化,提升后续阶段调度算法的执行效率。 +当 API 资源发生变化时,Informer 的回调函数还负责更新调度器缓存(Scheduler Cache),以便将 Pod 和 Node 信息尽可能缓存,从而提高后续调度算法的执行效率。 -第二个控制循环称为“Scheduling 循环”。该循环主要逻辑是不断地从调度队列(PriorityQueue)中出队一个 Pod。然后,触发两个最核心的调度阶段:预选阶段(图 7-36 中的 Predicates)和优选阶段(图 7-36 中的 Priority)。 - -这里有必要补充调度器的扩展机制。Kubernetes 从 v1.15 版本起,为默认调度器(kube-scheduler)设计了可扩展的机制 —— Scheduling Framework。这个设计的主要目的,是在调度器生命周期的关键点上(图7-37 中绿色矩形箭头框),向外暴露可以扩展和实现自定义调度逻辑的接口。 - -:::tip -需要注意的是,上述可扩展的机制是使用标准 Go 语言插件机制实现的,需要按照规范编写 Go 代码,通过静态编译集成进去。它的通用性和前文提到的 CNI、CSI 或者 CRI 无法相提并论。 -::: +第二个控制循环是“Scheduling 循环”。其主要逻辑是从调度队列(PriorityQueue)中不断出队一个 Pod,并触发两个核心的调度阶段:预选阶段(图 7-36 中的 Predicates)和优选阶段(图 7-36 中的 Priority)。 +Kubernetes 从 v1.15 版本起,为默认调度器(kube-scheduler)设计了可扩展的机制 —— Scheduling Framework。其主要目的是在调度器生命周期的关键点(如图7-37中的绿色矩形箭头框所示)暴露可扩展接口,允许实现自定义的调度逻辑。这套可扩展的机制是使用标准 Go 语言插件机制实现的,需要按照规范编写 Go 代码,通过静态编译集成进去。所以,它的通用性和前文提到的 CNI、CSI 或者 CRI 无法相提并论。 :::center ![](../assets/scheduling-framework-extensions.svg)
图 7-37 Pod 的调度上下文以及调度框架公开的扩展点 ::: -本文一直强调“**默认**调度器”,指的是本文所阐述的调度逻辑由 Kubernetes 内置一批插件完成。如果你编写自己的调度插件注册到 Scheduling Framework 的扩展点,也就跟默认调度逻辑没有关系了。 +接下来,我们回到调度处理逻辑,首先来看预选阶段的处理。 -我们回到调度处理逻辑中,先来看预选阶段的处理。 - -预选阶段的主要逻辑是在调度器生命周期的 PreFilter 和 Filter 阶段,调用相关的过滤插件筛选出符合 Pod 要求的 Node 节点集合。 - -Kubernetes 默认调度器中内置的一批筛选插件,如下所示。 +预选阶段的主要逻辑是在调度器生命周期的 PreFilter 和 Filter 阶段,调用相关的过滤插件,筛选出符合 Pod 要求的节点集合。以下是 Kubernetes 默认调度器内置的一些筛选插件: ```go // k8s.io/kubernetes/pkg/scheduler/algorithmprovider/registry.go func getDefaultConfig() *schedulerapi.Plugins { @@ -73,18 +64,17 @@ func getDefaultConfig() *schedulerapi.Plugins { } ``` -上述插件本质上是按照 Scheduling Framework 规范实现 Filter 方法,根据方法内预设的策略筛选节点。总结它们的筛选策略为下述三类: +上述插件本质上是按照 Scheduling Framework 规范实现 Filter 方法,根据一系列预设的策略筛选节点。它们的筛选策略可以总结为以下三类: - - **通用过滤策略**:负责最基础的筛选策略。例如,检查节点可用资源是否满足 Pod 请求(request),检查 Pod 申请的宿主机端口号(spec.nodeport)是否跟节点中端口号冲突。对应的插件有 noderesources、nodeports 等。 - - **节点相关的过滤策略**:与节点相关的筛选策略。例如,检查 Pod 中污点容忍度(tolerations)是否匹配节点的污点(taints);检查待调度 Pod 节点亲和性设置(nodeAffinity)是否和节点匹配;检查待调度 Pod 与节点中已有 Pod 之间亲和性(Affinity)和反亲和性(Anti-Affinity)的关系。对应的插件有 tainttoleration、interpodaffinity、nodeunschedulable 等。 - - **Volume 相关的过滤策略**:例如,检查 Pod 挂载的 PV 是否冲突(AWS EBS 类型的 Volume 不允许被两个 Pod 同时使用)。或者是检查一个节点上某个类型的 PV 是否超过了一定数目。对应的插件 nodevolumelimits、volumerestrictions 等。 + - **通用过滤策略**:负责基础的筛选操作,例如检查节点是否有足够的可用资源满足 Pod 请求,或检查 Pod 请求的宿主机端口是否与节点中的端口冲突。相关插件包括 noderesources、nodeports 等。。 + - **节点相关的过滤策略**:与节点特性相关的筛选策略。例如,检查 Pod 的污点容忍度(tolerations)是否匹配节点的污点(taints),检查 Pod 的节点亲和性(nodeAffinity)是否与节点匹配,或检查 Pod 与节点中已有 Pod 之间的亲和性(Affinity)和反亲和性(Anti-Affinity)。相关插件包括 tainttoleration、interpodaffinity、nodeunschedulable 等。 + - **Volume 相关的过滤策略**:与存储卷相关的筛选策略。例如,检查 Pod 挂载的 PV 是否冲突(如 AWS EBS 类型的 Volume 不允许多个 Pod 同时使用),或者检查节点上某类型 PV 的数量是否超限。相关插件包括 nodevolumelimits、volumerestrictions 等。 -预选阶段执行完毕之后,得到一个可供 Pod 调度的所有节点列表。如果这个列表是空的,代表这个 Pod 不可调度。至此,预选阶段宣告结束,接着进入优选阶段。 +预选阶段执行完毕后,会得到一个可供 Pod 调度的节点列表。如果该列表为空,表示 Pod 无法调度。至此,预选阶段宣告结束,接着进入优选阶段。 -优选阶段设计和预选阶段的实现逻辑基本一致,即调用相关的打分插件,对预选阶段得到的节点进行排序,选择出一个评分最高的节点来运行 Pod。 - -Kubernetes 默认调度器中内置的打分插件如下所示,打分插件与筛选插件稍有不同,它多一个权重属性。 +优选阶段的设计与预选阶段类似,主要通过调用相关的打分插件,对预选阶段得到的节点进行排序,选择出评分最高的节点来运行 Pod。 +Kubernetes 默认调度器内置的打分插件如下所示。与筛选插件不同,打分插件额外包含一个权重属性。 ```go // k8s.io/kubernetes/pkg/scheduler/algorithmprovider/registry.go func getDefaultConfig() *schedulerapi.Plugins { @@ -105,31 +95,32 @@ func getDefaultConfig() *schedulerapi.Plugins { } ``` -优选阶段最重要的优选策略是 NodeResources.LeastAllocated,它的计算公式大致如下: +优选阶段最重要的策略是 NodeResources.LeastAllocated,它的计算公式如下: $ \text{score} = \frac{\frac{\left( \text{capacity}_{\text{cpu}} - \sum_{\text{pods}}\text{requested}_{\text{cpu}} \right) \times 10 }{\text{capacity}_{\text{cpu}}} + {\frac{ \left( \text{capacity}_{\text{memeory}} - \sum_{\text{pods}}(\text{requested}_{\text{memeory}})\right) \times 10 }{\text{capacity}_{\text{memeory}}} }}{2} $ -可以看到,上述公式实际上是根据节点中 CPU 和内存资源剩余量来打分,使得 Pod 倾向于被调度到资源使用较少的节点,避免某些节点资源过载而其他节点资源闲置。 +上述公式实际上是根据节点中 CPU 和内存资源的剩余量进行打分,从而使 Pod 更倾向于调度到资源使用较少的节点,避免某些节点资源过载而其他节点资源闲置。 -与 NodeResources.LeastAllocated 一起搭配的,还有 NodeResources.BalancedAllocation 策略,它的计算公式如下。 +与 NodeResources.LeastAllocated 策略配合使用的,还有 NodeResources.BalancedAllocation 策略,它的计算公式如下: $ \text{score} = 10 - \text{variance}(\text{cpuFraction}, \text{memoryFraction}, \text{volumeFraction}) \times 10 $ -这里的 Fraction 指的是资源的利用比例。笔者以 cpuFraction 为例,它的计算公式是: +这里的 Fraction 指的是资源利用比例。笔者以 cpuFraction 为例,它的计算公式如下: $ -\text{cpuFraction} = \frac{\text{ Pod 的 CPU 请求}}{\text{节点中 CPU 总量}} +\text{cpuFraction} = \frac{\text{ Pod 的 CPU 请求}}{\text{节点中 CPU 总量}} $ -memoryFraction、volumeFraction 也是类似的概念。Fraction 算法的主要作用是:计算资源使用比例的方差,来评估节点的资源(CPU、内存、volume)分配均衡程度,避免出现 CPU 被大量分配,但内存大量剩余的情况。方差越小,说明资源分配越均衡,因此得分越高。 -除了上述两种优选策略外,还有 InterPodAffinity(根据 Pod 之间的亲和性和反亲和性规则来打分)、Nodeaffinity(根据节点的亲和性规则来打分)、ImageLocality(根据节点中是否缓存容器镜像打分)、NodePreferAvoidPods(基于节点的注解信息打分)等等,笔者就不再一一解释了。 +memoryFraction 和 volumeFraction 也是类似的概念。Fraction 算法的主要作用是计算资源利用比例的方差,以评估节点的资源(CPU、内存、volume)分配是否均衡,避免出现 CPU 被过度分配而内存浪费的情况。方差越小,说明资源分配越均衡,得分也就越高。 + +除了上述两种优选策略外,还有 InterPodAffinity(根据 Pod 之间的亲和性、反亲和性规则来打分)、Nodeaffinity(根据节点的亲和性规则来打分)、ImageLocality(根据节点中是否缓存容器镜像打分)、NodePreferAvoidPods(基于节点的注解信息打分)等等,笔者就不再一一解释了。 -值得一提的是,上述打分插件的权重可以在调度器配置文件中设置,重新调整它们在调度决策中的影响力。例如,如果你希望更重视 NodePreferAvoidPods 插件的打分结果,可以为该插件设置更高的权重。如下所示: +值得注意的是,打分插件的权重可以在调度器配置文件中进行设置,以调整它们在调度决策中的影响力。例如,如果希望更重视 NodePreferAvoidPods 插件的打分结果,可以为该插件分配更高的权重,如下所示: ```yaml apiVersion: kubescheduler.config.k8s.io/v1 @@ -148,10 +139,10 @@ profiles: 经过优选阶段之后,调度器根据预定的打分策略为每个节点分配一个分数,最终选择出分数最高的节点来运行 Pod。如果存在多个节点分数相同,调度器则随机选择其中一个。 -选择出最终目标节点后,接下来就是通知目标节点内的 Kubelet 创建 Pod 了。 +选择出最终目标节点后,接下来就是通知目标节点内的 kubelet 创建 Pod 了。 -这一阶段中,调度器并不会直接与 Kubelet 通信,而是将 Pod 对象的 nodeName 字段的值,修改为上述选中 Node 的名字即可。Kubelet 会持续监控 Etcd 中 Pod 信息的变化,然后执行一个称为“Admin”的本地操作,确认资源是否可用、端口是否冲突,实际上就是通用过滤策略再执行一遍,二次确认 Pod 是否能在该节点运行。 +在这一阶段,调度器不会直接与 kubelet 通信,而是将 Pod 对象的 nodeName 修改为选定节点的名称。kubelet 会持续监控 Etcd 中 Pod 信息的变化,发现变动后执行一个名为“Admin”的本地操作,确认资源可用性和端口是否冲突。这相当于执行一遍通用的过滤策略,对 Pod 是否能在该节点运行进行二次确认。 -不过,从调度器更新 Etcd 中的 nodeName,到 Kueblet 检测到变化,再到二次确认是否可调度。这一系列的过程,会持续一段不等的时间。如果等到一切工作都完成,才宣告调度结束,那势必影响整体调度的效率。 +不过,从调度器更新 Etcd 中的 nodeName 到 kubelet 检测到变化,再到二次确认是否可调度,这一过程可能会持续一段不等的时间。如果等到所有操作完成才宣布调度结束,势必会影响整体调度效率。 -调度器采用了“乐观绑定”(Optimistic Binding)策略来解决上述问题。首先,调度器同步更新 Scheduler Cache 里的 Pod 的 nodeName 的信息,并发起异步请求 Etcd 更新 Pod 的 nodeName 信息,该步骤在调度生命周期中称 Bind 步骤。如果调度成功了,Scheduler Cache 和 Etcd 中的信息势必一致。如果调度失败了(也就是异步更新失败),也没有太大关系,Informer 会持续监控 Pod 的变化,将调度成功却没有创建成功的 Pod 清空 nodeName 字段,并重新同步至调度队列。 +调度器采用了“乐观绑定”(Optimistic Binding)策略来解决上述问题。首先,调度器更新 Scheduler Cache 里的 Pod 的 nodeName 的信息,并发起异步请求 更新 Etcd 中的远程信息,该操作在调度生命周期中称 Bind。如果调度成功了,Scheduler Cache 和 Etcd 中的信息势必一致。如果调度失败了(也就是异步更新失败),也没有太大关系。因为 Informer 会持续监控 Pod 变化,只要将调度成功、但没有创建成功的 Pod nodeName 字段清空,然后同步至调度队列,待下一次调度解决即可。 diff --git a/container/storage.md b/container/storage.md index 375a1370..1e2f1f49 100644 --- a/container/storage.md +++ b/container/storage.md @@ -113,7 +113,7 @@ spec: 直接使用 PV 时,需要详细描述存储的配置信息,这对业务工程师并不友好。业务工程师只想知道我有多大的空间、I/O 是否满足要求,肯定不关心存储底层的配置细节。 -为了简化存储的使用,Kubernetes 将存储服务再次抽象,把业务工程师关心的逻辑再抽象一层,于是有了 PVC(Persistent Volume Claim,持久卷声明)。PVC 和 PV 的设计其实和软件开发中的“面向对象”的思想一致: +为了简化存储的使用,Kubernetes 将存储服务再次抽象,把业务工程师关心的逻辑再抽象一层,于是有了 PVC(Persistent Volume Claim,持久卷声明),这种设计很像软件开发中的“面向对象”思想: - PVC 可以理解为持久化存储的“接口”,它提供了对某种持久化存储的描述,但不提供具体的实现; - 而持久化存储的实现部分则由 PV 负责完成。 @@ -133,12 +133,12 @@ spec: storage: 3Gi ``` -现在,还有个问题,PV 和 PVC 两者之间并没有明确相关的绑定参数,它们之间是如何绑定的?这其实是一个自动过程,PV 和 PVC 的绑定依赖以下两个匹配条件: +现在,还有个问题,PV 和 PVC 两者之间并没有明确相关的绑定参数,它们之间是如何绑定的?PV 和 PVC 的绑定是自动的,依赖以下两个匹配条件: - **Spec 参数匹配**:Kubernetes 会根据 PVC 中声明的规格自动寻找符合条件的 PV。这包括存储容量、所需的访问模式(如 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany),以及存储类型(如文件系统或块存储); - **存储类匹配**:PV 和 PVC 必须具有相同的 storageClassName,它定义了存储类型和特性,确保 PVC 请求的存储资源与 PV 提供的资源一致。 -以下 YAML 配置展示了如何在 Pod 中使用 PVC。当 PVC 成功绑定到 PV 后,NFS 远程存储将被挂载到 Pod 内指定的目录,比如 nginx 容器中的 /data 目录。这样,Pod 内的应用就可以像使用本地存储一样,操作远程存储资源了。 +以下 YAML 配置展示了如何在 Pod 中使用 PVC。当 PVC 成功绑定到 PV 后,NFS 远程存储将被挂载到 Pod 内指定的目录,比如 nginx 容器中的 /data 目录。这样,Pod 内的应用就可以像使用本地存储一样,使用远程存储资源了。 ```yaml apiVersion: v1