在資料密集型應用中,有效管理運算資源對於控制成本和確保效能至關重要。如同穿著多層衣物以適應不同的溫度變化,動態調整運算資源能讓系統根據工作負載波動,彈性地增減資源。這不僅避免了資源閒置造成的浪費,也能確保系統在高峰期擁有足夠的運算能力。水平擴充套件,即調整運算資源的數量,是實作動態資源管理的關鍵技術。透過監控系統負載和資源使用情況,並結合自動化策略,可以根據預設規則或條件自動調整資源,實作成本效益和效能最佳化的平衡。
動態調整運算資源以應對需求變化
如同我在規劃健行時,需要根據天氣預報、健行強度和步伐來決定該攜帶的衣物一樣,為了保持舒適和安全,我會穿著多層衣物。當我快速步行上坡時,我會脫掉一些衣物以避免過熱;而當我在山頂停下來欣賞景色時,我會再穿上它們,以保持溫暖。這種穿著多層衣物的方式,使我能夠根據不同情況進行調整。
同樣地,您也可以根據不同的工作負載,動態調整資料管道的運算資源。這被視為一種動態調整運算資源大小的過程,涉及監控和調優,以使資源組態更為精準。
水平擴充套件與垂直擴充套件
在第一章中,我們討論了垂直擴充套件的定義,即改變單一資源的容量。本章將重點介紹水平擴充套件,即改變資源的數量。增加資源稱為擴充套件,而減少資源則稱為縮減,如微軟Azure架構良好的框架中所描述的那樣。
水平擴充套件的優勢
- 靈活性:能夠根據工作負載的變化,動態地增加或減少資源。
- 成本效益:只在需要時使用資源,從而避免不必要的成本。
- 效能最佳化:透過監控和調優,能夠找到最適合特定工作負載的資源組態。
實踐水平擴充套件
要有效地進行水平擴充套件,需要考慮以下幾點:
監控:持續監控系統的效能和資源利用率,以識別何時需要擴充套件或縮減。
自動化:實施自動化策略,以便根據預設的規則或條件自動調整資源。
測試和調優:定期進行測試和調優,以確保資源組態能夠滿足不斷變化的需求。
import autoscaling
# 定義自動擴充套件策略
def autoscaling_policy(current_load):
if current_load > 0.8:
return "scale_out"
elif current_load < 0.2:
return "scale_in"
else:
return "no_change"
# 取得當前負載
current_load = get_current_load()
# 根據當前負載決定是否擴充套件或縮減
decision = autoscaling_policy(current_load)
if decision == "scale_out":
autoscaling.scale_out()
elif decision == "scale_in":
autoscaling.scale_in()
else:
print("No change needed.")
內容解密:
這段程式碼展示了一個簡單的自動擴充套件策略。首先定義了一個函式autoscaling_policy,根據當前的系統負載決定是否需要擴充套件或縮減。如果當前負載超過80%,則觸發擴充套件;如果低於20%,則觸發縮減。否則,保持現狀不變。接著,取得當前系統負載,並根據決策結果呼叫相應的擴充套件或縮減函式。
擴充套件資料管道的挑戰與機會
在處理資料管道時,擴充套件工作負載是成本與效能之間的重要權衡。我們希望在節省成本的同時,不犧牲管道的可靠性和效能。本章將重點介紹如何水平自動擴充套件資料管道,闡述何時、何地以及如何擴充套件資料工作負載。
識別擴充套件機會
在討論如何擴充套件之前,我們首先需要確定資料管道中有哪些擴充套件機會。擴充套件需要兩個主要元素:變異性和指標。
- 變異性提供了擴充套件的機會。如果沒有變異性,資源使用將是靜態的。例如,在一個炎熱的晴天徒步旅行時,我們不會帶多餘的衣物,因為我們知道整個過程中都會感到溫暖。在資料管道中,運作和工作負載的變化是擴充套件機會的來源。
- 指標用於識別何時需要擴充套件。例如,當處理大量資料批次時,記憶體使用量會增加,表明需要更多的資源。反之,當資料量減少時,CPU 使用率也會下降,提供了一個縮減資源以降低成本的機會。
資料管道中的變異性
資料管道中的變異性可以從管道運作和資料工作負載兩個方面來考慮。
- 管道運作指的是管道處理資料的頻率。
- 資料工作負載則涵蓋了資料量和複雜度的變化。
固定運作與固定工作負載
圖 2-1 展示了一個運作和工作負載都保持不變的基準案例。在資料管道中,這可能是一個持續監控溫度的串流系統。這種情況下,由於沒有變化,因此沒有擴充套件的機會。
固定工作負載與變動運作
圖 2-2 展示了工作負載保持不變,但運作頻率變動的情況。這種情況下,可以在沒有活躍任務時縮減資源以節省成本。
變動工作負載與固定運作
圖 2-3 展示了工作負載變動,但運作頻率保持不變的情況。這種情況下,可以根據工作負載的變化來擴充套件或縮減資源。
變動工作負載與變動運作
圖 2-4 展示了工作負載和運作頻率都變動的情況。這種情況下,有最大的擴充套件機會,可以根據實際需求來調整資源。
擴充套件的實際應用
在實際應用中,不同的工作負載可能會對資源有不同的需求。例如,某些階段可能需要更多的計算資源,而其他階段則不需要。因此,可以根據資料處理的不同階段來動態調整資源。
此圖示說明瞭不同型別的資料管道運作模式及其對應的擴充套件機會。
此圖示呈現了從固定運作與固定工作負載到變動運作與變動工作負載的不同場景,以及它們如何影響擴充套件機會和資源調整策略。
程式碼例項與解析
以下是一個簡單的範例,展示如何根據工作負載變化來動態調整資源:
import time
from typing import Dict
class DynamicResourceAllocator:
def __init__(self, min_resources: int, max_resources: int):
self.min_resources = min_resources
self.max_resources = max_resources
self.current_resources = min_resources
def adjust_resources(self, workload: int):
if workload > 80:
self.scale_out()
elif workload < 20:
self.scale_in()
def scale_out(self):
if self.current_resources < self.max_resources:
self.current_resources += 1
print(f"Scaled out to {self.current_resources} resources")
def scale_in(self):
if self.current_resources > self.min_resources:
self.current_resources -= 1
print(f"Scaled in to {self.current_resources} resources")
# 使用範例
allocator = DynamicResourceAllocator(min_resources=1, max_resources=10)
workloads = [10, 50, 90, 30, 5]
for workload in workloads:
print(f"Current workload: {workload}%")
allocator.adjust_resources(workload)
time.sleep(1) # 模擬時間間隔
程式碼解密:
DynamicResourceAllocator類別負責根據工作負載動態調整資源。adjust_resources方法根據當前工作負載決定是否需要擴充套件或縮減資源。scale_out和scale_in方法分別負責增加和減少資源。- 在使用範例中,我們模擬了不同的工作負載,並觀察資源如何根據工作負載變化進行調整。
這個範例展示瞭如何根據實際需求來動態調整資源,以實作成本與效能的最佳平衡。
資料管道的擴充套件機會與實踐
在進行「提升並轉移」(lift and shift)遷移時,務必要調查擴充套件(scaling)的需求,因為這類別遷移通常涉及將工作負載直接遷移到雲端,而不進行任何調整或重新設計。如果不利用雲端的彈性來降低成本,這可能會是一個非常昂貴的選擇。
此外,管道階段(pipeline stage)的資源需求可能會因任務而異,這為擴充套件提供了另一個機會。「管道擴充套件範例」(Pipeline Scaling Example)部分將探討這類別變異性的例子。
擴充套件指標
一旦確定了擴充套件的機會,下一步就是確定何時應該進行擴充套件。在自動擴充套件(autoscaling)的術語中,這對應於閾值(thresholds)和觀察視窗(observation windows)。閾值是指在決定是否應該發生擴充套件事件時與其進行比較的指標值。觀察視窗則是評估指標是否達到閾值的時間段。
例如,一個可能的自動擴充套件規則是:如果 CPU 使用率在五分鐘內超過 70%,則擴展出兩個單位。這裡的指標是 CPU 使用率,閾值是 70%,觀察視窗是五分鐘。
擴充套件指標可以像一天中的時間一樣簡單。曾經有一個資料平台僅在工作日使用,這提供了在週末降低基礎設施成本的機會。這是根據操作變異性進行擴充套件的一個例子。
程式碼範例:根據時間擴充套件
import datetime
def scale_based_on_time(current_time):
if current_time.weekday() >= 5: # 週末
return "scale_in"
else:
return "scale_out"
current_time = datetime.datetime.now()
scaling_decision = scale_based_on_time(current_time)
print(f"Scaling decision: {scaling_decision}")
內容解密:
import datetime:匯入 Python 的datetime模組,用於處理日期和時間。def scale_based_on_time(current_time)::定義一個函式,根據當前時間決定是否擴充套件。if current_time.weekday() >= 5::檢查當前時間是否為週末(週六或週日)。return "scale_in"或return "scale_out":根據是否是週末,傳回相應的擴充套件決策。current_time = datetime.datetime.now():取得當前時間。scaling_decision = scale_based_on_time(current_time):呼叫函式並取得擴充套件決策。print(f"Scaling decision: {scaling_decision}"):列印擴充套件決策。
另一個例子是根據資料工作負載變異性的擴充套件。曾經有一個串流管道,每天在特定時間段內處理最大的工作負載。管道資源根據一天中的時間進行擴充套件,在時間視窗開始時增加資源,幾小時後再減少資源。這樣將雲端成本降低了三分之二。
避免過度依賴擴充套件
不要過度依賴擴充套件,回想第一章,目標是使基準計算組態能夠適應大多數工作負載。擴充套件在時間和成本上都很昂貴。根據第一章的容量問題,這可能需要幾分鐘或更長時間。這是在執行生產工作負載時可能沒有的時間。
在「自動擴充套件範例」(Autoscaling Example)部分,你將看到一個例子,其中從一個過小的叢集組態擴充套件並沒有為一個艱難的工作提供緩解。
其他擴充套件指標
其他用於擴充套件的指標往往源於資源使用和系統操作。這提供了根據管道如何處理資料的更細微訊號,從而提供了額外的節省成本的機會。例如,如果一個記憶體密集型任務的記憶體使用率低於某個百分比,則可能表明分配的資源過多。在這種情況下,可以透過縮減規模來節省成本。
擴充套件決策流程
此圖示展示了一個簡單的擴充套件決策流程,根據 CPU 使用率決定是否擴充套件。
隨著你將在「常見自動擴充套件陷阱」(Common Autoscaling Pitfalls)部分看到,指標波動可能會觸發不想要的自動擴充套件事件。因此,與其使用恆定值,不如檢視根據平均值的指標。回到 CPU 使用率的例子,如果平均 CPU 使用率高於 70%,則可能需要擴充套件,儘管在觀察視窗期間 CPU 使用率的原始值可能會在這個值上下波動。
根據工作負載的執行方式,可以使用叢集或 Spark 指標進行擴充套件。你可以看到,在 AWS 託管的自動擴充套件中,使用了幾個根據 YARN 的指標來根據工作負載進行擴充套件。Spark 動態資源分配檢視待處理任務的數量,以確定是否需要擴充套件,並根據執行器空閒時間進行縮減。
管道擴充套件範例
回想第一章中的 HoD 批次管道,它每兩周處理一次鳥類別調查資料。資料量圖表如圖 2-5 所示,供參考。
雙周資料工作負載
此圖示展示了 HoD 批次管道的雙周資料工作負載變化,具有季節性變化特徵。
現在讓我們回顧一下可擴充套件性問題。
- 管道操作如何隨時間變化?管道操作是可變的,每兩周執行一次,或者根據需要臨時執行。
- 資料工作負載如何變化?直方圖顯示資料量具有季節性變化。
- 資料工作負載或操作的變異性如何影響資源需求?根據第一章中對該管道的基準測試結果,你知道記憶體需求根據資料工作負載具有彈性。還有每兩週一次的時間表需要考慮。大多數時候,你不需要準備好資源,只有在作業執行時才需要。
- 你如何知道資源需求正在變化?要回答這個問題,讓我們先來看看當資料在 HoD 管道中移動時,資源需求如何變化。
回想到管道內的變異性,「豐富社交資料」(Enrich with social)步驟可能比「取得資料」(Acquire data)步驟需要更多的資源,因為涉及到合併。可以先在「豐富社交資料」開始時擴充套件資源,然後在合併完成後再縮減資源。圖 2-7 說明瞭這個想法,其中「豐富社交資料」的填充背景表示比「取得資料」的白色背景需要更高的資源需求。
HoD 管道中的資源需求變化
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title 動態調整運算資源以應對需求變化
package "系統架構" {
package "前端層" {
component [使用者介面] as ui
component [API 客戶端] as client
}
package "後端層" {
component [API 服務] as api
component [業務邏輯] as logic
component [資料存取] as dao
}
package "資料層" {
database [主資料庫] as db
database [快取] as cache
}
}
ui --> client : 使用者操作
client --> api : HTTP 請求
api --> logic : 處理邏輯
logic --> dao : 資料操作
dao --> db : 持久化
dao --> cache : 快取
note right of api
RESTful API
或 GraphQL
end note
@enduml
此圖示展示了 HoD 管道中的資源需求變化,在「豐富社交資料」步驟中資源需求增加,合併完成後再縮減資源。
動態調整運算資源以應對需求變化
在處理大量資料的管線(pipeline)中,根據工作負載動態調整運算資源至關重要。本章將探討如何透過擴充套件(scaling)運算資源來提高效率並降低成本。
資源需求的變化
在 HoD 管線中,資源需求會根據處理階段和資料特徵而變化。圖 2-7 展示了不同階段的資源需求差異,其中填滿的方框表示較高的資源需求。
另一個變數是問卷資料與社群媒體資料之間的重疊程度,這將影響「Enrich with social」步驟的資源需求。圖 2-8 展示了這種情況,左側顯示問卷資料與社群媒體資料之間的關係,右側顯示「Enrich with social」步驟的資源需求。背景越暗,表示需要更多的資源來合併資料。
識別擴充套件機會
根據第一章的基準測試結果,我們知道這是一個記憶體密集型的過程。隨著資料大小和問卷-社群媒體重疊程度的增加,記憶體使用量也會增加。因此,可以透過監控記憶體使用量的持續增加來判斷是否需要更多的資源。相反,記憶體使用量的減少可能表明需要較少的資源,從而提供縮減規模以節省成本的機會。
透過擴充套件降低成本
如果為管線提供固定的資源,為了保證一定的效能和可靠性,需要為資料來源之間高度重疊的情況提供足夠的資源。如果提供的資源不足,則在合併大量資料時,可能會遇到執行時間過長或管線失敗的風險。
透過雲端運算彈性與擴充套件,可以根據工作負載比對資源,在較小的工作負載上節省成本,同時保持較大工作負載的效能和可靠性。
制定擴充套件計劃
最有效的方法是在作業啟動時分配運算資源,而不是 24-7 佈署。作業完成後,可以釋放運算資源。在 AWS EMR 等叢集運算環境中,可以在作業啟動時啟動叢集,並設定在作業完成後終止。在 Kubernetes 中,由於水平 Pod 自動擴充套件(HPA)不支援縮減至零,因此可能需要保持最小佈署規模。
計算資源需求
根據圖 2-5 的資料量和圖 2-8 的資源需求,可以確定兩種極端情況:低容量問卷資料且無重疊,以及高容量問卷資料且高度重疊。對這兩種情況進行基準測試,可以為最小和最大資源需求提供起始點。
假設基準測試結果表明,低容量無重疊情況需要 3 個工作節點,而高容量高度重疊情況需要 10 個工作節點。這為最小和最大資源需求提供了起始點。與第一章的權衡調整(right-sizing)類別似,調整擴充套件邊界是一個迭代過程。在開始時,擴大最小和最大邊界,以確保不會因資源不足而犧牲效能和可靠性。監控資源利用率和管線效能,以判斷是否可以縮減這些值。
為擴充套件而設計
要充分利用運算彈性,需要設計管線以支援擴充套件。使用分散式資料處理引擎(如 Spark、Presto 和 Dask)是支援水平擴充套件的關鍵設計考慮。這些引擎能夠將計算分散到可用資源上。相反,單機資料處理方法(如 Pandas)無法水平擴充套件。
此外,資料檔案的結構和資料轉換程式碼的寫法也會影響可擴充套件性。確保資料可以被分割,將有助於透過分割來分散處理。在轉換資料時,需要注意寬相依性(wide dependencies)的影響,這可能會導致資料洗牌(shuffling)。資料分割可以幫助最小化洗牌,在進行具有寬相依性的轉換之前過濾資料集也可以達到同樣的效果。Spark v3.2.0 及更高版本包含自適應查詢執行(Adaptive Query Execution),可以透過重新最佳化查詢計劃來緩解一些與洗牌相關的效能問題。
另一個需要考慮的是,過度擴充套件可能會損害效能。正如第一章所述,具有洗牌的 Spark 工作負載如果分散在太多工作節點上,可能會導致效能不佳。