服务网格ASM为ACK集群内的服务通信提供了一种非侵入式的生成遥测数据的能力。这种遥测功能提供了服务行为的可观测性,可以帮助运维人员对应用程序进行故障排除、维护和优化,而不会带来任何额外负担。根据监控的四个黄金指标维度(延迟、流量、错误和饱和度),服务网格ASM为管理的服务生成一系列指标。本文介绍如何使用ASM指标实现工作负载的自动弹性伸缩。

前提条件

背景信息

服务网格ASM为管理的服务生成一系列指标。更多信息,请参见Istio标准指标

自动伸缩是一种根据资源使用情况进行自动扩缩工作负载的方法。Kubernetes中的自动伸缩具有以下两个维度:
  • 集群自动伸缩器CA(Cluster Autoscaler):用于处理节点伸缩操作,可以增加或减少节点。
  • 水平自动伸缩器HPA(Horizontal Pod Autoscaler):用于自动伸缩应用部署中的Pod,可以调节Pod的数量。
Kubernetes提供的聚合层允许第三方应用程序将自身注册为API Addon组件来扩展Kubernetes API。这样的Addon组件可以实现Custom Metrics API,并允许HPA访问任意指标。HPA会定期通过Resource Metrics API查询核心指标(例如CPU或内存)以及通过Custom Metrics API获取特定于应用程序的指标,包括ASM提供的可观测性指标。弹性伸缩

步骤一:开启采集Prometheus监控指标

  1. 登录ASM控制台
  2. 在左侧导航栏,选择服务网格 > 网格管理
  3. 网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理
  4. 在网格管理详情页面单击右上角的功能设置
    说明 请确保ASM实例的Istio为1.6.8.4及以上版本。
  5. 功能设置更新面板中选中开启采集Prometheus监控指标,然后单击确定

    ASM将自动生成采集Prometheus监控指标相关的EnvoyFilter配置。

步骤二:部署自定义指标API Adapter

  1. 下载Adapter安装包,关于Adapter安装包请参见kube-metrics-adapter。然后在ACK集群中安装部署自定义指标API Adapter。
    ## 如果使用Helm v3。
    helm -n kube-system install asm-custom-metrics ./kube-metrics-adapter  --set prometheus.url=http://prometheus.istio-system.svc:9090
  2. 安装完成后,通过以下方式确认kube-metrics-adapter已启用。
    • 确认autoscaling/v2beta已存在。
      kubectl api-versions |grep "autoscaling/v2beta"

      预期输出:

      autoscaling/v2beta
    • 确认kube-metrics-adapter Pod状态。
      kubectl get po -n kube-system |grep metrics-adapter

      预期输出:

      asm-custom-metrics-kube-metrics-adapter-85c6d5d865-2cm57          1/1     Running   0          19s
    • 列出Prometheus适配器提供的自定义外部指标。
      kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1" | jq .

      预期输出:

      {
        "kind": "APIResourceList",
        "apiVersion": "v1",
        "groupVersion": "external.metrics.k8s.io/v1beta1",
        "resources": []
      }

