本文最后编辑于 前,其中的内容可能需要更新。
背景 Kubernetes
的应用扩缩指标仅有CPU,通过这种单一指标来指导单一应用进行扩缩相对比较低效而且滞后。
在部分场景(大促营销/奖品开奖/定时秒杀)下,应用常态负载不高,出现扩容的情况多数为请求量激增导致的CPU使用率上升。而CPU负载飙高的情况下,应用多半已经难以正常运行开始报错退出了,此时再开始慢吞吞的扩容,等待扩容完成后,大量的流量也已经消失了。
另一个问题是HPA的独立性导致的: 举个例子,在流量激增的情况下,流量会先经过应用A的鉴权接口,此时A可能负载上升,发生扩容,而其余流量在这之后进入B应用,此时的流量可能有一部分已经由于A的网关限流功能被限制了,但对于其他应用来说依然存在较高的压力,此时CPU出现上升,才会出现这部分应用的扩容操作,也是一个问题。
存在的问题
应用扩缩指标单一,不能准确反应负载
各个应用扩缩策略相对独立,在应用有需求的时候无法做到根据其他应用的需求进行上下游的应用同时扩缩
针对性解决方案 为解决这些问题,我们有下面两种选择:
prometheus-adapter 先来说说prometheus-adapter,prometheus在配置中指定单一prometheus的地址,我们可以通过helm
来拉取helm chart
,修改参数后进行部署。
1 2 3 $ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts $ helm repo update $ helm pull --untar prometheus-community/prometheus-adapter
由于默认镜像在github的镜像仓库中,推荐同步到私有库后更换镜像地址:
1 2 3 4 5 6 repository: PRIVATE_REGISTRY/prometheus-adapter tag: "v0.11.2" pullPolicy: IfNotPresent pullSecrets: - acr-registry-token
我们还需要配置prometheus的地址:
1 2 3 4 5 prometheus: url: http://xxxxxxx.arms.aliyuncs.com port: 9090 path: "xxxxxxxxxxxxx"
规则配置 以下是重头戏:
我们此处以应用QPS为例:
查询应用QPS的promql为:
1 sum(irate(envoy_vhost_route_upstream_rq{envoy_clusterid="<YPUR_GW_ID>",route="<YOUR_ROUTE_NAME>"}[2m]))
由于我们通过envoy_clusterid
以及route
来匹配规则,而prometheus-adapter的custom
目前只匹配pod_name
和namespace
两个标签,所以我们采用external rules
1 2 3 4 5 6 7 8 9 10 11 12 13 external: - seriesQuery: 'envoy_vhost_route_upstream_rq' resources: namespaced: false name: matches: "^(.*)" as: "${1}_per_second" metricsQuery: 'sum(irate(<<.Series>>{<<.LabelMatchers>>}[1m]))'
注: 在最终的查询PromQL中,seriesQuery
会替换metricsQuery
中的<<.Series>>
,而<<.LabelMatchers>>
则会根据HPA中配置的值来替换。
设置好后直接应用
1 $ helm install prometheus-adapter ./prometheus-adapter -n prometheus-adapter --create-namespace
安装完成后可通过命令查看对应的resource:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1/namespaces/default/envoy_vhost_route_upstream_rq_per_second" |jq . { "kind": "MetricValueList", "apiVersion": "external.metrics.k8s.io/v1beta1", "metadata": { "selfLink": "/apis/external.metrics.k8s.io/v1beta1/namespaces/default/envoy_vhost_route_upstream_rq_per_second" }, "items": [ { "metricName": "nginx_vts_server_requests_per_second", "timestamp": "2023-12-20T11:30:47Z", "value": "644230m", "selector": null } ] }
然后我们可以通过HPA来定义这个规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 apiVersion: autoscaling/v2beta2 kind: HorizontalPodAutoscaler metadata: name: my-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: nginx-static minReplicas: 1 maxReplicas: 10 metrics: - type: External external: metric: name: envoy_vhost_route_upstream_rq_per_second selector: matchLabels: route: "xxxxx-router" envoy_clusterid: "gw-xxxxxxxxx" target: type: Value value: 5
当这个值超过5的时候就会触发扩容。
KEDA KEDA和prometheus-adapter不同,针对prometheus的配置放在HPA中,所以直接安装即可,1.22以及1.24版本的集群安装2.8.1版本
1 2 $ helm repo add kedacore https://kedacore.github.io/charts $ helm install keta kedacore/keda -n keda --versio 2.8.1
规则配置 自定义指标扩缩容配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: prometheus-scaledobject namespace: test spec: scaleTargetRef: name: www-static-base minReplicaCount: 2 maxReplicaCount: 20 triggers: - type: prometheus metadata: metricName: envoy_vhost_route_upstream_rq serverAddress: xxx threshold: '10' query: sum(irate(envoy_vhost_route_upstream_rq{envoy_clusterid="gw-xxxxxxx",route="xxxxxxx"}[2m]))
其中triggers
支持配置多种规则,KEDA内置了多种不同的scaler,如kafka,prometheus,CPU以及根据其他应用等比扩缩,一个示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 triggers: - type: prometheus metadata: metricName: envoy_vhost_route_upstream_rq serverAddress: <PROMETHEUS_ADDRESS> threshold: '10' query: <PROMQL> - type: cpu metricType: Utilization metadata: type: Utilization value: "600" - type: cron metadata: timezone: Asia/Shanghai start: 0 0 * * * end: 45 0 * * * desiredReplicas: "10" - type: kubernetes-workload metadata: podSelector: 'app=backend' value: '0.5' - type: kafka metadata: bootstrapServers: kafka.svc:9092 consumerGroup: my-group topic: test-topic lagThreshold: '5' activationLagThreshold: '3' offsetResetPolicy: latest allowIdleConsumers: false scaleToZeroOnInvalidOffset: false excludePersistentLag: false version: 1.0 .0 partitionLimitation: '1,2,10-20,31' sasl: plaintext tls: enable unsafeSsl: false