Fluid 攜手 Vineyard,打造 Kubernetes 上的高效中間數據管理

作者:曹野 車漾

背景介紹和麪臨的挑戰

隨着 Kubernetes 在 AI/大數據領域的普及和業務場景變得越來越複雜,數據科學家在研發效率和運行效率上遇到了新的挑戰。當下的應用,往往需要使用端到端的流水線來實現,以下圖所示的一個風控作業數據操作流爲例:首先,需要從數據庫中導出訂單相關數據;隨後,圖計算引擎會處理這些原始數據,構建“用戶-商品”關係圖,並通過圖算法,初篩出其中隱藏的潛在作弊團伙;接下來,機器學習算法會對這些潛在團伙進行作弊歸因,篩選出更準確的結果;最後這些結果會經過人工篩查,並最終做出業務處理。

在這樣的場景下,我們常常會遇到如下問題:

1. 開發環境和生產環境的差異導致數據工作流的開發和調試變得複雜且低效: 數據科學家在自己的計算機上開發時通常使用 Python,但是又需要在生產環境中將代碼轉化爲他們並不熟悉的 YAML 文件從而利用 Argo、Tekton 等基於 Kubernetes 的工作流引擎,這大大降低了開發和部署效率。

2. 需要引入分佈式存儲實現中間臨時數據交換,帶來額外的開發、費用、運維成本: 端到端任務的子任務之間的數據交換通常依賴分佈式文件系統或對象存儲系統(如 HDFS、S3 和 OSS),這使得整個工作流需要進行大量的數據格式轉換和適配工作,導致冗餘的 I/O 操作,並由於中間數據的短期性,使用分佈式存儲系統會導致額外的成本。

3. 在大規模 Kubernetes 集羣環境中數據處理的效率問題: 在大規模的 Kubernetes 集羣中,使用現有的分佈式文件系統處理數據時,由於調度系統對數據的讀寫本地性缺乏足夠的理解,並未有效地考慮到數據的位置問題,沒有充分利用數據的局部性,導致在處理節點間的數據交換時,無法避免大量的數據重複拉取操作。這種操作既增加了 I/O 消耗,也降低了整體的運行效率。

爲了解決上述問題,我們提出了結合 Vineyard 數據共享能力和 Fluid 數據編排能力的解決方案:

  • Fluid 的 Python SDK 能夠方便地對數據流進行編排,爲熟悉 Python 的數據科學家提供了一種簡單的方式來構建和提交以數據集操作爲中心的工作流。此外,Fluid 支持在開發環境和雲上生產環境通過一套代碼進行數據流管理。
  • Vineyard 使端到端工作流中任務之間的數據共享更加高效, 通過內存映射的方式實現零拷貝數據共享,從而避免了額外的 I/O 開銷。
  • 通過利用 Fluid 的數據親和性調度能力,在 Pod 調度策略考慮數據寫入節點的信息,從而減小數據遷移引入的網絡開銷,提升端到端性能。

解決方案

什麼是 Fluid

Fluid 是一個開源的 Kubernetes 原生的分佈式數據集編排和加速引擎,主要服務於雲原生場景下的數據密集型應用。通過 Kubernetes 服務提供的數據層抽象,可以讓數據像流體一樣在諸如 HDFS、OSS、Ceph 等存儲源和 Kubernetes 上層雲原生應用計算之間靈活高效地移動、複製、驅逐、轉換和管理。

而具體數據操作對用戶透明,用戶不必再擔心訪問遠端數據的效率、管理數據源的便捷性,以及如何幫助 Kuberntes 做出運維調度決策等問題。用戶只需以最自然的 Kubernetes 原生數據卷方式直接訪問抽象出來的數據,剩餘任務和底層細節全部交給 Fluid 處理。

Fluid 項目架構如下圖所示,當前主要關注數據集編排和應用編排這兩個重要場景。數據集編排可以將指定數據集的數據緩存到指定特性的 Kubernetes 節點,而應用編排將指定該應用調度到可以或已經存儲了指定數據集的節點上。這兩者還可以組合形成協同編排場景,即協同考慮數據集和應用需求進行節點資源調度。

