github.com/gocrane/crane@v0.11.0/docs/tutorials/using-effective-hpa-to-scaling-with-effectiveness.zh.md (about) 1 # EffectiveHorizontalPodAutoscaler 2 3 EffectiveHorizontalPodAutoscaler(简称 EHPA)是 Crane 提供的弹性伸缩产品,它基于社区 HPA 做底层的弹性控制,支持更丰富的弹性触发策略(预测,观测,周期),让弹性更加高效,并保障了服务的质量。 4 5 - 提前扩容,保证服务质量:通过算法预测未来的流量洪峰提前扩容,避免扩容不及时导致的雪崩和服务稳定性故障。 6 - 减少无效缩容:通过预测未来可减少不必要的缩容,稳定工作负载的资源使用率,消除突刺误判。 7 - 支持 Cron 配置:支持 Cron-based 弹性配置,应对大促等异常流量洪峰。 8 - 兼容社区:使用社区 HPA 作为弹性控制的执行层,能力完全兼容社区。 9 10 ## 产品功能 11 12 一个简单的 EHPA yaml 文件如下: 13 14 ```yaml 15 apiVersion: autoscaling.crane.io/v1alpha1 16 kind: EffectiveHorizontalPodAutoscaler 17 metadata: 18 name: php-apache 19 spec: 20 scaleTargetRef: #(1) 21 apiVersion: apps/v1 22 kind: Deployment 23 name: php-apache 24 minReplicas: 1 #(2) 25 maxReplicas: 10 #(3) 26 scaleStrategy: Auto #(4) 27 metrics: #(5) 28 - type: Resource 29 resource: 30 name: cpu 31 target: 32 type: Utilization 33 averageUtilization: 50 34 prediction: #(6) 35 predictionWindowSeconds: 3600 #(7) 36 predictionAlgorithm: 37 algorithmType: dsp 38 dsp: 39 sampleInterval: "60s" 40 historyLength: "3d" 41 ``` 42 43 1. ScaleTargetRef 配置你希望弹性的工作负载。 44 2. MinReplicas 指定了自动缩容的最小值。 45 3. MaxReplicas 指定了自动扩容的最大值。 46 4. ScaleStrategy 定义了弹性的策略,值可以是 "Auto" and "Preview". 47 5. Metrics 定义了弹性阈值配置。 48 6. Prediction 定义了预测算法配置。 49 7. PredictionWindowSeconds 指定往后预测多久的数据。 50 51 ### 基于预测的弹性 52 53 大多数在线应用的负载都有周期性的特征。我们可以根据按天或者按周的趋势预测未来的负载。EHPA 使用 DSP 算法来预测应用未来的时间序列数据。 54 55 以下是一个开启了预测能力的 EHPA 模版例子: 56 ```yaml 57 apiVersion: autoscaling.crane.io/v1alpha1 58 kind: EffectiveHorizontalPodAutoscaler 59 spec: 60 prediction: 61 predictionWindowSeconds: 3600 62 predictionAlgorithm: 63 algorithmType: dsp 64 dsp: 65 sampleInterval: "60s" 66 historyLength: "3d" 67 68 ``` 69 70 #### 监控数据兜底 71 72 在使用预测算法预测时,你可能会担心预测数据不准带来一定的风险,EHPA 在计算副本数时,不仅会按预测数据计算,同时也会考虑实际监控数据来兜底,提升了弹性的安全性。 73 实现的原理是当你在 EHPA 中定义 `spec.metrics` 并且开启弹性预测时,EffectiveHPAController 会在创建底层管理的 HPA 时按策略自动生成多条 Metric Spec。 74 75 例如,当用户在 EHPA 的 yaml 里定义如下 Metric Spec: 76 ```yaml 77 apiVersion: autoscaling.crane.io/v1alpha1 78 kind: EffectiveHorizontalPodAutoscaler 79 spec: 80 metrics: 81 - type: Resource 82 resource: 83 name: cpu 84 target: 85 type: Utilization 86 averageUtilization: 50 87 ``` 88 89 它会自动转换成两条 HPA 的阈值配置: 90 ```yaml 91 apiVersion: autoscaling/v2beta1 92 kind: HorizontalPodAutoscaler 93 spec: 94 metrics: 95 - pods: 96 metric: 97 name: crane_pod_cpu_usage 98 selector: 99 matchLabels: 100 autoscaling.crane.io/effective-hpa-uid: f9b92249-eab9-4671-afe0-17925e5987b8 101 target: 102 type: AverageValue 103 averageValue: 100m 104 type: Pods 105 - resource: 106 name: cpu 107 target: 108 type: Utilization 109 averageUtilization: 50 110 type: Resource 111 ``` 112 113 在上面这个例子中,用户在 EHPA 创建的 Metric 阈值配置会自动转换成底层 HPA 上的两条 Metric 阈值配置:预测 Metric 阈值和实际监控 Metric 阈值 114 115 * **预测 Metric 阈值** 是一个 custom metric。值通过 Crane 的 MetricAdapter 提供。 116 * **实际监控 Metric 阈值**是一个 resource metric,它和用户在 EHPA 上定义的一样。这样 HPA 会根据应用实际监控的 Metric 计算副本数。 117 118 HPA 在配置了多个弹性 Metric 阈值时,在计算副本数时会分别计算每条 Metric 对应的副本数,并选择**最大**的那个副本数作为最终的推荐弹性结果。 119 120 #### 水平弹性的执行流程 121 122 1. EffectiveHPAController 创建 HorizontalPodAutoscaler 和 TimeSeriesPrediction 对象 123 2. PredictionCore 从 prometheus 获取历史 metric 通过预测算法计算,将结果记录到 TimeSeriesPrediction 124 3. HPAController 通过 metric client 从 KubeApiServer 读取 metric 数据 125 4. KubeApiServer 将请求路由到 Crane 的 MetricAdapter。 126 5. HPAController 计算所有的 Metric 返回的结果得到最终的弹性副本推荐。 127 6. HPAController 调用 scale API 对目标应用扩/缩容。 128 129 整体流程图如下: 130  131 132 #### 用户案例 133 我们通过一个生产环境的客户案例来介绍 EHPA 的落地效果。 134 135 我们将生产上的数据在预发环境重放,对比使用 EHPA 和社区的 HPA 的弹性效果。 136 137 下图的红线是应用在一天内的实际 CPU 使用量曲线,我们可以看到在8点,12点,晚上8点时是使用高峰。绿线是 EHPA 预测的 CPU 使用量。 138  139 140 下图是对应的自动弹性的副本数曲线,红线是社区 HPA 的副本数曲线,绿线是 EHPA 的副本数曲线。 141  142 143 可以看到 EHPA 具有以下优势: 144 145 * 在流量洪峰来临前扩容。 146 * 当流量先降后立刻升时不做无效缩容。 147 * 相比 HPA 更少的弹性次数却更高效。 148 149 ### ScaleStrategy 弹性策略 150 EHPA 提供了两种弹性策略:`Auto` 和 `Preview`。用户可以随时切换它并立即生效。 151 152 #### Auto 153 Auto 策略下 EHPA 会自动执行弹性行为。默认 EHPA 的策略是 Auto。在这个模式下 EHPA 会创建一个社区的 HPA 对象并自动接管它的生命周期。我们不建议用户修改或者控制这个底层的 HPA 对象,当 EHPA 被删除时,底层的 HPA 对象也会一并删除。 154 155 #### Preview 156 Preview 策略提供了一种让 EHPA 不自动执行弹性的能力。所以你可以通过 EHPA 的 desiredReplicas 字段观测 EHPA 计算出的副本数。用户可以随时在两个模式间切换,当用户切换到 Preview 模式时,用户可以通过 `spec.specificReplicas` 调整应用的副本数,如果 `spec.specificReplicas` 为空,则不会对应用执行弹性,但是依然会执行副本数的计算。 157 158 以下是一个配置成 Preview 模式的 EHPA 模版例子: 159 ```yaml 160 apiVersion: autoscaling.crane.io/v1alpha1 161 kind: EffectiveHorizontalPodAutoscaler 162 spec: 163 scaleStrategy: Preview # ScaleStrategy indicate the strategy to scaling target, value can be "Auto" and "Preview". 164 specificReplicas: 5 # SpecificReplicas specify the target replicas. 165 status: 166 expectReplicas: 4 # expectReplicas is the calculated replicas that based on prediction metrics or spec.specificReplicas. 167 currentReplicas: 4 # currentReplicas is actual replicas from target 168 ``` 169 170 ### HorizontalPodAutoscaler 社区兼容 171 EHPA 从设计之初就希望和社区的 HPA 兼容,因为我们不希望重新造一个类似 HPA 的轮子,HPA 在不断演进的过程已经解决了很多通用的问题,EHPA 希望在 HPA 的基础上提供更高阶的 CRD,EHPA 的功能是社区 HPA 的超集。 172 173 EHPA 也会持续跟进支持 HPA 的新功能。 174 175 ### EffectiveHorizontalPodAutoscaler status 176 EHPA 的 Status 包括了自身的 Status 同时也汇聚了底层 HPA 的部分 Status。 177 178 以下是一个 EHPA 的 Status yaml例子: 179 ```yaml 180 apiVersion: autoscaling.crane.io/v1alpha1 181 kind: EffectiveHorizontalPodAutoscaler 182 status: 183 conditions: 184 - lastTransitionTime: "2021-11-30T08:18:59Z" 185 message: the HPA controller was able to get the target's current scale 186 reason: SucceededGetScale 187 status: "True" 188 type: AbleToScale 189 - lastTransitionTime: "2021-11-30T08:18:59Z" 190 message: Effective HPA is ready 191 reason: EffectiveHorizontalPodAutoscalerReady 192 status: "True" 193 type: Ready 194 currentReplicas: 1 195 expectReplicas: 0 196 197 ``` 198 199 ### Cron-based autoscaling 200 EffectiveHorizontalPodAutoscaler 支持基于 cron 的自动缩放。 201 202 除了基于监控指标,有时节假日和工作日的工作负载流量存在差异,简单的预测算法可能效果不佳。然后可以通过设置周末 cron 来支持更大数量的副本来弥补预测的不足。 203 204 对于一些非 web 流量的应用,比如一些应用不需要在周末使用,可以把工作负载的副本数减少到 1,也可以配置 cron 来降低你的服务成本。 205 206 以下是 `EHPA Spec` 中的 cron 主要字段: 207 208 - `CronSpec`:可以设置多个 cron 自动伸缩配置,cron cycle 可以设置循环的开始时间和结束时间,并且工作负载的副本数可以在时间范围内持续保持为设定的目标值。 209 - `Name`:cron 标识符 210 - `TargetReplicas`:此 cron 时间范围内工作负载的目标副本数。 211 - `Start`:cron 的开始时间,标准 linux crontab 格式 212 - `End`:cron 的结束时间,标准 linux crontab 格式 213 214 215 一些云厂商和社区当前的 cron 自动缩放功能存在一些缺点。 216 217 1. cron 能力单独提供,没有在全局视图中进行自动缩放,与 HPA 兼容性差,与其他缩放触发器冲突。 218 2. cron 的语义和行为不是很匹配,使用时甚至很难理解,很容易误导用户,导致自动伸缩失败。 219 220 下图显示了当前 EHPA cron 自动伸缩实现与其他 cron 能力的对比。 221 222  223 224 225 针对以上问题,EHPA 实现的 cron autoscaling 是在与 HPA 兼容的基础上设计的,cron 作为 HPA 的一个指标,与其他指标一起作用于工作负载。 226 227 另外,cron 的设置也很简单。单独配置 cron 时,不在活动时间范围内时,不会对工作负载执行缩放。 228 229 230 #### Cron working without other metrics 231 假设你没有配置其他指标,你只需配置 cron 本身即可工作。 232 ```yaml 233 apiVersion: autoscaling.crane.io/v1alpha1 234 kind: EffectiveHorizontalPodAutoscaler 235 metadata: 236 name: php-apache-local 237 spec: 238 # ScaleTargetRef 关联到需扩缩容的工作负载 239 scaleTargetRef: 240 apiVersion: apps/v1 241 kind: Deployment 242 name: php-apache 243 minReplicas: 1 # MinReplicas : autoscaler 缩放的最低副本数 244 maxReplicas: 100 # MaxReplicas : autoscaler 缩放的最大副本数 245 scaleStrategy: Auto # ScaleStrategy : 缩放工作负载时候,所采用的策略。可选值为 "Auto" "Manual" 246 # 最好将Cron Scheduling设置为一个完整的时间周期,例如: 一天,一周 247 # 下面是一天的Cron Scheduling 248 #(targetReplicas) 249 #80 -------- --------- ---------- 250 # | | | | | | 251 #10 ------------ ----- -------- ---------- 252 #(time) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 253 #本地时区(timezone: "Local")意味着您使用运行Craned所在的服务器(或者可能是容器)的时区。例如,当Craned 是以UTC时区开始,那么它就是UTC。如果一开始是Asia/Shanghai,那么它就是Asia/Shanghai。 254 crons: 255 - name: "cron1" 256 timezone: "Local" 257 description: "scale down" 258 start: "0 0 ? * *" 259 end: "0 6 ? * *" 260 targetReplicas: 10 261 - name: "cron2" 262 timezone: "Local" 263 description: "scale up" 264 start: "0 6 ? * *" 265 end: "0 9 ? * *" 266 targetReplicas: 80 267 - name: "cron3" 268 timezone: "Local" 269 description: "scale down" 270 start: "00 9 ? * *" 271 end: "00 11 ? * *" 272 targetReplicas: 10 273 - name: "cron4" 274 timezone: "Local" 275 description: "scale up" 276 start: "00 11 ? * *" 277 end: "00 14 ? * *" 278 targetReplicas: 80 279 - name: "cron5" 280 timezone: "Local" 281 description: "scale down" 282 start: "00 14 ? * *" 283 end: "00 17 ? * *" 284 targetReplicas: 10 285 - name: "cron6" 286 timezone: "Local" 287 description: "scale up" 288 start: "00 17 ? * *" 289 end: "00 20 ? * *" 290 targetReplicas: 80 291 - name: "cron7" 292 timezone: "Local" 293 description: "scale down" 294 start: "00 20 ? * *" 295 end: "00 00 ? * *" 296 targetReplicas: 10 297 ``` 298 299 CronSpec 具有以下字段: 300 301 * **name** 定义了 cron 的名字,cron 名字在同一个 Ehpa 中必须是唯一的 302 * **description** 定义 cron 的详细描述。它可以是空的。 303 * **timezone** 定义Crane所要调度的 cron 时区。如果未指定,则默认使用`UTC`时区。你可以将它设置为`Local`,这将使用正在运行的Crane容器所在的时区。其实,你定义`America/Los_Angeles`也是可以的。 304 * **start** 定义 cron 开始调度的时间,是 crontab 格式。参考 [wiki-Cron](https://en.wikipedia.org/wiki/Cron) 305 * **end** 定义 cron 结束调度的时间,是 crontab 格式。参考 [wiki-Cron](https://en.wikipedia.org/wiki/Cron) 306 * **targetReplicas** 定义目标副本在 cron 处于活动状态时要扩展的工作负载,这意味着目标副本数介于开始时间和结束时间之间生效。 307 308 以上YAML定义,意味着一天当中,工作负载在每小时所需要保持的副本数。工作负载将会每天按照该规则执行。 309 310 ``` 311 #80 -------- --------- ---------- 312 # | | | | | | 313 #1 ------------ ----- -------- ---------- 314 #(time) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 315 ``` 316 317 记住**不要设置开始时间在结束时间之后**。 318 319 例如,当你设置以下内容时: 320 ``` 321 crons: 322 - name: "cron2" 323 timezone: "Local" 324 description: "scale up" 325 start: "0 9 ? * *" 326 end: "0 6 ? * *" 327 targetReplicas: 80 328 ``` 329 以上无效,因为开始总是晚于结束。 330 331 HPA 控制器始终根据工作负载所描述的副本数进行扩展,这意味着保留原有副本数不变。 332 333 334 #### Horizontal scaling process 335 336 cron 驱动和扩展过程有六个步骤: 337 338 1. `EffectiveHPAController` 创建 `HorizontalPodAutoscaler`,它被注入到`spec`中的`external cron metrics`中。 339 2. `HPAController` 从 `KubeApiServer` 读取 `external cron metrics` 340 3. `KubeApiServer` 将请求转发给 `MetricAdapter` 和 `MetricServer` 341 4. `MetricAdapter` 找到目标 hpa 的 `cron scaler`,并检测 `cron scaler` 是否处于活动状态。这意味着当前时间介于 cron 开始和结束计划时间之间。它将返回`TargetReplicas`中定义的`CronSpec`。 342 5. `HPAController` 计算所有 metrics 结果,并通过选择最大的一个为目标副本数。并由此创建一个新的`scale replicas`。 343 6. `HPAController` 使用 `Scale Api` 缩放目标 344 345 346 使用 EHPA 时,用户可以只配置 cron metric,让 EHPA 用作 cron hpa。 347 348 一个 EHPA 的多个 crons 将转换为一个`external metrics`。 349 350 HPA 将获取`external metrics`并在协调时计算目标副本。当存在多个指标的工作负载时,HPA 将选择最大的副本数来扩展。 351 352 353 354 #### Cron working with other metrics together 355 356 `EffectiveHorizontalPodAutoscaler` 兼容 `HorizontalPodAutoscaler`(内置在 kubernetes)。因此,如果你为 HPA 配置了指标,例如 cpu 或内存,那么 HPA 将根据它观察到的实时指标对副本数进行扩展。 357 358 通过 EHPA,用户可以同时配置 `CronMetric`、`PredictionMetric`、`OriginalMetric`。 359 360 **我们强烈建议你配置所有维度的指标。它们分别代表 cron 副本、先前预测的副本、后观察的副本。** 361 362 这是一个强大的功能。因为 HPA 总是选择由所有维度`metrics`计算的最大副本进行扩展。 363 364 这将保证你工作负载的 QoS,当你同时配置三种类型的自动缩放时,根据实际观察到的指标计算的副本最大,然后它将使用最大的一个。 365 366 尽管由于某些意想不到的原因,导致由`PredictionMetric`计算的副本更小。因此,你不必担心 QoS。 367 368 369 #### Mechanism 370 当`metrics adapter`处理`external cron metrics`请求时,`metrics adapter`将执行以下步骤。 371 372 ``` mermaid 373 graph LR 374 A[Start] --> B{Active Cron?}; 375 B -->|Yes| C(largest targetReplicas) --> F; 376 B -->|No| D{Work together with other metrics?}; 377 D -->|Yes| G(minimum replicas) --> F; 378 D -->|No| H(current replicas) --> F; 379 F[Result workload replicas]; 380 ``` 381 382 383 1. 没有活跃的cron,有两种情况: 384 385 - 没有其他 hpa 指标与 cron 一起使用,然后返回当前工作负载副本以保留原始所需的副本 386 - 当其他 hpa 指标与 cron 一起使用,将会返回最小值以消除cron对其他指标的影响。当 cron 与其他指标一起工作时,它不应该返回工作负载的原始副本数,因为可能有其他指标想要缩小工作负载的副本数。`HPA Controller`选择由所有指标计算的最大副本(这是硬代码中的 hpa 默认策略),cron 会影响 hpa。所以我们应该在 cron 不活动时移除 cron 效果,它应该返回最小值。 387 388 389 2. 有活跃的cron。我们使用`cron spec`中指定的最大目标副本。基本上,在同一时间段内不应有超过一个活跃的 cron,这不是最佳实践。 390 391 HPA 将获取`cron external metrics`,然后它会自行计算副本数。 392 393 #### Use Case 394 395 当你需要在午夜将工作负载副本数保持在最低限度,根据该需求配置了 cron。 396 397 你需要 HPA 来获取指标服务器观察到的真实指标,以根据实时观察到的指标进行扩展。 398 399 最后,你配置一个`prediction-driven metric`,通过预测方式提前扩大规模并在末期缩小规模。 400 401 ```yaml 402 apiVersion: autoscaling.crane.io/v1alpha1 403 kind: EffectiveHorizontalPodAutoscaler 404 metadata: 405 name: php-apache-multi-dimensions 406 spec: 407 # ScaleTargetRef 关联到需扩缩容的工作负载 408 scaleTargetRef: 409 apiVersion: apps/v1 410 kind: Deployment 411 name: php-apache 412 minReplicas: 1 # MinReplicas : 缩放的最小副本数 413 maxReplicas: 100 # MaxReplicas : 缩放的最大副本数 414 scaleStrategy: Auto # ScaleStrategy : 缩放工作负载时候,所采用的策略。可选值为 "Auto" "Manual" 415 # Metrics 包含了用于计算所需副本数的指标。 416 metrics: 417 - type: Resource 418 resource: 419 name: cpu 420 target: 421 type: Utilization 422 averageUtilization: 50 423 # Prediction 的配置定义了需要预测的资源 424 # 若不配置,则默认不启动 prediction 425 prediction: 426 predictionWindowSeconds: 3600 # PredictionWindowSeconds 是预测未来指标的时间窗口。 427 predictionAlgorithm: 428 algorithmType: dsp 429 dsp: 430 sampleInterval: "60s" 431 historyLength: "3d" 432 crons: 433 - name: "cron1" 434 description: "scale up" 435 start: "0 0 ? * 6" 436 end: "00 23 ? * 0" 437 targetReplicas: 100 438 ``` 439 440 441 ## 常见问题 442 443 ### 错误: unable to get metric crane_pod_cpu_usage 444 445 当你查看 EffectiveHorizontalPodAutoscaler 的 Status 时,可以会看到这样的错误: 446 447 ```yaml 448 - lastTransitionTime: "2022-05-15T14:05:43Z" 449 message: 'the HPA was unable to compute the replica count: unable to get metric 450 crane_pod_cpu_usage: unable to fetch metrics from custom metrics API: TimeSeriesPrediction 451 is not ready. ' 452 reason: FailedGetPodsMetric 453 status: "False" 454 type: ScalingActive 455 ``` 456 457 原因:不是所有的工作负载的 CPU 使用率都是可预测的,当无法预测时就会显示以上错误。 458 459 解决方案: 460 461 - 等一段时间再看。预测算法 `DSP` 需要一定时间的数据才能进行预测。希望了解算法细节的可以查看算法的文档。 462 - EffectiveHorizontalPodAutoscaler 提供一种保护机制,当预测失效时依然能通过实际的 CPU 使用率工作。