基于ELB监控指标的弹性伸缩实践
应用现状
在使用工作负载弹性伸缩时,Kubernetes默认提供基于CPU/内存等资源使用率指标进行伸缩。但是在流量突发的场景下,基于CPU/内存使用率资源使用率数据会滞后于ELB流量指标,无法及时反映应用实际需求。因此,对于某些需要快速弹性扩缩容的业务(例如抢购和社交媒体),仅依靠资源使用率进行扩缩容可能存在伸缩不及时的问题,无法及时满足业务的实际需求。在这种情况下,通过基于ELB的QPS数据进行弹性伸缩可以更加及时地响应业务需求。
解决方案
本文介绍一种基于ELB监控指标的弹性伸缩方法,相比CPU/内存使用率进行弹性伸缩,基于ELB的QPS数据弹性伸缩更有针对性,更加及时。
本方案的关键点是获取ELB的指标数据并上报到Prometheus,再将Prometheus中的数据转换成HPA能够识别的metric数据,然后HPA根据metric数据进行弹性伸缩。
基于ELB监控指标的弹性伸缩具体实施方案如下所示:
- 开发一个Prometheus exporter,获取ELB的指标,并转化成Prometheus需要的格式,上报到Prometheus。本文使用cloudeye-exporter作为示例。
- 将Prometheus的数据转换成Kubernetes metric api提供给HPA controller使用。
- 设置HPA规则,使用ELB的监控数据作为弹性伸缩指标。
本文介绍的方法不限于ELB指标,其他指标可按照类似方法操作。
前提条件
- 本实践需要您熟悉Prometheus。
- 在集群中安装3.10.1以上版本的云原生监控插件(kube-prometheus-stack)。
插件“数据存储配置”需开启“本地数据存储”。
构建exporter镜像
本文使用cloudeye-exporter实现ELB指标监控,如您需要自行开发exporter,请参见附录:自行开发一个exporter。
- 登录一台可访问公网且安装Docker的虚拟机,编写Dockerfile。
vi Dockerfile
Dockerfile内容如下:FROM ubuntu:18.04 RUN apt-get update \ && apt-get install -y git ca-certificates curl \ && update-ca-certificates \ && curl -O https://dl.google.com/go/go1.14.14.linux-amd64.tar.gz \ && tar -zxf go1.14.14.linux-amd64.tar.gz -C /usr/local \ && git clone -b master https://github.com/huaweicloud/cloudeye-exporter \ && export PATH=$PATH:/usr/local/go/bin \ && export GO111MODULE=on \ && export GOPROXY=https://goproxy.cn,direct \ && export GONOSUMDB=* \ && cd cloudeye-exporter \ && go build CMD ["/cloudeye-exporter/cloudeye-exporter -config=/tmp/clouds.yml"]
- 构建镜像,镜像名称为cloudeye-exporter,版本为1.0。
docker build --network host . -t cloudeye-exporter:1.0
- 上传镜像至SWR镜像仓库。
- (可选)登录SWR管理控制台,选择左侧导航栏的“组织管理”,单击页面右上角的“创建组织”,创建一个组织。
- 在左侧导航栏选择“我的镜像”,单击右侧“客户端上传”,在弹出的页面中单击“生成临时登录指令”,单击复制登录指令。
- 在集群节点上执行上一步复制的登录指令,登录成功会显示“Login Succeeded”。
- 为cloudeye-exporter镜像打标签。
docker tag [镜像名称1:版本名称1] [镜像仓库地址]/[组织名称]/[镜像名称2:版本名称2]
- [镜像名称1:版本名称1]:请替换为您本地所要上传的实际镜像的名称和版本名称。
- [镜像仓库地址]:可在SWR控制台上查询,b中登录指令末尾的域名即为镜像仓库地址。
- [组织名称]:请替换为a中创建的组织。
- [镜像名称2:版本名称2]:请替换为SWR镜像仓库中需要显示的镜像名称和镜像版本。
示例:
docker tag cloudeye-exporter:1.0 swr.cn-east-3.myhuaweicloud.com/cloud-develop/cloudeye-exporter:1.0
- 上传镜像至镜像仓库。
docker push [镜像仓库地址]/[组织名称]/[镜像名称2:版本名称2]
示例:
docker push swr.cn-east-3.myhuaweicloud.com/cloud-develop/cloudeye-exporter:1.0
终端显示如下信息,表明上传镜像成功。
... 030***: Pushed 1.0: digest: sha256:eb7e3bbd*** size: **
返回容器镜像服务控制台,在“我的镜像”页面,执行刷新操作后可查看到对应的镜像信息。
部署exporter
Prometheus可以动态监测,一般来说给资源打上Prometheus对应的annotations,Prometheus会自动采集监控信息(默认为“/metrics”路径)。本文使用cloudeye-exporter作为示例。
Prometheus中常用的annotations如下:
- prometheus.io/scrape:true表示该资源会作为监控目标。
- prometheus.io/path:采集的url,默认为/metrics。
- prometheus.io/port:采集endpoint的端口号。
- prometheus.io/scheme:默认为http,如果为了安全设置了https,此处需要改为https。
- 使用kubectl连接集群。
- 创建Secret,cloudeye-exporter将使用该Secret进行认证。
- 创建clouds.yml文件,文件内容如下:
global: prefix: "huaweicloud" scrape_batch_size: 10 auth: auth_url: "https://iam.cn-east-3.myhuaweicloud.com/v3" project_name: "cn-east-3" access_key: "********" secret_key: "***********" region: "cn-east-3"
其中:
- 获取上述文件的base64加密内容字符串。
cat clouds.yml | base64 -w0 ;echo
回显如下:
ICAga*****
- 创建clouds-secret.yaml文件,其内容如下:
apiVersion: v1 kind: Secret data: clouds.yml: ICAga***** #替换为base64加密字符串 metadata: annotations: description: '' name: 'clouds.yml' namespace: default #密钥所在的命名空间,需和deployment命名空间保持一致 labels: {} type: Opaque
- 创建密钥。
kubectl apply -f clouds-secret.yaml
- 创建clouds.yml文件,文件内容如下:
- 创建cloudeye-exporter-deployment.yaml文件,内容如下:
kind: Deployment apiVersion: apps/v1 metadata: name: cloudeye-exporter namespace: default spec: replicas: 1 selector: matchLabels: app: cloudeye-exporter version: v1 template: metadata: labels: app: cloudeye-exporter version: v1 spec: volumes: - name: vol-166055064743016314 secret: secretName: clouds.yml defaultMode: 420 containers: - name: container-1 image: swr.cn-east-3.myhuaweicloud.com/cloud-develop/cloudeye-exporter:1.0 # 上文构建的exporter镜像地址 command: - /cloudeye-exporter/cloudeye-exporter - '-config=/tmp/clouds.yml' resources: {} volumeMounts: - name: vol-166055064743016314 readOnly: true mountPath: /tmp terminationMessagePath: /dev/termination-log terminationMessagePolicy: File imagePullPolicy: IfNotPresent restartPolicy: Always terminationGracePeriodSeconds: 30 dnsPolicy: ClusterFirst securityContext: {} imagePullSecrets: - name: default-secret schedulerName: default-scheduler strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 25% maxSurge: 25% revisionHistoryLimit: 10 progressDeadlineSeconds: 600
创建上述工作负载。
kubectl apply -f cloudeye-exporter-deployment.yaml
- 创建cloudeye-exporter-service.yaml文件。
apiVersion: v1 kind: Service metadata: name: cloudeye-exporter namespace: default labels: app: cloudeye-exporter version: v1 annotations: prometheus.io/port: '8087' #采集endpoint的端口号 prometheus.io/scrape: 'true' #设置为true表示该资源会作为监控目标 prometheus.io/path: "/metrics" #采集的url,默认为/metrics prometheus.io/scheme: "http" #默认为http,如果为了安全设置了https,此处需要改为https spec: ports: - name: cce-service-0 protocol: TCP port: 8087 targetPort: 8087 selector: app: cloudeye-exporter version: v1 type: ClusterIP
创建上述Service。
kubectl apply -f cloudeye-exporter-service.yaml
对接Prometheus
Prometheus收集到监控数据后,需要将Prometheus的数据转换成Kubernetes metric api提供给HPA controller使用,这样HPA controller就能根据监控数据进行弹性伸缩。
本示例中需要监控工作负载相关联的ELB指标,因此目标工作负载需要使用负载均衡类型的Service或Ingress。
- 查看需要监控的工作负载访问方式,获取ELB监听器ID。
- 使用kubectl连接集群,添加Prometheus的配置。本示例中的采集配置为ELB指标,更多高级用法详情请参见Configuration。
- 新建prometheus-additional.yaml文件,添加以下内容并保存:
- job_name: elb_metric params: services: ['SYS.ELB'] kubernetes_sd_configs: - role: endpoints relabel_configs: - action: keep regex: '8087' source_labels: - __meta_kubernetes_service_annotation_prometheus_io_port - action: replace regex: ([^:]+)(?::\d+)?;(\d+) replacement: $1:$2 source_labels: - __address__ - __meta_kubernetes_service_annotation_prometheus_io_port target_label: __address__ - action: labelmap regex: __meta_kubernetes_service_label_(.+) - action: replace source_labels: - __meta_kubernetes_namespace target_label: kubernetes_namespace - action: replace source_labels: - __meta_kubernetes_service_name target_label: kubernetes_service
- 使用上述配置文件创建一个名为additional-scrape-configs的Secret。
kubectl create secret generic additional-scrape-configs --from-file prometheus-additional.yaml -n monitoring --dry-run=client -o yaml | kubectl apply -f -
- 编辑persistent-user-config配置项,开启AdditionalScrapeConfigs能力。
kubectl edit configmap persistent-user-config -n monitoring
在其中operatorConfigOverride字段下新增一行--common.prom.default-additional-scrape-configs-key=prometheus-additional.yaml,开启AdditionalScrapeConfigs能力,示例如下:
... data: lightweight-user-config.yaml: | customSettings: additionalScrapeConfigs: [] agentExtraArgs: [] metricsDeprecated: globalDeprecateMetrics: [] nodeExporterConfigOverride: [] operatorConfigOverride: - --common.prom.default-additional-scrape-configs-key=prometheus-additional.yaml ...
- 您可以前往Prometheus查看自定义采集指标是否采集成功。
- 新建prometheus-additional.yaml文件,添加以下内容并保存:
- 修改名为user-adapter-config的配置项。
kubectl edit configmap user-adapter-config -nmonitoring
在rules字段下添加以下内容并保存,其中lbaas_listener_id参数需要替换1中获取的监听器ID。apiVersion: v1 data: config.yaml: |- rules: - metricsQuery: sum(<<.Series>>{<<.LabelMatchers>>,lbaas_listener_id="*****"}) by (<<.GroupBy>>) resources: overrides: kubernetes_namespace: resource: namespace kubernetes_service: resource: service name: matches: huaweicloud_sys_elb_(.*) as: "elb01_${1}" seriesQuery: '{lbaas_listener_id="*****"}' ...
- 重新部署monitoring命名空间下的custom-metrics-apiserver工作负载。
创建HPA弹性伸缩规则
exporter上报到Prometheus的数据,经过Prometheus adapter监控数据转换成Kubernetes metric api后,就可以创建HPA规则实现弹性伸缩。
- 创建HPA规则示例如下,使用ELB的入流量来作为扩容的标准,当m7_in_Bps(网络流入速率)的值超过1k时,会触发名为nginx的Deployment弹性伸缩。
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: nginx namespace: default spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: nginx minReplicas: 1 maxReplicas: 10 metrics: - type: Object object: metric: name: elb01_listener_m7_in_Bps #监控指标名称 describedObject: apiVersion: v1 kind: Service name: cloudeye-exporter target: type: Value value: 1000
图2 已创建的HPA策略
- 创建完后,可以对负载进行压测(也就是通过ELB访问Pod),然后HPA controller会根据设置的值计算是否需要扩容。
单击HPA策略操作栏中的“事件”,可从K8s事件中查询扩缩容记录。图3 扩缩容事件
ELB监听器指标
通过本文方法可采集的ELB监听器指标如下:
指标 |
指标名称 |
单位 |
说明 |
---|---|---|---|
m1_cps |
并发连接数 |
个 |
统计负载均衡器当前处理的并发连接数量。 |
m1e_server_rps |
后端服务器重置数量 |
个/秒 |
该指标用于统计后端服务器发送至客户端的重置(RST)数据包的计数。这些重置由后端服务器生成,然后由负载均衡器转发。 |
m1f_lvs_rps |
负载均衡器重置数量 |
个/秒 |
该指标用于统计负载均衡器生成的重置(RST)数据包的计数。 |
m21_client_rps |
客户端重置数量 |
个/秒 |
该指标用于统计客户端发送至后端服务器的重置(RST)数据包的计数。这些重置由客户端生成,然后由负载均衡器转发。 |
m22_in_bandwidth |
入网带宽 |
bit/s |
该指标用于统计负载均衡器当前入网带宽。 |
m23_out_bandwidth |
出网带宽 |
bit/s |
该指标用于统计负载均衡器当前出网带宽。 |
m2_act_conn |
活跃连接数 |
个 |
该指标用于统计当前处理的活跃连接数量。 |
m3_inact_conn |
非活跃连接数 |
个 |
该指标用于统计当前处理的非活跃连接数量。 |
m4_ncps |
新建连接数 |
个 |
该指标用于统计当前处理的新建连接数量。 |
m5_in_pps |
流入数据包数 |
个 |
该指标用于统计流入负载均衡器的数据包。 |
m6_out_pps |
流出数据包数 |
个 |
该指标用于统计流出负载均衡器的数据包。 |
m7_in_Bps |
网络流入速率 |
byte/s |
该指标用于统计每秒流入负载均衡器的网络流量。 |
m8_out_Bps |
网络流出速率 |
byte/s |
该指标用于统计每秒流出负载均衡器的网络流量。 |
附录:自行开发一个exporter
Prometheus通过周期性的调用exporter的“/metrics”接口获取指标信息,应用只需要通过“/metrics”上报监控数据即可。Prometheus提供了各种语言的客户端,在应用中集成Prometheus客户端可以方便的实现“/metrics”接口,客户端具体请参见Prometheus CLIENT LIBRARIES,开发Exporter具体方法请参见WRITING EXPORTERS。
监控数据需要Prometheus的格式提供,每条数据提供ELB ID、监听器ID、Service所在的命名空间、Service名称以及Service的uid作为标签,如下所示。
获取上述数据的方法如下所示。
- 查询当前所有Service。
Service的返回信息中annotations字段可以查出Service关联的ELB。
- kubernetes.io/elb.id
- kubernetes.io/elb.class
- 根据上一步查询到ELB实例ID,使用查询监听器接口查询监听器ID。
- 获取ELB监控数据。
ELB的调用CES批量查询监控数据接口,查询ELB的监控数据,ELB详细的监控数据指标请参见ELB监控指标说明。例如如下几种参数
- m1_cps:并发连接数
- m5_in_pps:流入数据包数
- m6_out_pps:流出数据包数
- m7_in_Bps:网络流入速率
- m8_out_Bps:网络流出速率
- 按Prometheus的格式汇聚数据,并通过“/metrics”接口开放出去。
Prometheus客户端可以方便的实现“/metrics”接口,具体请参见Prometheus CLIENT LIBRARIES,开发Exporter具体方法请参见WRITING EXPORTERS。