diff --git a/assets/HPA.svg b/assets/HPA.svg index 7bdca05c..b79de6b4 100644 --- a/assets/HPA.svg +++ b/assets/HPA.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/container/auto-scaling.md b/container/auto-scaling.md index 7fa6dda5..e29eec0c 100644 --- a/container/auto-scaling.md +++ b/container/auto-scaling.md @@ -1,41 +1,95 @@ # 7.8 资源弹性伸缩 -应用的实际流量会不断变化,因此使用率也是不断变化的,应该有一种自动调整应用的资源的策略,譬如一个在线电子商城: -- 促销的时候访问量会增加,应该自动增加服务运算能力来应对; -- 当促销结束后,又需要自动降低服务的运算能力防止浪费。 - -运算能力的增减有两种方式:增减 Pod 的数量以及改变单个 Pod 可用资源,这两种方式分别对应了 Kubernetes 的 Pod 水平自动扩缩(Horizontal Pod Autoscaler,HPA)和 Pod 垂直自动扩缩(Vertical Pod Autoscaler,VPA)组件。 - +为了平衡服务负载的巨大波动及资源预估与实际使用之间的差距,Kubernetes 提供了三种资源自动伸缩(autoscaling)解决方案:HPA、VPA 以及节点自动伸缩(Cluster Autoscaler)组件。 ## 7.8.1 Pod 水平自动伸缩 -横向 Pod 自动扩展的思路是这样的:kubernetes 会运行一个 controller,周期性地监听 Pod 的资源使用情况: -- 当高于设定的阈值时,会自动增加 pod 的数量; -- 当低于某个阈值时,会自动减少 pod 的数量。 +HPA 全称是 Horizontal Pod Autoscaler(Pod 水平自动扩缩),是对 Kubernetes 的工作负载的副本数进行自动水平扩缩容的机制,也是 Kubernetes 中使用最广泛的自动扩缩机制。 + +HPA 的实现思路很简单,即通过监控业务繁忙情况做出相应的挑战。在业务忙时,扩容 Worload(如 Deployment)Pod 副本数量;等到业务闲下来时,自然又要把 Pod 副本数再缩下去。所以实现水平扩缩容的关键之一是:”如何识别业务的忙闲程度?“。 + +Kubernetes 提供一种标准的 Metrics 接口提供有关节点和 Pod 的资源使用情况的信息。如下所示,在 minikube 节点上一个 Metrics 接口响应示例。 + +```bash +$ kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes/minikube" | jq '.' +{ + "kind": "NodeMetrics", + "apiVersion": "metrics.k8s.io/v1beta1", + "metadata": { + "name": "minikube", + "selfLink": "/apis/metrics.k8s.io/v1beta1/nodes/minikube", + "creationTimestamp": "2022-01-27T18:48:43Z" + }, + "timestamp": "2022-01-27T18:48:33Z", + "window": "30s", + "usage": { + "cpu": "487558164n", + "memory": "732212Ki" + } +} +``` +最早,Metrics 接口只支持 CPU 和 内存的使用指标。后来为了适应更灵活的需求,Metrics API 开始扩展支持用户自定义 metrics 指标(custom metrics),自定义数据则需要用户自行开发 custom metrics server 调用其他服务(如 Prometheus)来获取自定义指标。 + +有了 Metrics 接口,也就是能识别业务的繁忙程度了。那么,如图 7-34 所示,使用 kubectl autoscale 命令创建 HPA,并设置监控 metrics 的类型(cpu-percent)、期望目标 metrics 数值(70%)以及 Workload 内 Pod 副本数量的区间(最小 1,最大 10)。 -自然,这里的阈值以及 Pod 的上限和下限的数量都是需要用户配置的。 +```bash +kubectl autoscale deployment foo --cpu-percent=70 --min=1 --max=10 +``` +然后,HPA 组件就会定期实时获取 metrics 数据并将它与目标期望值比较,决定是否扩缩。如果执行扩缩,则调用 deployment 的 scale 接口调整当前副本数,最终实现尽可能将 deployment 下的每个 Pod 的最终 metrics 指标维持到用户期望的水平。 :::center ![](../assets/HPA.svg)
- 图 7-34 Node 资源逻辑分配图 + 图 7-34 HPA 扩缩容的原理 ::: -上面这句话隐藏了一个重要的信息:HPA 只能和 RC、deployment、RS 这些可以动态修改 replicas 的对象一起使用,而无法用于单个 Pod、Daemonset(因为它控制的 Pod 数量不能随便修改)等对象。 +## 7.8.2 Pod 垂直自动伸缩 -目前官方的监控数据来源是 metrics server 项目,可以配置的资源 CPU、自定义的监控数据(比如 prometheus) 等。 +VPA 的全称是 Vertical Pod Autoscaler(Pod 垂直自动伸缩)。VPA 的实现思路和 HPA 基本一致,也是通过监控 Metrics 接口,然后评估指标做出相应的调整。与 HPA 调整 workload 副本数量的方式不同,VPA 调整的是 workload 的资源配额(如 Pod 的 CPU、内存 的 request、limit)。 -## 7.8.2 Pod 垂直自动伸缩 +值得一提的是,VPA 是 Kubernetes 中一个可选的附加组件,需要单独安装和配置,才能为特定的 Deployment 创建 VPA 资源,定义 Pod 的资源调整策略。如下为一个 VPA 配置示例,供读者参考: -和 HPA 的思路相似,只不过 VPA 调整的是单个 Pod 的 request 值(包括 CPU 和 memory)。VPA 包括三个组件: +```yaml +apiVersion: autoscaling.k8s.io/v1 +kind: VerticalPodAutoscaler +metadata: + name: example-app-vpa + namespace: default +spec: + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: example-app + updatePolicy: + updateMode: Auto # 决定 VPA 如何应用推荐的资源调整,也可以设置为 "Off" 或 "Initial" 来控制更新策略 +``` -- Recommander:消费 metrics server 或者其他监控组件的数据,然后计算 Pod 的资源推荐值。 -- Updater:找到被 vpa 接管的 Pod 中和计算出来的推荐值差距过大的,对其做 update 操作(目前是 evict,新建的 Pod 在下面 admission controller 中会使用推荐的资源值作为 request)。 -- Admission Controller:新建的 Pod 会经过该 Admission Controller,如果 Pod 是被 vpa 接管的,会使用 Recommander 计算出来的推荐值。 +将上述 yaml 文件提交到 Kubernetes 集群,通过 kubectl describe vpa 可以查看 VPA 的推荐的资源策略。 + +```bash +$ kubectl describe vpa example-app-vpa +... +Recommendation: + Container Recommendations: + Container Name: nginx + Lower Bound: + Cpu: 25m + Memory: 262144k + Target: + Cpu: 25m + Memory: 262144k + Uncapped Target: + Cpu: 25m + Memory: 262144k + Upper Bound: + Cpu: 11601m + Memory: 12128573170 +... +``` -可以看到,这三个组件的功能是互相补充的,共同实现了动态修改 Pod 请求资源的功能。 +由上可以看出,VPA 适用于负载动态变化较大、对资源使用要求不确定的应用场景。特别是在无法精确预估应用资源需求的情况下。 -## 7.8.3 基于事件驱动的弹性伸缩 +## 7.8.3 基于事件驱动的伸缩 -HPA 虽然能基于外部指标实现弹性伸缩,但缺点指标有限且粒度太粗。为了根据外部事件实现更细粒度的自动扩展,微软和红帽联合开发一种基于事件触发的 Kubernetes 自动伸缩器 KEDA(Kubernetes Event-driven Autoscaling)。 +HPA 虽然能基于外部指标实现弹性伸缩,但缺点指标的作用有限且粒度太粗。为了支持外部事件实现更细粒度的自动扩缩,微软和红帽联合开发一种基于事件触发的 Kubernetes 自动扩缩器 KEDA(Kubernetes Event-driven Autoscaling)。 KEDA 的出现并非取代 HPA,它们实际上是一种组合配合关系。KEDA 的工作原理如图 7-35 所示,用户通过配置 ScaledObject(缩放对象)来定义 Scaler 的工作方式,Scaler(KEDA 内部的组件)持续从外部系统获取实时数据,并将这些数据与配置的扩展条件进行比较。当条件满足时,Scaler 将触发扩展操作,调用 Kubernetes 的 Horizontal Pod Autoscaler(HPA)调整对应工作负载的 Pod 副本数。 @@ -53,7 +107,9 @@ KEDA 通过内置几十种常见的 Scaler 用来处理特定的事件源或指 - 时间 Scaler:根据特定的时间段触发扩展逻辑,例如每日的高峰期或夜间低峰期。 -以下是一个 Kafka Scaler 配置示例,它监控某个 Kafka 主题中的消息数量。当消息数量超过设定的阈值时,它会触发 Kubernetes 集群中的工作负载自动扩展以处理更多的消息;当消息处理完毕,消息队列变空时,Scaler 会触发缩减操作,减少 Pod 的副本数(可以缩减至 0,minReplicaCount)。 +以下是一个 Kafka Scaler 配置示例,它监控某个 Kafka 主题中的消息数量: +- 当消息数量超过设定的阈值时,它会触发 Kubernetes 集群中的工作负载自动扩展以处理更多的消息; +- 当消息处理完毕,消息队列变空时,Scaler 会触发缩减操作,减少 Pod 的副本数(可以缩减至 0,minReplicaCount)。 ```yaml apiVersion: keda.sh/v1alpha1 diff --git a/container/borg-omega-k8s.md b/container/borg-omega-k8s.md index 77322abd..dad8f66b 100644 --- a/container/borg-omega-k8s.md +++ b/container/borg-omega-k8s.md @@ -14,10 +14,11 @@ Google 内部第一代容器管理系统叫 Borg。Borg 的架构如图 7-1 所 图 7-1 Borg 架构图 [图片来源](https://research.google/pubs/large-scale-cluster-management-at-google-with-borg/) ::: -开发 Borg 的过程中,Google 的工程师为 Borg 设计了两种工作负载(workload)[^1]: +开发 Borg 的过程中,Google 的工程师为 Borg 设计了两种工作负载(Workload)[^1]: - **长期运行的服务(Long-Running Service)**:通常是对请求延迟敏感的在线业务,例如 Gmail、Google Docs 和 Web 搜索以及内部基础设施服务; - **批处理任务(Batch Job)**:通常用于一次性地、按批次处理一大批数据或执行一系列任务,涉及大量数据处理,需要较长的运行时间和较多的计算资源。典型的 Batch Job 为 Apache Hadoop 或 Spark 等框架进行的各类离线计算任务。 + 区分 2 种不同类型工作负载的原因在于: - **两者运行状态不同**:Long-Running Service 存在“环境准备ok,但进程没有启动”、“健康检查失败”等状态,这些状态 Batch Job 是没有的。运行状态不同,决定了两类应用生命周期管理、监控、资源分配操作的不同; @@ -100,5 +101,4 @@ Google 开发的第三套容器管理系统是 Kubernetes,其背景如下: 1. 开发者和运维团队无需再关心机器、操作系统等底层细节; 2. 基础设施团队引入新硬件和升级操作系统更加灵活,最大限度减少对线上应用和应用开发者的影响; 3. 将收集到的各类关键性能指标(如 CPU 使用率、内存用量、每秒查询率 QPS 等)与应用程序而非物理机器关联起来,显著提高了应用监控的精确度和可观测性,尤其是在系统垂直扩容、 机器故障或主动运维等场景。这直接促使软件可观测性领域应运而生。 - -[^1]: 工作负载(Workload),云计算环境中,工作负载是指消耗云资源的任何服务、应用程序或功能。 +源的任何服务、应用程序或功能。在 Kubernetes 中的 Workload 资源包括多种类型,例如 Deployment、StatefulSet、DaemonSet、ReplicaSet 等等 diff --git a/container/conclusion.md b/container/conclusion.md index 4c4ca2b7..8af1d0bd 100644 --- a/container/conclusion.md +++ b/container/conclusion.md @@ -2,9 +2,9 @@ 从 Google 的 Brog 到如今的 Kubernetes,容器化技术的最大益处早就超越了单纯的提高硬件资源使用率的范畴。 -Kubernetes 通过无数的扩展/接口,变成外部可扩展的功能,通过 CNI 插件,实现容器间通信,通过 CSI,所有的存储生态。通过 Device Plugin 又把资源扩展到 GPU、FPGA 这类物理设备,正是这种开放性的设计,Kubernetes 乘着整个开源社区的力量不断向前。 +Kubernetes 通过无数的扩展/接口,变成外部可扩展的功能:通过 CNI 插件,实现容器间通信;通过 CSI,所有的存储生态;通过 Device Plugin 机制把资源的支持扩展到 GPU、FPGA 以及各类硬件加速器等异构设备。正是这种开放性的设计,Kubernetes 乘着整个开源社区的力量不断向前。 -最关键的是,Kubernetes 以统一的方式抽象以上资源/基础设施的能力,并将这些抽象以声明式 API 的方式对外暴露,用户只需要关心应用的最终状态,而不是去关注底层的基础设施哪来的、如何实现、如何配置。屏蔽底层细节,为用户提供一种简单/一致/跨平台的方式来管理和部署应用,正是 Kubernetes 设计哲学的精髓所在。 +最关键的是,Kubernetes 以统一的方式抽象以上资源/基础设施的能力,并将这些抽象以声明式 API 的方式对外暴露,用户无需关注底层的基础设施哪来的、如何实现、如何配置,只需要关心应用的最终状态。屏蔽底层细节,为用户提供一种简单/一致/跨平台的方式来管理和部署应用,正是 Kubernetes 设计哲学的精髓所在。 本章参考内容: diff --git a/container/orchestration.md b/container/orchestration.md index a42c9092..5f7dad9e 100644 --- a/container/orchestration.md +++ b/container/orchestration.md @@ -197,7 +197,6 @@ Infra Container 的逻辑非常简单,主要负责处理如 SIGTERM、SIGINT - 文件系统隔离:容器之间需要独立的文件系统以避免冲突。如果容器之间需要实现文件共享,Kubernetes 提供了 Volume 支持(Volume 的概念将在本章 7.5 节中介绍); - PID 隔离:PID 命名空间的隔离是为了避免某些容器进程不具备 PID=1 的情况,这可能会导致容器启动失败(例如,使用 systemd 的容器)。 - 如果要共享 PID 命名空间,可以在声明 Pod 时设置 shareProcessNamespace 为 true,如下所示的 YAML 配置: ```yaml @@ -219,7 +218,15 @@ spec: 解决了容器的资源隔离、限制以及容器间协作问题,Kubernetes 的功能开始围绕容器和 Pod 不断向实际应用的场景扩展。 -因为 Pod 不可能只有一个实例,为了管理多个 Pod 实例的运行,Deployment 概念应运而生。Deployment 负责确保指定数量的 Pod 副本始终运行,从而提供高可用性。由于 Pod 的 IP 地址是动态分配的,因此需要一种机制来提供稳定的网络访问入口,并在 Pod 实例之间实现负载均衡,这就是 Service 的作用。为了支持应用层协议(如 HTTP 或 HTTPS)并实现基于域名或路径的细粒度路由,引入了 Ingress。Ingress 充当反向代理,根据定义的规则将进入的请求路由到后端的 Service 或 Pod,从而允许更复杂的流量管理和访问控制策略,等等。 +因为 Pod 不可能只有一个实例,为了管理多个 Pod 实例的运行,Kubernetes 抽象了更高层的 Workload。例如: + +- Deployment:用于管理无状态应用程序的 Pods,支持滚动更新和扩缩容。 +- StatefulSet:用于管理有状态应用程序,保证 Pods 的顺序和持久性。 +- DaemonSet:确保在每个节点上运行一个 Pod,通常用于集群管理或监控。 +- ReplicaSet:用于确保指定数量的 Pod 副本在运行。 +- Job/CronJob:用于管理一次性任务或定期任务。 + +由于 Pod 的 IP 地址是动态分配的,因此需要一种机制来提供稳定的网络访问入口,并在 Pod 实例之间实现负载均衡,这就是 Service 的作用。为了支持应用层协议(如 HTTP 或 HTTPS)并实现基于域名或路径的细粒度路由,引入了 Ingress。Ingress 充当反向代理,根据定义的规则将进入的请求路由到后端的 Service 或 Pod,从而允许更复杂的流量管理和访问控制策略,等等。 最终,围绕 Pod 绘制出图 7-5 所示的 Kubernetes 核心功能全景图。 @@ -253,7 +260,9 @@ Pod 承担的另一个重要职责是作为调度的原子单位。 组合多种不同角色的容器,共享资源、统一调度编排,在 Kubernetes 中是一种非常经典的容器设计模式 —— Sidecar(边车)模式。 -在 Sidecar 模式中,一个主容器(主要处理业务逻辑)与一个或多个辅助容器(提供附加功能)共享同一个 Pod。辅助容器实现特性的技术需求(如日志记录、监控、安全性或数据同步),将非业务逻辑从应用中剥离。如图 7-6 所示的 Sidecar 容器设计模式,能看到 Sidecar 容器通过增强或扩展主应用容器的功能,使开发一个高内聚、低耦合的软件变的更加容易。 +在 Sidecar 模式中,一个主容器(主要处理业务逻辑)与一个或多个辅助容器(提供附加功能)共享同一个 Pod。辅助容器实现特性的技术需求(如日志记录、监控、安全性或数据同步),将非业务逻辑从应用中剥离。 + +如图 7-6 所示的 Sidecar 容器设计模式,能看到 Sidecar 容器通过增强或扩展主应用容器的功能,使开发一个高内聚、低耦合的软件变的更加容易。 :::center ![](../assets/sidecar.svg)