步骤三:部署示例应用

  1. 创建test命名空间。具体操作,请参见创建命名空间
  2. 启用Sidecar自动注入。具体操作,请参见安装Sidecar代理
  3. 部署示例应用。
    1. 创建podinfo.yaml文件。
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: podinfo
        namespace: test
        labels:
          app: podinfo
      spec:
        minReadySeconds: 5
        strategy:
          rollingUpdate:
            maxUnavailable: 0
          type: RollingUpdate
        selector:
          matchLabels:
            app: podinfo
        template:
          metadata:
            annotations:
              prometheus.io/scrape: "true"
            labels:
              app: podinfo
          spec:
            containers:
            - name: podinfod
              image: stefanprodan/podinfo:latest
              imagePullPolicy: IfNotPresent
              ports:
              - containerPort: 9898
                name: http
                protocol: TCP
              command:
              - ./podinfo
              - --port=9898
              - --level=info
              livenessProbe:
                exec:
                  command:
                  - podcli
                  - check
                  - http
                  - localhost:9898/healthz
                initialDelaySeconds: 5
                timeoutSeconds: 5
              readinessProbe:
                exec:
                  command:
                  - podcli
                  - check
                  - http
                  - localhost:9898/readyz
                initialDelaySeconds: 5
                timeoutSeconds: 5
              resources:
                limits:
                  cpu: 2000m
                  memory: 512Mi
                requests:
                  cpu: 100m
                  memory: 64Mi
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: podinfo
        namespace: test
        labels:
          app: podinfo
      spec:
        type: ClusterIP
        ports:
          - name: http
            port: 9898
            targetPort: 9898
            protocol: TCP
        selector:
          app: podinfo
    2. 部署podinfo。
      kubectl apply -n test -f podinfo.yaml
  4. 为了触发自动弹性伸缩,需要在命名空间test中部署负载测试服务,用于触发请求。
    1. 创建loadtester.yaml文件。
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: loadtester
        namespace: test
        labels:
          app: loadtester
      spec:
        selector:
          matchLabels:
            app: loadtester
        template:
          metadata:
            labels:
              app: loadtester
            annotations:
              prometheus.io/scrape: "true"
          spec:
            containers:
              - name: loadtester
                image: weaveworks/flagger-loadtester:0.18.0
                imagePullPolicy: IfNotPresent
                ports:
                  - name: http
                    containerPort: 8080
                command:
                  - ./loadtester
                  - -port=8080
                  - -log-level=info
                  - -timeout=1h
                livenessProbe:
                  exec:
                    command:
                      - wget
                      - --quiet
                      - --tries=1
                      - --timeout=4
                      - --spider
                      - http://localhost:8080/healthz
                  timeoutSeconds: 5
                readinessProbe:
                  exec:
                    command:
                      - wget
                      - --quiet
                      - --tries=1
                      - --timeout=4
                      - --spider
                      - http://localhost:8080/healthz
                  timeoutSeconds: 5
                resources:
                  limits:
                    memory: "512Mi"
                    cpu: "1000m"
                  requests:
                    memory: "32Mi"
                    cpu: "10m"
                securityContext:
                  readOnlyRootFilesystem: true
                  runAsUser: 10001
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: loadtester
        namespace: test
        labels:
          app: loadtester
      spec:
        type: ClusterIP
        selector:
          app: loadtester
        ports:
          - name: http
            port: 80
            protocol: TCP
            targetPort: http
    2. 部署负载测试服务。
      kubectl apply -n test -f loadtester.yaml
  5. 验证部署示例应用和负载测试服务是否成功。
    1. 确认Pod状态。
      kubectl get pod -n test

      预期输出:

      NAME                          READY   STATUS    RESTARTS   AGE
      loadtester-64df4846b9-nxhvv   2/2     Running   0          2m8s
      podinfo-6d845cc8fc-26xbq      2/2     Running   0          11m
    2. 进入负载测试器容器,并使用hey命令生成负载。
      export loadtester=$(kubectl -n test get pod -l "app=loadtester" -o jsonpath='{.items[0].metadata.name}')
      kubectl -n test exec -it ${loadtester} -c loadtester -- hey -z 5s -c 10 -q 2 http://podinfo.test:9898
      返回结果,生成负载成功,说明示例应用和负载测试服务部署成功。

步骤四:使用ASM指标配置HPA

定义一个HPA,该HPA将根据每秒接收的请求数来扩缩Podinfo的工作负载数量。当平均请求流量负载超过10 req/sec时,将指示HPA扩大部署。

  1. 创建hpa.yaml
    apiVersion: autoscaling/v2beta2
    kind: HorizontalPodAutoscaler
    metadata:
      name: podinfo
      namespace: test
      annotations:
        metric-config.external.prometheus-query.prometheus/processed-requests-per-second: |
          sum(
              rate(
                  istio_requests_total{
                    destination_workload="podinfo",
                    destination_workload_namespace="test",
                    reporter="destination"
                  }[1m]
              )
          ) 
    spec:
      maxReplicas: 10
      minReplicas: 1
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: podinfo
      metrics:
        - type: External
          external:
            metric:
              name: prometheus-query
              selector:
                matchLabels:
                  query-name: processed-requests-per-second
            target:
              type: AverageValue
              averageValue: "10"
  2. 部署HPA。
    kubectl apply -f hpa.yaml
  3. 验证HPA是否部署成功。

    列出Prometheus适配器提供的自定义外部指标。

    kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1" | jq .

    预期输出:

    {
      "kind": "APIResourceList",
      "apiVersion": "v1",
      "groupVersion": "external.metrics.k8s.io/v1beta1",
      "resources": [
        {
          "name": "prometheus-query",
          "singularName": "",
          "namespaced": true,
          "kind": "ExternalMetricValueList",
          "verbs": [
            "get"
          ]
        }
      ]
    }

    返回结果中包含自定义的ASM指标的资源列表,说明HPA部署成功。

验证自动弹性伸缩

  1. 进入测试器容器,并使用hey命令生成工作负载请求。
    kubectl -n test exec -it ${loadtester} -c loadtester -- sh
    ~ $ hey -z 5m -c 10 -q 5 http://podinfo.test:9898
  2. 查看自动伸缩状况。
    说明 默认情况下,指标每30秒执行一次同步,并且只有在最近3分钟~5分钟内容器没有重新缩放时,才可以进行放大或缩小。这样,HPA可以防止冲突决策的快速执行,并为集群自动扩展程序预留时间。
    watch kubectl -n test get hpa/podinfo

    预期输出:

    NAME      REFERENCE            TARGETS          MINPODS   MAXPODS   REPLICAS   AGE
    podinfo   Deployment/podinfo   8308m/10 (avg)   1         10        6          124m
    一分钟后,HPA将开始扩大工作负载,直到请求/秒降至目标值以下。负载测试完成后,每秒的请求数将降为零,并且HPA将开始缩减工作负载Pod数量,几分钟后上述命令返回结果中的REPLICAS副本数将恢复为一个。