FederatedHPA 基于自定义指标扩缩容

在 Karmada 中,为了自动扩展工作负载以满足需求,FederatedHPA 会跨多个集群扩/缩容工作负载。

FederatedHPA 不仅支持 CPU 和内存等资源指标,还支持自定义指标,这有助于扩展 FederatedHPA 的使用场景。

本文档将引导您完成这样一个案例:启用 FederatedHPA 利用自定义指标来自动扩缩容跨集群应用。

演示案例将执行以下操作:

federatedhpa-custom-metrics-demo

  • member1 集群中存在一个示例 Deployment 的 Pod。
  • Service 部署在 member1member2 集群。
  • 请求多集群 Service 并触发 Pod 的自定义指标(http_requests_total)增长。
  • Pod 副本将在 member1member2 集群中扩容。

前提条件

Karmada 已安装

您可以参考快速入门安装 Karmada,或直接运行 hack/local-up-karmada.sh 脚本,该脚本也用于运行 E2E 测试。

成员集群网络

确保至少已有两个集群加入 Karmada,并且成员集群之间的容器网络已连通。

  • 如果您使用 hack/local-up-karmada.sh 脚本部署 Karmada,Karmada 中会有 3 个成员集群,并且集群 member1member2 间的容器网络已连通。
  • 您可以使用 Submariner 或其他相关开源项目来连接成员集群之间的网络。

注意:为了防止路由冲突,集群中 Pod 和 Service 的 CIDR 必须互不重叠。

ServiceExport 和 ServiceImport 自定义资源已安装

我们需要在成员集群中安装 ServiceExportServiceImport 以启用多集群 Service。

Karmada 控制平面 上安装了 ServiceExportServiceImport 后,我们就可以创建 ClusterPropagationPolicy,将以下两个 CRD 分发到成员集群。

  1. # propagate ServiceExport CRD
  2. apiVersion: policy.karmada.io/v1alpha1
  3. kind: ClusterPropagationPolicy
  4. metadata:
  5. name: serviceexport-policy
  6. spec:
  7. resourceSelectors:
  8. - apiVersion: apiextensions.k8s.io/v1
  9. kind: CustomResourceDefinition
  10. name: serviceexports.multicluster.x-k8s.io
  11. placement:
  12. clusterAffinity:
  13. clusterNames:
  14. - member1
  15. - member2
  16. ---
  17. # propagate ServiceImport CRD
  18. apiVersion: policy.karmada.io/v1alpha1
  19. kind: ClusterPropagationPolicy
  20. metadata:
  21. name: serviceimport-policy
  22. spec:
  23. resourceSelectors:
  24. - apiVersion: apiextensions.k8s.io/v1
  25. kind: CustomResourceDefinition
  26. name: serviceimports.multicluster.x-k8s.io
  27. placement:
  28. clusterAffinity:
  29. clusterNames:
  30. - member1
  31. - member2

成员集群中已安装 prometheus 和 prometheus-adapter

我们需要为成员集群安装 prometheusprometheus-adapter 以提供自定义 metrics API。 可以通过在成员集群中运行以下命令来安装:

  1. git clone https://github.com/prometheus-operator/kube-prometheus.git
  2. cd kube-prometheus
  3. kubectl apply --server-side -f manifests/setup
  4. kubectl wait \
  5. --for condition=Established \
  6. --all CustomResourceDefinition \
  7. --namespace=monitoring
  8. kubectl apply -f manifests/

我们可以通过下面的命令验证安装:

  1. $ kubectl --kubeconfig=/root/.kube/members.config --context=member1 get po -nmonitoring
  2. NAME READY STATUS RESTARTS AGE
  3. alertmanager-main-0 2/2 Running 0 30h
  4. alertmanager-main-1 2/2 Running 0 30h
  5. alertmanager-main-2 2/2 Running 0 30h
  6. blackbox-exporter-6bc47b9578-zcbb7 3/3 Running 0 30h
  7. grafana-6b68cd6b-vmw74 1/1 Running 0 30h
  8. kube-state-metrics-597db7f85d-2hpfs 3/3 Running 0 30h
  9. node-exporter-q8hdx 2/2 Running 0 30h
  10. prometheus-adapter-57d9587488-86ckj 1/1 Running 0 29h
  11. prometheus-adapter-57d9587488-zrt29 1/1 Running 0 29h
  12. prometheus-k8s-0 2/2 Running 0 30h
  13. prometheus-k8s-1 2/2 Running 0 30h
  14. prometheus-operator-7d4b94944f-kkwkk 2/2 Running 0 30h

