文档首页> 云容器引擎 CCE> 最佳实践> 弹性伸缩> 基于ELB监控指标的弹性伸缩实践
更新时间:2024-05-10 GMT+08:00

基于ELB监控指标的弹性伸缩实践

应用现状

在使用工作负载弹性伸缩时,Kubernetes默认提供基于CPU/内存等资源使用率指标进行伸缩。但是在流量突发的场景下,基于CPU/内存使用率资源使用率数据会滞后于ELB流量指标,无法及时反映应用实际需求。因此,对于某些需要快速弹性扩缩容的业务(例如抢购和社交媒体),仅依靠资源使用率进行扩缩容可能存在伸缩不及时的问题,无法及时满足业务的实际需求。在这种情况下,通过基于ELB的QPS数据进行弹性伸缩可以更加及时地响应业务需求。

解决方案

本文介绍一种基于ELB监控指标的弹性伸缩方法,相比CPU/内存使用率进行弹性伸缩,基于ELB的QPS数据弹性伸缩更有针对性,更加及时。

本方案的关键点是获取ELB的指标数据并上报到Prometheus,再将Prometheus中的数据转换成HPA能够识别的metric数据,然后HPA根据metric数据进行弹性伸缩。

基于ELB监控指标的弹性伸缩具体实施方案如下所示:

  1. 开发一个Prometheus exporter,获取ELB的指标,并转化成Prometheus需要的格式,上报到Prometheus。本文使用cloudeye-exporter作为示例。
  2. 将Prometheus的数据转换成Kubernetes metric api提供给HPA controller使用。
  3. 设置HPA规则,使用ELB的监控数据作为弹性伸缩指标。
图1 ELB流量与监控数据示意图

本文介绍的方法不限于ELB指标,其他指标可按照类似方法操作。

前提条件

  • 本实践需要您熟悉Prometheus。
  • 在集群中安装基于开源Prometheus云原生监控插件(kube-prometheus-stack)。该插件支持v1.17及以后的集群版本。

    插件部署模式需选择“Server模式”。

构建exporter镜像