接着介紹 Fluid 中數據集 Dataset 的概念,數據集是邏輯上相關的一組數據的集合,會被運算引擎使用,比如大數據的 Spark、AI 場景的 TensorFlow。Dataset 的管理實際上也有多個維度,比如安全性、版本管理和數據加速。

Fluid 希望從數據加速出發,對數據集的管理提供支持。在 Dataset 上面, 通過定義 Runtime 這樣一個執行引擎來實現數據集安全性、版本管理和數據加速等能力。Runtime 定義了一系列生命週期的接口,可以通過實現這些接口來支持數據集的管理和加速。Fluid 的目標是爲 AI 與大數據雲原生應用提供一層高效便捷的數據抽象,將數據從存儲抽象出來從而達到如下功能:

  • 通過數據親和性調度和分佈式緩存引擎加速,實現數據和計算之間的融合,從而加速計算對數據的訪問。
  • 將數據獨立於存儲進行管理,並且通過 Kubernetes 的命名空間進行資源隔離,實現數據的安全隔離。
  • 將來自不同存儲的數據聯合起來進行運算,從而有機會打破不同存儲的差異性帶來的數據孤島效應。

什麼是Vineyard Runtime

首先我們來介紹 Vineyard,一個專爲雲原生環境大數據分析工作流中不同任務之間高效共享中間結果而設計的數據管理引擎。通過共享內存實現不同計算引擎之間中間數據共享的零拷貝,避免數據存入外部存儲(本地磁盤、S3 及 OSS 等)的開銷,從而提高了數據處理的效率和速度。

Vineyard Runtime 是 Fluid 中 Vineyard 相關組件的抽象,整體架構如下圖所示。

從圖中可以看到,Vineyard Runtime 由兩個核心組件 Master 和 Worker 構成,它們共同管理 Vineyard  中構成每個對象的 metadata 和 payload:

  • Master:使用 etcd 作爲元數據管理系統,承擔 metadata 的存儲功能。
  • Worker:使用 vineyardd(Vineyard 的守護進程) 管理共享內存,負責存儲 payload 數據。

在性能方面,當應用任務與 vineyardd 位於同一節點上,它們通過內部進程通信(IPC)進行高速數據傳輸;而在不同節點之間,應用任務與 vineyardd 通過遠程過程調用(RPC)傳輸數據,速率相對較慢。

Vineyard Runtime 的優勢

  • 性能卓越: 避免不必要的內存拷貝、序列化/反序列化、文件 I/O 和網絡傳輸等。同節點內應用與數據交互實現瞬時讀取(大約 0.1 秒),寫入僅需秒級時間;跨節點操作時,數據傳輸速度可接近網絡帶寬上限。
  • 簡單易用: 通過對分佈式對象進行數據抽象,用戶只需要使用簡單的 put 和 get 操作就能在 Vineyard 中進行數據存取。同時支持包括 C++、Python、Java、Rust 和 Golang 在內的多語言 SDK 。
  • 集成 Python 生態: 集成了常用的 Python 對象,例如 numpy.ndarray 、pandas.DataFrame、pyarrow.{Array,RecordBatch,Table}、pytorch.Dataset 和 pytorch.Module 等。

Vineyard Runtime 性能怎麼樣

在數據量大小爲 22GB 的情況下,使用 Vineyard Runtime 的同節點和跨節點性能分別如下圖所示。

當用戶任務被調度到 vineyardd 所在節點上時,此時達到 Vineyard 的最佳性能,即通過 IPC 實現數據傳輸。由於 Vineyard 會預先分配內存,數據寫入到 Vineyard 比對象存儲作爲中間介質快 68 倍左右;由於和 Vineyard 共享同一塊內存,用戶任務在讀取 Vineyard 數據時,數據將通過零拷貝的形式被傳遞到用戶任務中,實現瞬時讀取。