Karmada 控制平面已安装 karmada-metrics-adapter

我们需要在 Karmada 控制平面中安装 karmada-metrics-adapter 以提供 metrics API,通过运行以下命令来安装:

  1. hack/deploy-metrics-adapter.sh ${host_cluster_kubeconfig} ${host_cluster_context} ${karmada_apiserver_kubeconfig} ${karmada_apiserver_context_name}

如果您使用 hack/local-up-karmada.sh 脚本部署 Karmada,将默认安装 karmada-metrics-adapter

member1member2 集群中部署 Deployment

我们需要在 member1member2 集群中部署示例 Deployment(1 个副本)和 Service。

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: sample-app
  5. labels:
  6. app: sample-app
  7. spec:
  8. replicas: 1
  9. selector:
  10. matchLabels:
  11. app: sample-app
  12. template:
  13. metadata:
  14. labels:
  15. app: sample-app
  16. spec:
  17. containers:
  18. - image: luxas/autoscale-demo:v0.1.2
  19. name: metrics-provider
  20. ports:
  21. - name: http
  22. containerPort: 8080
  23. ---
  24. apiVersion: v1
  25. kind: Service
  26. metadata:
  27. labels:
  28. app: sample-app
  29. name: sample-app
  30. spec:
  31. ports:
  32. - name: http
  33. port: 80
  34. protocol: TCP
  35. targetPort: 8080
  36. selector:
  37. app: sample-app
  38. type: ClusterIP
  39. ---
  40. apiVersion: policy.karmada.io/v1alpha1
  41. kind: PropagationPolicy
  42. metadata:
  43. name: app-propagation
  44. spec:
  45. resourceSelectors:
  46. - apiVersion: apps/v1
  47. kind: Deployment
  48. name: sample-app
  49. - apiVersion: v1
  50. kind: Service
  51. name: sample-app
  52. placement:
  53. clusterAffinity:
  54. clusterNames:
  55. - member1
  56. - member2
  57. replicaScheduling:
  58. replicaDivisionPreference: Weighted
  59. replicaSchedulingType: Divided
  60. weightPreference:
  61. staticWeightList:
  62. - targetCluster:
  63. clusterNames:
  64. - member1
  65. weight: 1
  66. - targetCluster:
  67. clusterNames:
  68. - member2
  69. weight: 1

部署完成后,您可以检查 Pod 和 Service 的分发情况:

  1. $ karmadactl get pods
  2. NAME CLUSTER READY STATUS RESTARTS AGE
  3. sample-app-9b7d8c9f5-xrnfx member1 1/1 Running 0 111s
  4. $ karmadactl get svc
  5. NAME CLUSTER TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ADOPTION
  6. sample-app member1 ClusterIP 10.11.29.250 <none> 80/TCP 3m53s Y

member1member2 集群中监控您的应用

为了监控您的应用,您需要配置一个指向该应用的 ServiceMonitor。假设您已配置 Prometheus 实例来使用带有 app:sample-app 标签的 ServiceMonitor,那么请创建一个 ServiceMonitor 以通过 Service 监控应用的指标:

  1. apiVersion: monitoring.coreos.com/v1
  2. kind: ServiceMonitor
  3. metadata:
  4. name: sample-app
  5. labels:
  6. app: sample-app
  7. spec:
  8. selector:
  9. matchLabels:
  10. app: sample-app
  11. endpoints:
  12. - port: http
  1. kubectl create -f sample-app.monitor.yaml

现在,您应该可以看到自定义指标 (http_requests_total) 出现在 Prometheus 实例中。通过仪表盘找到指标,并确保其含有 Namespace 和 Pod 标签。如果没有,请检查 ServiceMonitor 中的标签与 Prometheus CRD 中的标签是否匹配。