本文使用cloudeye-exporter实现ELB指标监控,如您需要自行开发exporter,请参见附录:自行开发一个exporter

  1. 登录一台可访问公网且安装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 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"]

  2. 构建镜像,镜像名称为cloudeye-exporter,版本为1.0。

    docker build --network host . -t cloudeye-exporter:1.0

  3. 上传镜像至SWR镜像仓库。

    1. (可选)登录SWR管理控制台,选择左侧导航栏的“组织管理”,单击页面右上角的“创建组织”,创建一个组织。

      如已有组织可跳过此步骤。

    2. 在左侧导航栏选择“我的镜像”,单击右侧“客户端上传”,在弹出的页面中单击“生成临时登录指令”,单击复制登录指令。
    3. 在集群节点上执行上一步复制的登录指令,登录成功会显示“Login Succeeded”。
    4. 为cloudeye-exporter镜像打标签。

      docker tag [镜像名称1:版本名称1] [镜像仓库地址]/[组织名称]/[镜像名称2:版本名称2]

      • [镜像名称1:版本名称1]:请替换为您本地所要上传的实际镜像的名称和版本名称。
      • [镜像仓库地址]:可在SWR控制台上查询,b中登录指令末尾的域名即为镜像仓库地址。
      • [组织名称]:请替换为a中创建的组织。
      • [镜像名称2:版本名称2]:请替换为SWR镜像仓库中需要显示的镜像名称和镜像版本。

      示例:

      docker tag cloudeye-exporter:1.0 swr.ap-southeast-1.myhuaweicloud.com/cloud-develop/cloudeye-exporter:1.0

    5. 上传镜像至镜像仓库。

      docker push [镜像仓库地址]/[组织名称]/[镜像名称2:版本名称2]

      示例:

      docker push swr.ap-southeast-1.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。
  1. 使用kubectl连接集群。
  2. 创建Secret,cloudeye-exporter将使用该Secret进行认证。

    1. 创建clouds.yml文件,文件内容如下:
          global:
            prefix: "huaweicloud"
            scrape_batch_size: 10
          auth:
            auth_url: "https://iam.ap-southeast-1.myhuaweicloud.com/v3"
            project_name: "ap-southeast-1"
            access_key: "********"
            secret_key: "***********"
            region: "ap-southeast-1"

      其中:

      • auth_url:IAM终端节点,可通过地区和终端节点获取。
      • project_name:项目名称。您可在“我的凭证”页面,前往“项目列表”区域查看项目名称和项目ID。
      • access_key和secret_key:可通过访问密钥获取。
      • region:区域名称,需要与project_name中的项目对应。
    2. 获取上述文件的base64加密内容字符串。
      cat clouds.yml | base64 -w0 ;echo

      回显如下:

      ICAga*****
    3. 创建clouds-secret.yaml文件,其内容如下:
      apiVersion: v1
      kind: Secret
      data: 
        clouds.yml: ICAga*****  #替换为base64加密字符串
      metadata:
        annotations:
          description: ''
        name: 'clouds.yml'
        namespace: default  #密钥所在的命名空间,需和deployment命名空间保持一致
        labels: {}
      type: Opaque
    4. 创建密钥。
      kubectl apply -f clouds-secret.yaml

  3. 创建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.ap-southeast-1.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

  4. 创建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。

  1. 查看需要监控的工作负载访问方式,获取ELB监听器ID。

    1. 在CCE集群控制台中,选择左侧“服务发现”,在“服务”或“路由”页签下查看负载均衡类型的Service或Ingress,单击对应的负载均衡器名称跳转至ELB页面。

    2. 在“监听器”页面,查看工作负载所对应的监听器,并复制该监听器ID。

  2. 使用kubectl连接集群,添加Prometheus的配置。本示例中的采集配置为ELB指标,更多高级用法详情请参见Configuration

    1. 新建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
    2. 使用上述配置文件创建一个名为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 -
    3. 修改Prometheus对象。
      kubectl edit prometheus server -n monitoring

      在spec字段下添加以下内容并保存:

      spec:
        additionalScrapeConfigs:
          key: prometheus-additional.yaml
          name: additional-scrape-configs
    4. 执行以下命令,检验修改是否生效。
      kubectl get secret prometheus-server -n monitoring -o jsonpath="{.data['prometheus\.yaml\.gz']}" | base64 --decode | gzip -d | grep -A3 elb

      如有回显,则说明修改已生效。

  3. 修改名为user-adapter-config的配置项(历史版本插件中该配置项的名称为adapter-config)。

    kubectl edit configmap user-adapter-config -nmonitoring
    在rules字段下添加以下内容并保存,其中seriesQuery参数中需要替换1中获取的监听器ID。
    apiVersion: v1
    data:
      config.yaml: |-
        rules:
        - metricsQuery: sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)
          resources:
            overrides:
              kubernetes_namespace:
                resource: namespace
              kubernetes_service:
                resource: service
          name:
            matches: huaweicloud_sys_elb_(.*)
            as: "elb01_${1}"
          seriesQuery: '{lbaas_listener_id="*****"}' #ELB监听器ID
        ...

  4. 重新部署monitoring命名空间下的custom-metrics-apiserver工作负载。

创建HPA弹性伸缩规则

exporter上报到Prometheus的数据,经过Prometheus adapter监控数据转换成Kubernetes metric api后,就可以创建HPA规则实现弹性伸缩。

  1. 创建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策略

  2. 创建完后,可以对负载进行压测(也就是通过ELB访问Pod),然后HPA controller会根据设置的值计算是否需要扩容。

    单击HPA策略操作栏中的“事件”,可从K8s事件中查询扩缩容记录。
    图3 扩缩容事件

ELB监听器指标

通过本文方法可采集的ELB监听器指标如下:

表1 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作为标签,如下所示。

获取上述数据的方法如下所示。

  1. 查询当前所有Service。

    Service的返回信息中annotations字段可以查出Service关联的ELB。

    • kubernetes.io/elb.id
    • kubernetes.io/elb.class

  2. 根据上一步查询到ELB实例ID,使用查询监听器接口查询监听器ID。
  3. 获取ELB监控数据。

    ELB的调用CES批量查询监控数据接口,查询ELB的监控数据,ELB详细的监控数据指标请参见ELB监控指标说明。例如如下几种参数

    • m1_cps:并发连接数
    • m5_in_pps:流入数据包数
    • m6_out_pps:流出数据包数
    • m7_in_Bps:网络流入速率
    • m8_out_Bps:网络流出速率

  4. 按Prometheus的格式汇聚数据,并通过“/metrics”接口开放出去。

    Prometheus客户端可以方便的实现“/metrics”接口,具体请参见Prometheus CLIENT LIBRARIES,开发Exporter具体方法请参见WRITING EXPORTERS