當用戶任務被調度非 vineyardd 所在節點時,此時達到 Vineyard 的最差性能,即通過 RPC 進行數據傳輸。相比於 OSS,由於 Vineyard 進行 RPC 傳輸時基本能打滿帶寬,數據寫入到 Vineyard 比 OSS 快 2.3 倍左右,從 Vineyard 讀取數據比 OSS 快 2.2 倍左右。

Fluid+Vineyard 實戰

在本教程中,我們將演示如何使用 Vineyard Runtime 在 ACK(Alibaba Cloud Kubernetes)上訓練一個線性迴歸模型,請按照以下步驟進行操作。

步驟一:在 ACK 集羣中安裝 Fluid

選擇 1:安裝 ack-fluid,安裝步驟可參考雲原生 AI 套件文檔 [ 1]

選擇 2:使用開源版,我們使用 Kubectl [ 2] 創建一個名字爲 fluid-system 的 namespace,然後使用 Helm [ 3] 來安裝 Fluid,僅需通過以下幾個命令來完成這個過程。

# 創建 fluid-system namespace
$ kubectl create ns fluid-system

# 將 Fluid 存儲庫添加到 Helm 存儲庫
$ helm repo add fluid https://fluid-cloudnative.github.io/charts
# 獲取最新的 Fluid 存儲庫
$ helm repo update
# 找到 Fluid 存儲庫中的開發版本
$ helm search repo fluid --devel
# 在 ACK 上部署相應版本的 Fluid chart
$ helm install fluid fluid/fluid --devel

當我們在 ACK 上部署好 Fluid 平臺後,接下來需要執行以下命令來安裝 Fluid Python SDK。

$ pip install git+https://github.com/fluid-cloudnative/fluid-client-python.git

步驟二:開啓數據與任務的協同調度(可選)

在雲環境中,端到端數據操作流水線經常包含多個子任務。當這些任務被 Kubernetes 調度時,系統僅考慮了所需的資源約束,而無法保證連續執行的兩個任務能在同一個節點運行。這導致二者在使用 Vineyard 共享中間結果時,會因爲數據遷移引入額外的網絡開銷。如果希望將任務和 Vineyard 調度到同一節點達到最佳性能,可以按照修改如下 configmap 配置開啓 FUSE 親和性調度,這樣系統調度將優先讓相關聯的任務在同一個節點進行內存訪問,以減少數據遷移產生的網絡開銷。

# 按照以下命令修改 webhook-plugins 的配置,並開啓 fuse 親和性調度,
$ kubectl edit configmap webhook-plugins -n fluid-system
data:
  pluginsProfile: |
    pluginConfig:
    - args: |
        preferred:
          # 開啓 fuse 親和性調度
          - name: fluid.io/fuse
            weight: 100
    ...

# 重啓 fluid-webhook pod
$ kubectl delete pod -lcontrol-plane=fluid-webhook -n fluid-system

步驟三:構建和部署線性迴歸數據操作流水線

該步驟包括數據預處理、模型訓練和模型測試階段,完整代碼可參考示例代碼 [ 4] 。該步驟主要步驟如下:

  1. 創建 Fluid 客戶端:代碼使用默認的 kubeconfig 文件連接到 Fluid 控制平臺,並創建 Fluid 客戶端實例。
import fluid

# 使用默認 kubeconfig 文件連接到 Fluid 控制平臺,並創建 Fluid 客戶端實例
fluid_client = fluid.FluidClient(fluid.ClientConfig())
  1. 創建和配置 Vineyard 數據集與運行時環境:腳本創建名爲 vineyard 的數據集並初始化相關配置,包括副本數和內存大小,使數據集綁定到運行時環境。
# 在 default namespace 下創建名爲 vineyard 的數據集
fluid_client.create_dataset(dataset_name="vineyard")

# 獲取 vineyard 數據集實例
dataset = fluid_client.get_dataset(dataset_name="vineyard")