member1member2 集群中启动您的适配器

部署了 prometheus-adapter 之后,为了暴露自定义指标,您需要更新适配器配置。

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: adapter-config
  5. namespace: monitoring
  6. data:
  7. config.yaml: |-
  8. "rules":
  9. - "seriesQuery": |
  10. {namespace!="",__name__!~"^container_.*"}
  11. "resources":
  12. "template": "<<.Resource>>"
  13. "name":
  14. "matches": "^(.*)_total"
  15. "as": ""
  16. "metricsQuery": |
  17. sum by (<<.GroupBy>>) (
  18. irate (
  19. <<.Series>>{<<.LabelMatchers>>}[1m]
  20. )
  21. )
  1. kubectl apply -f prom-adapter.config.yaml
  2. # Restart prom-adapter pods
  3. kubectl rollout restart deployment prometheus-adapter -n monitoring

member1member2 集群中注册 metrics API

您还需要向 API 聚合器(主 Kubernetes API 服务器的一部分)注册自定义指标 API。为此,您需要创建一个 APIService 资源。

  1. apiVersion: apiregistration.k8s.io/v1
  2. kind: APIService
  3. metadata:
  4. name: v1beta2.custom.metrics.k8s.io
  5. spec:
  6. group: custom.metrics.k8s.io
  7. groupPriorityMinimum: 100
  8. insecureSkipTLSVerify: true
  9. service:
  10. name: prometheus-adapter
  11. namespace: monitoring
  12. version: v1beta2
  13. versionPriority: 100
  1. kubectl create -f api-service.yaml

