Knative中提供了开箱即用、基于流量请求的自动扩缩容KPA(Knative Pod Autoscaler)功能。本文介绍如何基于流量请求数实现服务自动扩缩容。
前提条件
已创建ACK托管集群或ACK Serverless集群,且集群版本为1.20及以上。具体操作,请参见已创建Kubernetes托管版集群、已创建ACK Serverless集群。
实现原理
Knative Serving会为每个Pod注入一个名为queue-proxy
的QUEUE代理容器,该容器负责向Autoscaler报告业务容器的并发指标。Autoscaler接收到这些指标之后,会根据并发请求数及相应的算法,调整Deployment的Pod数量,从而实现自动扩缩容。
算法
Knative Pod Autoscaler(KPA)基于每个Pod的平均请求数(或并发数)进行自动扩缩容,Knative默认使用基于并发数的自动弹性,每个Pod的最大并发数为100。此外,Knative还提供了目标使用率(target-utilization-percentage)的概念,用于指定自动扩缩容的目标使用率。
基于并发数弹性为例,Pod数计算方式如为:Pod数=并发请求总数/(Pod最大并发数*目标使用率)
例如,如果服务中Pod最大并发数设置为10,目标使用率设置为0.7,此时如果接收到了100个并发请求,则Autoscaler就会创建15个Pod(即100/(0.7*10)≈15)。
KPA基于每个Pod的平均请求数(或并发数)来进行自动扩缩容,并结合了Stable稳定模式和Panic恐慌模式两个概念,以实现精细化的弹性。
Stable稳定模式
在稳定模式中,KPA会在默认的稳定窗口期(默认为60秒)内计算Pod的平均并发数。根据这个平均并发数,KPA会调整Pod的数量,以保持稳定的负载水平。
Panic恐慌模式
在恐慌模式中,KPA会在恐慌窗口期(默认为6秒)内计算Pod的平均并发数。恐慌窗口期=稳定窗口期*panic-window-percentage(panic-window-percentage取值是0~1,默认是0.1)。当请求突然增加导致当前Pod的使用率超过恐慌窗口百分比时,KPA会快速增加Pod的数量以满足负载需求。
在KPA中,弹性生效的判断是基于恐慌模式下计算得出的Pod数量是否超过恐慌阈值(PanicThreshold)。恐慌阈值=panic-threshold-percentage/100,panic-threshold-percentage默认为200,即恐慌阈值默认为2。
综上所述,如果在恐慌模式下计算得出的Pod数量大于或等于当前Ready Pod数量的两倍,那么KPA将使用恐慌模式下计算得出的Pod数量进行弹性生效;否则,将使用稳定模式下计算得出的Pod数量。
KPA配置介绍
config-autoscaler配置介绍
配置KPA,需要配置config-autoscaler,该参数默认已配置,以下为重点参数介绍。
执行以下命令,查看config-autoscaler。
kubectl -n knative-serving describe cm config-autoscaler
预期输出(config-autoscaler默认的ConfigMap):
apiVersion: v1
kind: ConfigMap
metadata:
name: config-autoscaler
namespace: knative-serving
data:
# 默认Pod最大并发数,默认值为100。
container-concurrency-target-default: "100"
# 并发数目标使用率。默认值为70,70实际表示0.7。
container-concurrency-target-percentage: "70"
# 默认每秒请求数(RPS)。默认值为200。
requests-per-second-target-default: "200"
# 突发请求容量参数主要是为了应对突发流量,以防止Pod业务容器被过载。当前默认值211。
# 它通过Activator作为请求缓冲区,通过这个参数的计算结果,来调节是否请求通过Activator组件。
# 当该值为0时,只有Pod缩容到0时,才切换到Activator。
# 当该值大于0并且container-concurrency-target-percentage设置为100时,请求总是会通过Activator。
# 当该值为-1,表示无限的请求突发容量。请求也总是会通过Activator。其他负值无效。
# 如果当前Ready Pod数*最大并发数-突发请求容量-恐慌模式计算出来的并发数<0,意味着突发流量超过了容量阈值,则切换到Activator进行请求缓冲。
target-burst-capacity: "211"
# 稳定窗口,默认值为60秒。
stable-window: "60s"
# 恐慌窗口比例,默认值为10,则表示默认恐慌窗口期为6秒(60*0.1=6)。
panic-window-percentage: "10.0"
# 恐慌阈值比例,默认值200。
panic-threshold-percentage: "200.0"
# 最大扩缩容速率,表示一次扩容最大数。实际计算方式:math.Ceil(MaxScaleUpRate*readyPodsCount) 。
max-scale-up-rate: "1000.0"
# 最大缩容速率,默认值为2,表示每次缩容一半。
max-scale-down-rate: "2.0"
# 是否开始缩容到0,默认开启。
enable-scale-to-zero: "true"
# 优雅缩容到0的时间,即延迟多久缩容到0,默认30秒。
scale-to-zero-grace-period: "30s"
# Pod缩容到0保留期,该参数适用于Pod启动成本较高的情况
scale-to-zero-pod-retention-period: "0s"
# 弹性插件类型,当前支持的弹性插件包括:KPA、HPA、AHA以及MPA(ACK Serverless集群中配合MSE支持缩容到0)。
pod-autoscaler-class: "kpa.autoscaling.knative.dev"
# activator请求容量。
activator-capacity: "100.0"
# 创建revision时,初始化启动的Pod数,默认1。
initial-scale: "1"
# 是否允许创建revision时,初始化0个Pod, 默认false,表示不允许。
allow-zero-initial-scale: "false"
# revision级别最小保留的Pod数量。默认0,表示最小值可以为0。
min-scale: "0"
# revision级别最大扩容的Pod数量。默认0,表示无最大扩容上限。
max-scale: "0"
# 表示延迟缩容时间。默认0,表示立即缩容。
scale-down-delay: "0s"
指标配置介绍
可以通过autoscaling.knative.dev/metric
注解为每个Revision配置指标,不同的弹性插件支持的指标配置不同。
支持的指标:
"concurrency"
、"rps"
、"cpu"
、"memory"
以及其他自定义指标。默认指标:
"concurrency"
。
Concurrency并发数指标配置
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-go
namespace: default
spec:
template:
metadata:
annotations:
autoscaling.knative.dev/metric: "concurrency"
每秒请求数(RPS)指标配置
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-go
namespace: default
spec:
template:
metadata:
annotations:
autoscaling.knative.dev/metric: "rps"
CPU指标配置
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-go
namespace: default
spec:
template:
metadata:
annotations:
autoscaling.knative.dev/class: "hpa.autoscaling.knative.dev"
autoscaling.knative.dev/metric: "cpu"
Memory指标配置
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-go
namespace: default
spec:
template:
metadata:
annotations:
autoscaling.knative.dev/class: "hpa.autoscaling.knative.dev"
autoscaling.knative.dev/metric: "memory"
目标阈值配置介绍
可以通过autoscaling.knative.dev/target
注解为每一个Revision配置目标阈值。通过container-concurrency-target-default
注解为Configmap全局配置目标阈值。
Revision级别配置
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-go
namespace: default
spec:
template:
metadata:
annotations:
autoscaling.knative.dev/target: "50"
全局配置
apiVersion: v1
kind: ConfigMap
metadata:
name: config-autoscaler
namespace: knative-serving
data:
container-concurrency-target-default: "200"
缩容到0配置介绍
通过全局配置是否缩容到0
enable-scale-to-zero
参数可取值为"false"
或"true"
,用于指定Knative服务在空闲时是否自动缩减为零副本。
apiVersion: v1
kind: ConfigMap
metadata:
name: config-autoscaler
namespace: knative-serving
data:
enable-scale-to-zero: "false" # 值被设置为"false",表示自动缩容功能被禁用,即Knative服务在空闲时不会自动缩减为零副本。
全局配置优雅缩容到0的时间
scale-to-zero-grace-period
参数用于指定Knative服务在缩减为零副本之前的等待时间。
apiVersion: v1
kind: ConfigMap
metadata:
name: config-autoscaler
namespace: knative-serving
data:
scale-to-zero-grace-period: "40s"
Pod缩容到0保留期
Revision级别配置
autoscaling.knative.dev/scale-to-zero-pod-retention-period
注解用于配置Knative服务的自动缩容功能,指定在服务空闲一段时间后保留的Pod的时间周期。apiVersion: serving.knative.dev/v1 kind: Service metadata: name: helloworld-go namespace: default spec: template: metadata: annotations: autoscaling.knative.dev/scale-to-zero-pod-retention-period: "1m5s" spec: containers: - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/helloworld-go:73fbdd56
全局级别配置
scale-to-zero-pod-retention-period
的配置项用于指定Knative服务在缩减为零副本之前保留的Pod的时间周期。apiVersion: v1 kind: ConfigMap metadata: name: config-autoscaler namespace: knative-serving data: scale-to-zero-pod-retention-period: "42s"
并发数配置介绍
并发数表示单个Pod能同时处理的最大请求数量。可以通过并发软限制配置、并发硬限制配置、目标使用率配置、每秒请求数(RPS)配置来设置并发数。
并发软限制配置
并发软限制是有针对性的限制,而不是严格执行的界限。在某些情况下,特别是请求突然爆发时,可能会超过该值。
Revision级别配置
apiVersion: serving.knative.dev/v1 kind: Service metadata: name: helloworld-go namespace: default spec: template: metadata: annotations: autoscaling.knative.dev/target: "200"
全局级别配置
apiVersion: v1 kind: ConfigMap metadata: name: config-autoscaler namespace: knative-serving data: container-concurrency-target-default: "200" # 指定Knative服务的默认容器并发目标。
并发硬限制配置(Revision级别)
仅当您的应用程序有明确的执行并发上限时,才建议使用硬限制配置。因为指定较低的硬限制可能会对应用程序的吞吐量和延迟产生负面影响。
并发硬限制是强制上限。如果并发达到硬限制,多余的请求将被缓冲,直到有足够的可用资源来执行请求。
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-go
namespace: default
spec:
template:
spec:
containerConcurrency: 50
目标使用率
使用目标利用率值调整并发值,该值指定Autoscaler实际目标值的百分比。这也称为资源预热,可以在请求达到定义的硬限制之前进行扩容。
例如,containerConcurrency
设置为10,目标利用率值设置为70(百分比),则当所有现有Pod的平均并发请求数达到7时,Autoscaler将创建一个新Pod。因为Pod从创建到Ready需要一定的时间,通过降低目标利用率值可以提前扩容Pod,从而减少冷启动导致的响应延迟等问题。
Revision级别配置
apiVersion: serving.knative.dev/v1 kind: Service metadata: name: helloworld-go namespace: default spec: template: metadata: annotations: autoscaling.knative.dev/target-utilization-percentage: "70" # 配置Knative服务的自动缩放功能,指定了目标资源利用率的百分比。 spec: containers: - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/helloworld-go:73fbdd56
全局级别配置
apiVersion: v1 kind: ConfigMap metadata: name: config-autoscaler namespace: knative-serving data: container-concurrency-target-percentage: "70" # Knative将尽量确保每个Pod的并发数不超过当前可用资源的70%。
每秒请求数(RPS)配置
RPS表示单个Pod每秒能处理的请求数。
Revision级别配置
apiVersion: serving.knative.dev/v1 kind: Service metadata: name: helloworld-go namespace: default spec: template: metadata: annotations: autoscaling.knative.dev/target: "150" autoscaling.knative.dev/metric: "rps" # 表示服务的自动缩放将根据每秒请求数(RPS)来调整副本数。 spec: containers: - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/helloworld-go:73fbdd56
全局级别配置
apiVersion: v1 kind: ConfigMap metadata: name: config-autoscaler namespace: knative-serving data: requests-per-second-target-default: "150"
场景一:设置并发请求数实现自动扩缩容
设置并发请求数,通过KPA实现自动扩缩容。
为集群部署Knative,具体操作,请参见在ACK集群中部署Knative、在ACK Serverless集群中部署Knative。
创建autoscale-go.yaml。
apiVersion: serving.knative.dev/v1 kind: Service metadata: name: autoscale-go namespace: default spec: template: metadata: labels: app: autoscale-go annotations: autoscaling.knative.dev/target: "10" # 设置当前最大并发请求数为10。 spec: containers: - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/autoscale-go:0.1
执行以下命令,部署autoscale-go.yaml。
kubectl apply -f autoscale-go.yaml
获取服务访问网关。
ALB
执行以下命令,获取服务访问网关。
kubectl get albconfig knative-internet
预期输出:
NAME ALBID DNSNAME PORT&PROTOCOL CERTID AGE knative-internet alb-hvd8nngl0lsdra15g0 alb-hvd8nng******.cn-beijing.alb.aliyuncs.com 2
MSE
执行以下命令,获取服务访问网关。
kubectl -n knative-serving get ing stats-ingress
预期输出:
NAME CLASS HOSTS ADDRESS PORTS AGE stats-ingress knative-ingressclass * 101.201.XX.XX,192.168.XX.XX 80 15d
ASM
执行以下命令,获取服务访问网关。
kubectl get svc istio-ingressgateway --namespace istio-system --output jsonpath="{.status.loadBalancer.ingress[*]['ip']}"
预期输出:
121.XX.XX.XX
Kourier
执行以下命令,获取服务访问网关。
kubectl -n knative-serving get svc kourier
预期输出:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kourier LoadBalancer 10.0.XX.XX 39.104.XX.XX 80:31133/TCP,443:32515/TCP 49m
使用Hey压测工具,执行30s内保持50个并发请求。
说明Hey压测工具的详细介绍,请参见Hey。
hey -z 30s -c 50 -host "autoscale-go.default.example.com" "http://121.199.XXX.XXX?sleep=100&prime=10000&bloat=5" # 121.199.XXX.XXX为网关IP。
预期输出:
结果正如所预期的,扩容了5个Pod。
场景二:设置扩缩容边界实现自动扩缩容
扩缩容边界指应用程序提供服务的最小和最大Pod数量。通过设置应用程序提供服务的最小和最大Pod数量实现自动扩缩容。
为集群部署Knative,具体操作,请参见在ACK集群中部署Knative、在ACK Serverless集群中部署Knative。
创建autoscale-go.yaml。
设置最大并发请求数为10,
min-scale
最小保留实例数为1,max-scale
最大扩容实例数为3。apiVersion: serving.knative.dev/v1 kind: Service metadata: name: autoscale-go namespace: default spec: template: metadata: labels: app: autoscale-go annotations: autoscaling.knative.dev/target: "10" autoscaling.knative.dev/min-scale: "1" autoscaling.knative.dev/max-scale: "3" spec: containers: - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/autoscale-go:0.1
执行以下命令,部署autoscale-go.yaml。
kubectl apply -f autoscale-go.yaml
获取服务访问网关。
ALB
执行以下命令,获取服务访问网关。
kubectl get albconfig knative-internet
预期输出:
NAME ALBID DNSNAME PORT&PROTOCOL CERTID AGE knative-internet alb-hvd8nngl0lsdra15g0 alb-hvd8nng******.cn-beijing.alb.aliyuncs.com 2
MSE
执行以下命令,获取服务访问网关。
kubectl -n knative-serving get ing stats-ingress
预期输出:
NAME CLASS HOSTS ADDRESS PORTS AGE stats-ingress knative-ingressclass * 101.201.XX.XX,192.168.XX.XX 80 15d
ASM
执行以下命令,获取服务访问网关。
kubectl get svc istio-ingressgateway --namespace istio-system --output jsonpath="{.status.loadBalancer.ingress[*]['ip']}"
预期输出:
121.XX.XX.XX
Kourier
执行以下命令,获取服务访问网关。
kubectl -n knative-serving get svc kourier
预期输出:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kourier LoadBalancer 10.0.XX.XX 39.104.XX.XX 80:31133/TCP,443:32515/TCP 49m
使用Hey压测工具,执行30s内保持50个并发请求。
说明Hey压测工具的详细介绍,请参见Hey。
hey -z 30s -c 50 -host "autoscale-go.default.example.com" "http://121.199.XXX.XXX" # 121.199.XXX.XXX为网关IP。
预期输出:
结果正如所预期,最多扩容出来了3个Pod,并且即使在无访问请求流量的情况下,保持了1个运行的Pod。