Fluid 给数据弹性一双隐形的翅膀 -- 自定义弹性伸缩
作者 |?
車漾 Fluid 社區(qū) Commiter
謝遠(yuǎn)東 Fluid 社區(qū) Commiter
來源 | 阿里巴巴云原生公眾號
**導(dǎo)讀:**彈性伸縮作為 Kubernetes 的核心能力之一,但它一直是圍繞這無狀態(tài)的應(yīng)用負(fù)載展開。而 Fluid 提供了分布式緩存的彈性伸縮能力,可以靈活擴充和收縮數(shù)據(jù)緩存。?它基于 Runtime 提供了緩存空間、現(xiàn)有緩存比例等性能指標(biāo), 結(jié)合自身對于 Runtime 資源的擴縮容能力,提供數(shù)據(jù)緩存按需伸縮能力。
背景
隨著越來越多的大數(shù)據(jù)和 AI 等數(shù)據(jù)密集應(yīng)用開始部署和運行在 Kubernetes 環(huán)境下,數(shù)據(jù)密集型應(yīng)用計算框架的設(shè)計理念和云原生靈活的應(yīng)用編排的分歧,導(dǎo)致了數(shù)據(jù)訪問和計算瓶頸。云原生數(shù)據(jù)編排引擎 Fluid 通過數(shù)據(jù)集的抽象,利用分布式緩存技術(shù),結(jié)合調(diào)度器,為應(yīng)用提供了數(shù)據(jù)訪問加速的能力。
彈性伸縮作為 Kubernetes 的核心能力之一,但它一直是圍繞這無狀態(tài)的應(yīng)用負(fù)載展開。而 Fluid 提供了分布式緩存的彈性伸縮能力,可以靈活擴充和收縮數(shù)據(jù)緩存。它基于 Runtime 提供了緩存空間、現(xiàn)有緩存比例等性能指標(biāo), 結(jié)合自身對于 Runtime 資源的擴縮容能力,提供數(shù)據(jù)緩存按需伸縮能力。
這個能力對于互聯(lián)網(wǎng)場景下大數(shù)據(jù)應(yīng)用非常重要,由于多數(shù)的大數(shù)據(jù)應(yīng)用都是通過端到端流水線來實現(xiàn)的。而這個流水線包含以下幾個步驟:
可以看到端到端的流水線會包含多種不同類型的計算任務(wù),針對每一個計算任務(wù),實踐中會有合適的專業(yè)系統(tǒng)來處理(TensorFlow,PyTorch,Spark, Presto);但是這些系統(tǒng)彼此獨立,通常要借助外部文件系統(tǒng)來實現(xiàn)把數(shù)據(jù)從一個階段傳遞到下一個階段。但是頻繁的使用文件系統(tǒng)實現(xiàn)數(shù)據(jù)交換,會帶來大量的 I/O 開銷,經(jīng)常會成為整個工作流的瓶頸。
而 Fluid 對于這個場景非常適合,用戶可以創(chuàng)建一個 Dataset 對象,這個對象有能力將數(shù)據(jù)分散緩存到 Kubernetes 計算節(jié)點中,作為數(shù)據(jù)交換的介質(zhì),這樣避免了數(shù)據(jù)的遠(yuǎn)程寫入和讀取,提升了數(shù)據(jù)使用的效率。但是這里的問題是臨時數(shù)據(jù)緩存的資源預(yù)估和預(yù)留。由于在數(shù)據(jù)生產(chǎn)消費之前,精確的數(shù)據(jù)量預(yù)估是比較難滿足,過高的預(yù)估會導(dǎo)致資源預(yù)留浪費,過低的預(yù)估會導(dǎo)致數(shù)據(jù)寫入失敗可能性增高。還是按需擴縮容對于使用者更加友好。我們希望能夠達(dá)成類似 page cache 的使用效果,對于最終用戶來說這一層是透明的但是它帶來的緩存加速效果是實實在在的。
我們通過自定義 HPA 機制,通過 Fluid 引入了緩存彈性伸縮能力。彈性伸縮的條件是當(dāng)已有緩存數(shù)據(jù)量達(dá)到一定比例時,就會觸發(fā)彈性擴容,擴容緩存空間。例如將觸發(fā)條件設(shè)置為緩存空間占比超過 75%,此時總的緩存空間為 10G,當(dāng)數(shù)據(jù)已經(jīng)占滿到 8G 緩存空間的時候,就會觸發(fā)擴容機制。
下面我們通過一個例子幫助您體驗 Fluid 的自動擴縮容能力。
前提條件
推薦使用 Kubernetes 1.18 以上,因為在 1.18 之前,HPA 是無法自定義擴縮容策略的,都是通過硬編碼實現(xiàn)的。而在 1.18 后,用戶可以自定義擴縮容策略的,比如可以定義一次擴容后的冷卻時間。
具體步驟
1. 安裝 jq 工具方便解析 json。
在本例子中我們使用操作系統(tǒng)是 centos,可以通過 yum 安裝 jq。
yum install -y jq2. 下載、安裝?Fluid 最新版。
git clone https://github.com/fluid-cloudnative/fluid.git cd fluid/charts kubectl create ns fluid-system helm install fluid fluid3. 部署或配置 Prometheus。
這里通過 Prometheus 對于 AlluxioRuntime 的緩存引擎暴露的 Metrics 進行收集,如果集群內(nèi)無 prometheus:
$ cd fluid $ kubectl apply -f integration/prometheus/prometheus.yaml如集群內(nèi)有 prometheus,可將以下配置寫到 prometheus 配置文件中:
scrape_configs:- job_name: 'alluxio runtime'metrics_path: /metrics/prometheuskubernetes_sd_configs:- role: endpointsrelabel_configs:- source_labels: [__meta_kubernetes_service_label_monitor]regex: alluxio_runtime_metricsaction: keep- source_labels: [__meta_kubernetes_endpoint_port_name]regex: webaction: keep- source_labels: [__meta_kubernetes_namespace]target_label: namespacereplacement: $1action: replace- source_labels: [__meta_kubernetes_service_label_release]target_label: fluid_runtimereplacement: $1action: replace- source_labels: [__meta_kubernetes_endpoint_address_target_name]target_label: podreplacement: $1action: replace4. 驗證 Prometheus 安裝成功。
$ kubectl get ep -n kube-system prometheus-svc NAME ENDPOINTS AGE prometheus-svc 10.76.0.2:9090 6m49s $ kubectl get svc -n kube-system prometheus-svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE prometheus-svc NodePort 172.16.135.24 <none> 9090:32114/TCP 2m7s如果希望可視化監(jiān)控指標(biāo),您可以安裝 Grafana 驗證監(jiān)控數(shù)據(jù),具體操作可以參考文檔。
5. 部署 metrics server。
檢查該集群是否包括 metrics-server,執(zhí)行kubectl top node有正確輸出可以顯示內(nèi)存和 CPU,則該集群 metrics server 配置正確。
kubectl top node NAME CPU(cores) CPU% MEMORY(bytes) MEMORY% 192.168.1.204 93m 2% 1455Mi 10% 192.168.1.205 125m 3% 1925Mi 13% 192.168.1.206 96m 2% 1689Mi 11%否則手動執(zhí)行以下命令:
kubectl create -f integration/metrics-server6. 部署 custom-metrics-api 組件。
為了基于自定義指標(biāo)進行擴展,你需要擁有兩個組件:
- 第一個組件是從應(yīng)用程序收集指標(biāo)并將其存儲到 Prometheus 時間序列數(shù)據(jù)庫。
- 第二個組件使用收集的度量指標(biāo)來擴展 Kubernetes 自定義 metrics API,即 k8s-prometheus-adapter。
第一個組件在第三步部署完成,下面部署第二個組件。
如果已經(jīng)配置了custom-metrics-api,在 adapter 的 configmap 配置中增加與 dataset 相關(guān)的配置:
apiVersion: v1 kind: ConfigMap metadata:name: adapter-confignamespace: monitoring data:config.yaml: |rules:- seriesQuery: '{__name__=~"Cluster_(CapacityTotal|CapacityUsed)",fluid_runtime!="",instance!="",job="alluxio runtime",namespace!="",pod!=""}'seriesFilters:- is: ^Cluster_(CapacityTotal|CapacityUsed)$resources:overrides:namespace:resource: namespacepod:resource: podsfluid_runtime:resource: datasetsname:matches: "^(.*)"as: "capacity_used_rate"metricsQuery: ceil(Cluster_CapacityUsed{<<.LabelMatchers>>}*100/(Cluster_CapacityTotal{<<.LabelMatchers>>}))否則手動執(zhí)行以下命令:
kubectl create -f integration/custom-metrics-api/namespace.yaml kubectl create -f integration/custom-metrics-api注意:因為 custom-metrics-api 對接集群中的 Prometheous 的訪問地址,請?zhí)鎿Q prometheous url 為你真正使用的 Prometheous 地址。
檢查自定義指標(biāo):
$ kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | jq {"kind": "APIResourceList","apiVersion": "v1","groupVersion": "custom.metrics.k8s.io/v1beta1","resources": [{"name": "pods/capacity_used_rate","singularName": "","namespaced": true,"kind": "MetricValueList","verbs": ["get"]},{"name": "datasets.data.fluid.io/capacity_used_rate","singularName": "","namespaced": true,"kind": "MetricValueList","verbs": ["get"]},{"name": "namespaces/capacity_used_rate","singularName": "","namespaced": false,"kind": "MetricValueList","verbs": ["get"]}] }7. 提交測試使用的 Dataset。
$ cat<<EOF >dataset.yaml apiVersion: data.fluid.io/v1alpha1 kind: Dataset metadata:name: spark spec:mounts:- mountPoint: https://mirrors.bit.edu.cn/apache/spark/name: spark --- apiVersion: data.fluid.io/v1alpha1 kind: AlluxioRuntime metadata:name: spark spec:replicas: 1tieredstore:levels:- mediumtype: MEMpath: /dev/shmquota: 1Gihigh: "0.99"low: "0.7"properties:alluxio.user.streaming.data.timeout: 300sec EOF $ kubectl create -f dataset.yaml dataset.data.fluid.io/spark created alluxioruntime.data.fluid.io/spark created8. 查看這個 Dataset 是否處于可用狀態(tài)。
可以看到該數(shù)據(jù)集的數(shù)據(jù)總量為 2.71GiB, 目前 Fluid 提供的緩存節(jié)點數(shù)為 1,可以提供的最大緩存能力為 1GiB。此時數(shù)據(jù)量是無法滿足全量數(shù)據(jù)緩存的需求。
$ kubectl get dataset NAME UFS TOTAL SIZE CACHED CACHE CAPACITY CACHED PERCENTAGE PHASE AGE spark 2.71GiB 0.00B 1.00GiB 0.0% Bound 7m38s9. 當(dāng)該 Dataset 處于可用狀態(tài)后,查看是否已經(jīng)可以從 custom-metrics-api 獲得監(jiān)控指標(biāo)。
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/datasets.data.fluid.io/*/capacity_used_rate" | jq {"kind": "MetricValueList","apiVersion": "custom.metrics.k8s.io/v1beta1","metadata": {"selfLink": "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/datasets.data.fluid.io/%2A/capacity_used_rate"},"items": [{"describedObject": {"kind": "Dataset","namespace": "default","name": "spark","apiVersion": "data.fluid.io/v1alpha1"},"metricName": "capacity_used_rate","timestamp": "2021-04-04T07:24:52Z","value": "0"}] }10. 創(chuàng)建 HPA 任務(wù)。
$ cat<<EOF > hpa.yaml apiVersion: autoscaling/v2beta2 kind: HorizontalPodAutoscaler metadata:name: spark spec:scaleTargetRef:apiVersion: data.fluid.io/v1alpha1kind: AlluxioRuntimename: sparkminReplicas: 1maxReplicas: 4metrics:- type: Objectobject:metric:name: capacity_used_ratedescribedObject:apiVersion: data.fluid.io/v1alpha1kind: Datasetname: sparktarget:type: Valuevalue: "90"behavior:scaleUp:policies:- type: Podsvalue: 2periodSeconds: 600scaleDown:selectPolicy: Disabled EOF首先,我們解讀一下從樣例配置,這里主要有兩部分一個是擴縮容的規(guī)則,另一個是擴縮容的靈敏度:
- 規(guī)則:觸發(fā)擴容行為的條件為 Dataset 對象的緩存數(shù)據(jù)量占總緩存能力的 90%;擴容對象為AlluxioRuntime,最小副本數(shù)為 1,最大副本數(shù)為 4;而 Dataset 和 AlluxioRuntime 的對象需要在同一個 namespace。
- 策略:可以 K8s 1.18 以上的版本,可以分別針對擴容和縮容場景設(shè)置穩(wěn)定時間和一次擴縮容步長比例。比如在本例子, 一次擴容周期為 10 分鐘(periodSeconds),擴容時新增 2 個副本數(shù),當(dāng)然這也不可以超過 maxReplicas 的限制;而完成一次擴容后,冷卻時間(stabilizationWindowSeconds)為 20 分鐘;而縮容策略可以選擇直接關(guān)閉。
11. 查看 HPA 配置, 當(dāng)前緩存空間的數(shù)據(jù)占比為 0。遠(yuǎn)遠(yuǎn)低于觸發(fā)擴容的條件。
$ kubectl get hpa NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE spark AlluxioRuntime/spark 0/90 1 4 1 33s $ kubectl describe hpa Name: spark Namespace: default Labels: <none> Annotations: <none> CreationTimestamp: Wed, 07 Apr 2021 17:36:39 +0800 Reference: AlluxioRuntime/spark Metrics: ( current / target )"capacity_used_rate" on Dataset/spark (target value): 0 / 90 Min replicas: 1 Max replicas: 4 Behavior:Scale Up:Stabilization Window: 0 secondsSelect Policy: MaxPolicies:- Type: Pods Value: 2 Period: 600 secondsScale Down:Select Policy: DisabledPolicies:- Type: Percent Value: 100 Period: 15 seconds AlluxioRuntime pods: 1 current / 1 desired Conditions:Type Status Reason Message---- ------ ------ -------AbleToScale True ScaleDownStabilized recent recommendations were higher than current one, applying the highest recent recommendationScalingActive True ValidMetricFound the HPA was able to successfully calculate a replica count from Dataset metric capacity_used_rateScalingLimited False DesiredWithinRange the desired count is within the acceptable range Events: <none>12. 創(chuàng)建數(shù)據(jù)預(yù)熱任務(wù)。
$ cat<<EOF > dataload.yaml apiVersion: data.fluid.io/v1alpha1 kind: DataLoad metadata:name: spark spec:dataset:name: sparknamespace: default EOF $ kubectl create -f dataload.yaml $ kubectl get dataload NAME DATASET PHASE AGE DURATION spark spark Executing 15s Unfinished13. 此時可以發(fā)現(xiàn)緩存的數(shù)據(jù)量接近了 Fluid 可以提供的緩存能力(1GiB)同時觸發(fā)了彈性伸縮的條件。
$ kubectl get dataset NAME UFS TOTAL SIZE CACHED CACHE CAPACITY CACHED PERCENTAGE PHASE AGE spark 2.71GiB 1020.92MiB 1.00GiB 36.8% Bound 5m15s從 HPA 的監(jiān)控,可以看到 Alluxio Runtime 的擴容已經(jīng)開始, 可以發(fā)現(xiàn)擴容的步長為 2。
$ kubectl get hpa NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE spark AlluxioRuntime/spark 100/90 1 4 2 4m20s $ kubectl describe hpa Name: spark Namespace: default Labels: <none> Annotations: <none> CreationTimestamp: Wed, 07 Apr 2021 17:56:31 +0800 Reference: AlluxioRuntime/spark Metrics: ( current / target )"capacity_used_rate" on Dataset/spark (target value): 100 / 90 Min replicas: 1 Max replicas: 4 Behavior:Scale Up:Stabilization Window: 0 secondsSelect Policy: MaxPolicies:- Type: Pods Value: 2 Period: 600 secondsScale Down:Select Policy: DisabledPolicies:- Type: Percent Value: 100 Period: 15 seconds AlluxioRuntime pods: 2 current / 3 desired Conditions:Type Status Reason Message---- ------ ------ -------AbleToScale True SucceededRescale the HPA controller was able to update the target scale to 3ScalingActive True ValidMetricFound the HPA was able to successfully calculate a replica count from Dataset metric capacity_used_rateScalingLimited False DesiredWithinRange the desired count is within the acceptable range Events:Type Reason Age From Message---- ------ ---- ---- -------Normal SuccessfulRescale 21s horizontal-pod-autoscaler New size: 2; reason: Dataset metric capacity_used_rate above targetNormal SuccessfulRescale 6s horizontal-pod-autoscaler New size: 3; reason: Dataset metric capacity_used_rate above target14. 在等待一段時間之后發(fā)現(xiàn)數(shù)據(jù)集的緩存空間由 1GiB 提升到了 3GiB,數(shù)據(jù)緩存已經(jīng)接近完成。
$ kubectl get dataset NAME UFS TOTAL SIZE CACHED CACHE CAPACITY CACHED PERCENTAGE PHASE AGE spark 2.71GiB 2.59GiB 3.00GiB 95.6% Bound 12m同時觀察 HPA 的狀態(tài),可以發(fā)現(xiàn)此時 Dataset 對應(yīng)的 runtime 的 replicas 數(shù)量為 3, 已經(jīng)使用的緩存空間比例 capacity_used_rate 為 85%,已經(jīng)不會觸發(fā)緩存擴容。
$ kubectl get hpa NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE spark AlluxioRuntime/spark 85/90 1 4 3 11m15. 清理環(huán)境。
kubectl delete hpa spark kubectl delete dataset spark總結(jié)
Fluid 提供了結(jié)合 Prometheous,Kubernetes HPA 和 Custom Metrics 能力,根據(jù)占用緩存空間的比例觸發(fā)自動彈性伸縮的能力,實現(xiàn)緩存能力的按需使用。這樣能夠幫助用戶更加靈活的使用通過分布式緩存提升數(shù)據(jù)訪問加速能力,后續(xù)我們會提供定時擴縮的能力,為擴縮容提供更強的確定性。
Fluid 的代碼倉庫:https://github.com/fluid-cloudnative/fluid.git, 歡迎大家關(guān)注、貢獻(xiàn)代碼和 star。
總結(jié)
以上是生活随笔為你收集整理的Fluid 给数据弹性一双隐形的翅膀 -- 自定义弹性伸缩的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 罗美琪和春波特的故事...
- 下一篇: 独家对话阿里云函数计算负责人不瞋:你所不