该 API 已注册为 custom.metrics.k8s.io/v1beta2,您可以使用以下命令进行验证:

  1. kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta2/namespaces/default/pods/*/http_requests?selector=app%3Dsample-app"

输出结果类似于:

  1. {
  2. "kind": "MetricValueList",
  3. "apiVersion": "custom.metrics.k8s.io/v1beta2",
  4. "metadata": {},
  5. "items": [
  6. {
  7. "describedObject": {
  8. "kind": "Pod",
  9. "namespace": "default",
  10. "name": "sample-app-9b7d8c9f5-9lw6b",
  11. "apiVersion": "/v1"
  12. },
  13. "metric": {
  14. "name": "http_requests",
  15. "selector": null
  16. },
  17. "timestamp": "2023-06-14T09:09:54Z",
  18. "value": "66m"
  19. }
  20. ]
  21. }

如果 karmada-metrics-adapter 安装成功,您也可以在 Karmada 控制平面中使用上述命令进行验证。

在 Karmada 控制平面部署 FederatedHPA

接下来让我们在 Karmada 控制平面中部署 FederatedHPA。

  1. apiVersion: autoscaling.karmada.io/v1alpha1
  2. kind: FederatedHPA
  3. metadata:
  4. name: sample-app
  5. spec:
  6. scaleTargetRef:
  7. apiVersion: apps/v1
  8. kind: Deployment
  9. name: sample-app
  10. minReplicas: 1
  11. maxReplicas: 10
  12. behavior:
  13. scaleDown:
  14. stabilizationWindowSeconds: 10
  15. scaleUp:
  16. stabilizationWindowSeconds: 10
  17. metrics:
  18. - type: Pods
  19. pods:
  20. metric:
  21. name: http_requests
  22. target:
  23. averageValue: 700m
  24. type: Value

部署完成后,您可以检查 FederatedHPA:

  1. NAME REFERENCE-KIND REFERENCE-NAME MINPODS MAXPODS REPLICAS AGE
  2. sample-app Deployment sample-app 1 10 1 15d

将 Service 导出到 member1 集群

正如前文所提到的,我们需要一个多集群 Service 来将请求转发到 member1member2 集群中的 Pod,因此让我们创建这个多集群 Service。

  • 在 Karmada 控制平面创建一个 ServiceExport 对象,然后创建一个 PropagationPolicyServiceExport 对象分发到 member1member2 集群。

    1. apiVersion: multicluster.x-k8s.io/v1alpha1
    2. kind: ServiceExport
    3. metadata:
    4. name: sample-app
    5. ---
    6. apiVersion: policy.karmada.io/v1alpha1
    7. kind: PropagationPolicy
    8. metadata:
    9. name: serve-export-policy
    10. spec:
    11. resourceSelectors:
    12. - apiVersion: multicluster.x-k8s.io/v1alpha1
    13. kind: ServiceExport
    14. name: sample-app
    15. placement:
    16. clusterAffinity:
    17. clusterNames:
    18. - member1
    19. - member2
  • 在 Karmada 控制平面创建一个 ServiceImport 对象,然后创建一个 PropagationPolicyServiceImport 对象分发到 member1 集群。

    1. apiVersion: multicluster.x-k8s.io/v1alpha1
    2. kind: ServiceImport
    3. metadata:
    4. name: sample-app
    5. spec:
    6. type: ClusterSetIP
    7. ports:
    8. - port: 80
    9. protocol: TCP
    10. ---
    11. apiVersion: policy.karmada.io/v1alpha1
    12. kind: PropagationPolicy
    13. metadata:
    14. name: serve-import-policy
    15. spec:
    16. resourceSelectors:
    17. - apiVersion: multicluster.x-k8s.io/v1alpha1
    18. kind: ServiceImport
    19. name: sample-app
    20. placement:
    21. clusterAffinity:
    22. clusterNames:
    23. - member1

部署完成后,您可以检查多集群 Service:

  1. $ karmadactl get svc
  2. NAME CLUSTER TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ADOPTION
  3. derived-sample-app member1 ClusterIP 10.11.59.213 <none> 80/TCP 9h Y

在 member1 集群中安装 hey http 负载测试工具

为了发送 http 请求,这里我们使用 hey

  • 下载 hey 并复制到 kind 集群容器中。
  1. wget https://hey-release.s3.us-east-2.amazonaws.com/hey_linux_amd64
  2. chmod +x hey_linux_amd64
  3. docker cp hey_linux_amd64 member1-control-plane:/usr/local/bin/hey

测试扩容

  • 首先检查 Pod 的分发情况。

    1. $ karmadactl get pods
    2. NAME CLUSTER READY STATUS RESTARTS AGE
    3. sample-app-9b7d8c9f5-xrnfx member1 1/1 Running 0 111s
  • 检查多集群 Service ip。

    1. $ karmadactl get svc
    2. NAME CLUSTER TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ADOPTION
    3. derived-sample-app member1 ClusterIP 10.11.59.213 <none> 80/TCP 20m Y
  • 使用 hey 请求多集群 Service,以提高 nginx Pod 的自定义指标(http_requests_total)。

    1. docker exec member1-control-plane hey -c 1000 -z 1m http://10.11.59.213/metrics
  • 等待 15 秒,副本将扩容,然后您可以再次检查 Pod 分发状态。

    1. $ karmadactl get po -l app=sample-app
    2. NAME CLUSTER READY STATUS RESTARTS AGE
    3. sample-app-9b7d8c9f5-454vz member2 1/1 Running 0 84s
    4. sample-app-9b7d8c9f5-7fjhn member2 1/1 Running 0 69s
    5. sample-app-9b7d8c9f5-ddf4s member2 1/1 Running 0 69s
    6. sample-app-9b7d8c9f5-mxqmh member2 1/1 Running 0 84s
    7. sample-app-9b7d8c9f5-qbc2j member2 1/1 Running 0 69s
    8. sample-app-9b7d8c9f5-2tgxt member1 1/1 Running 0 69s
    9. sample-app-9b7d8c9f5-66n9s member1 1/1 Running 0 69s
    10. sample-app-9b7d8c9f5-fbzps member1 1/1 Running 0 84s
    11. sample-app-9b7d8c9f5-ldmhz member1 1/1 Running 0 84s
    12. sample-app-9b7d8c9f5-xrnfx member1 1/1 Running 0 87m

测试缩容

1 分钟后,负载测试工具将停止运行,然后您可以看到工作负载在多个集群中缩容。

  1. $ karmadactl get pods -l app=sample-app
  2. NAME CLUSTER READY STATUS RESTARTS AGE
  3. sample-app-9b7d8c9f5-xrnfx member1 1/1 Running 0 91m