# 初始化 vineyard runtime 的配置,並將 vineyard 數據集實例綁定到該 runtime。
# 副本數爲 2,內存分別爲 30Gi
dataset.bind_runtime(
    runtime_type=constants.VINEYARD_RUNTIME_KIND,
    replicas=2,
    cache_capacity_GiB=30,
    cache_medium="MEM",
    wait=True
)
  1. 定義數據預處理、模型訓練和模型評估函數:分別實現了用於執行數據清洗和劃分數據集的預處理函數,訓練線性迴歸模型的訓練函數,以及用於評估模型性能的測試函數。
# 定義數據預處理函數
def preprocess():
  ...
  # 將訓練數據和測試數據存入 vineyard
  import vineyard
  vineyard.put(X_train, name="x_train", persist=True)
  vineyard.put(X_test, name="x_test", persist=True)
  vineyard.put(y_train, name="y_train", persist=True)
  vineyard.put(y_test, name="y_test", persist=True)
  ...

# 定義模型訓練函數
def train():
  ...
  # 從 vineyard 中讀取訓練數據
  import vineyard
  x_test_data = vineyard.get(name="x_test", fetch=True)
  y_test_data = vineyard.get(name="y_test", fetch=True)
  ...


# 定義模型測試函數
def test():
  ...
  # 從 vineyard 中讀取測試數據
  import vineyard
  x_train_data = vineyard.get(name="x_train", fetch=True)
  y_train_data = vineyard.get(name="y_train", fetch=True)
  ...
  1. 創建任務模版並定義任務工作流:利用先前定義的函數,創建了數據預處理、模型訓練和模型測試的任務模板。這些任務按順序執行以形成完整的工作流。
preprocess_processor = create_processor(preprocess)
train_processor = create_processor(train)
test_processor = create_processor(test)

# 創建線性迴歸模型的任務工作流:數據預處理 -> 模型訓練 -> 模型測試
# 下列的掛載路徑 "/var/run/vineyard" 是 vineyard 配置文件的默認路徑
flow = dataset.process(processor=preprocess_processor, dataset_mountpath="/var/run/vineyard") \
              .process(processor=train_processor, dataset_mountpath="/var/run/vineyard") \
              .process(processor=test_processor, dataset_mountpath="/var/run/vineyard")
  1. 提交併執行任務工作流:將工作流提交到 Fluid 平臺進行執行,並等待任務完成。
# 將線性迴歸模型的任務工作流提交到 fluid 平臺,並開始執行
run = flow.run(run_id="linear-regression-with-vineyard")
run.wait()
  1. 資源清理:執行完畢後,清理在 Fluid 平臺上創建的所有資源。
# 清理所有資源
dataset.clean_up(wait=True)

通過以上一套 Python 代碼可以幫助數據科學家在本地開發通過 kind 環境 [ 5] 進行調試,把阿里雲 ACK 作爲生產環境進行使用,在提升了開發效率的同時得到非常好的運行性能。

總結與展望

通過結合 Fluid 的數據編排和 Vineyard 的高效數據共享機制,可以解決 Kubernetes 中數據工作流中的開發效率低下、中間數據存儲成本高昂和運行效率不足的問題。點擊此處即可獲得實戰源代碼。

未來,我們計劃支持兩個主要場景,一個是 AIGC 的模型加速,通過避免 FUSE 開銷和存儲對象格式轉換將模型加載性能進一步提升;另一個是支持 Serverless 場景,爲 Serverless 容器提供高效的原生數據管理。

參考資料:

[1] 雲原生 AI 套件文檔

https://help.aliyun.com/zh/ack/cloud-native-ai-suite/user-guide/deploy-the-cloud-native-ai-suite?spm=a2c4g.11186623.0.i14#task-2038811

[2] Kubectl

https://github.com/kubernetes/kubectl

[3] Helm

https://github.com/helm/helm

[4] 示例代碼

https://v6d.io/tutorials/kubernetes/vineyard-on-fluid.html

[5] kind 環境

https://kind.sigs.k8s.io/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章