集群資源調度系統設計架構總結

之前為完成《AWS 下 Kylin 調度系統的設計》,閱讀了大量 集群資源管理和任務調度的資料和論文。了解了如 Hadoop YARN、 Mesos、 Spark Drizzle、 Borg/Kubernetes 和Omega 等系統的調度器設計架構,在這篇文章里我將試圖從這些架構案例中總結出此類系統一般的設計模式。

調度器的定義

無論是在單機系統還是分散式系統當中,調度器其實都是非常核心和普遍的組件,其內涵也比較寬廣和模糊。

一般來說,下面提到的幾種類型的模塊都可以認為是調度器:

  1. 早期計算機系統當中的批處理調度系統
  2. 現代計算機系統當中的搶佔式進程調度系統和內存分配系統
  3. 某些系統或程序提供或實現的,定時激發某些類型操作的工具(如 crontab、Quartz 等)
  4. 某些編程語言的 Runtime 提供的線程/纖程/協程調度器(如 Golang 內置的 Goroutine 調度器)
  5. 分散式系統當中的任務關係管理和調度執行系統,(如 Hadoop YARN, Airflow 等)
  6. 分散式系統當中的資源管理和調度系統(如 Mesos、Borg、Kubernetes 的調度器等)

可以被稱為調度器的工具涵蓋的範圍非常廣,他們有的提供定時激發任務的能力,有的提供資源管理的能力, 有的負責維護任務的依賴關係和執行順序。甚至有的系統還集成了任務監控和各種指標度量的工具。

這篇文章主要涉及的是管理系統資源和調度任務執行相關方面的架構和模型, 具體的資源分配策略和任務調度策略不在我們討論的範圍內。

調度器設計概述

在系統設計領域研究比較多的朋友可以容易地得出一個結論,那就是我們的系統設計——無論是小到一個嵌入式的系統, 還是大到好幾百個機器的集群,在設計抽象上都是在不同的層次上重複自己。比如說,如果我們著眼於一個 CPU, 他包括計算單元和一系列的用來加速數據訪問的緩存 L1、L2、L3 等,每種緩存具有不同的訪問速度。 當我們的視野擴大到整個機器,CPU 又可以被當成一個單元,我們又有內存和硬碟兩個層次的儲存系統用於加速數據載入。 而在分散式系統中,如果我們把 HDFS 或 S3 看作硬碟,也存在像 Alluxio 這樣發揮著類似內存作用的系統。

既然系統在設計上的基本原則都是類似的,那為什麼大規模分散式系統的設計這麼困難呢? 這是因為當問題的規模變化了,原先不顯著或者容易解決的問題可能會變的難以解決。舉例來說, 當我們談論起進程間通信或者同一個 CPU 不同內核之間的通信時,我們往往不考慮通訊不穩定所帶來的問題: 我們無法想像如果一個 CPU 內核無法發送消息到另一個內核的狀況。 然而在通過網路通訊的多機機群當中這是無法迴避的問題,Paxos、Raft、Zab 等演算法被設計出來的原因也在於此。

我們先看一看單機操作系統調度器的發展路線:

  1. 最早的調度器是批處理調度器,這種調度器批量調度和執行任務,通過對計算機資源的分時復用來增加資源的利用率。 一般具有較高的吞吐量
  2. 某些與外界交互次數頻繁的系統對響應時間具有較強的要求,因此發展出了實時操作系統。 實時操作系統的調度器具備低延遲相應外部信號的能力
  3. 我們常用的操作系統基本上以批處理的方式調度任務,又通過中斷等機制提供實時性的保證, 通過提供靈活的調度策略,在吞吐量和延遲時間當中獲得平衡

在分散式系統中的調度器的設計也是相同的。從單機調度器這裡我們首先可以總結出調度器設計的三個最基本的需求:

  1. 資源的有效利用
  2. 信號的實時響應
  3. 調度策略的靈活配置

這三個需求在某種程度上來說是相互矛盾的,在面對不同需求的時候需要做出不一樣的取捨。

在上述三個需求的基礎上,分散式的調度器設計還需要克服很多其他的困難。這些困難往往在單機系統當中並不顯著。 比如說:

  1. 狀態的同步問題。在單機系統中,我們一般使用常規的同步方法,如共享內存和鎖機制,就可以很好地保證任務的協調運行了。 這是因為在單機系統上的狀態同步比較穩定和容易。然而在分散式系統中,因為網路通訊的不確定性, 使機群中的各個機器對於周圍的狀態達成一致是非常困難的任務。實際上,在分散式系統中甚至無法通過網路精確同步所有機器的時間!
  2. 容錯性問題。由於單機系統的處理能力有限,我們運行任務的規模和同時運行任務的數量都比較有限。 出錯的概率和成本都比較低。但是在分散式系統中,由於任務規模變大、任務依賴關係變得更加複雜, 出錯的概率大大增加,錯誤恢復的成本可能也比較高,因此可能需要調度器快速地識別錯誤並進行恢復操作。
  3. 可擴展性的問題。當分散式系統的規模到達一定程度,調度器的可擴展性就可能會成為瓶頸。為了提供高可擴展性, 調度器不但要可以應對管理上千台機器的挑戰,也要能夠處理動態增減節點這樣的問題。

現階段比較流行的分散式調度器可以歸納為三種類型,在接下來的文章里將會結合具體的案例進行介紹。

集中式調度器

集中式(Centralized)調度器也可以被稱為宏(Monolithic)調度器。指的是使用中心化的方式管理資源和調度任務。 也就是說,調度器本身在系統中只存在單個實例,所有的資源請求和任務調度都通過這一個實例進行。 下圖展示了集中式調度器的一般模型。可以看到,在這一模型中,資源的使用狀態和任務的執行狀態都由中央調度器管理。

Centralized Scheduler

按照上面的思路,可以列出集中式調度器在各個方面的表現狀況:

  1. 適合批處理任務和吞吐量較大、運行時間較長的任務
  2. 調度演算法只能全部內置在核心調度器當中,靈活性和策略的可擴展性不高
  3. 狀態同步比較容易且穩定,這是因為資源使用和任務執行的狀態被統一管理,降低了狀態同步和並發控制的難度
  4. 由於存在單點故障的可能性,集中式調度器的容錯性一般,有些系統通過熱備份 Master 的方式提高可用性
  5. 由於所有的資源和任務請求都要由中央調度器處理,集中式調度器的可擴展性較差,容易成為分散式系統吞吐量的瓶頸

儘管應用場合比較局限,集中式調度器仍然是普遍使用的調度器,可以被廣泛應用於中小規模的數據平台應用。

集中式調度器案例1: 單機操作系統的調度器

單機操作系統,如 Windows、Linux 和 macOS 的進程調度器是典型的集中式調度器。 當用戶請求執行應用之後,由操作系統將進程載入內存。計算機硬體的所有資源,包括 CPU、內存和硬碟等都由操作系統集中式管理。 當進程需要時,通過系統調用請求操作系統分配資源。如果單機環境中的進程使用了系統級多線程, 這些線程的調度也由系統一併控制。

集中式調度器案例2: Hadoop YARN

對於集中式調度器,我們重點介紹 Hadoop YARN 這個案例。下圖是將集中式調度器的一般模型替換成 YARN 當中術語之後的示意圖:

Hadoop YARN

Hadoop YARN 的特點可以總結為:

  1. 集中式的資源管理和調度器被稱為 ResourceManager,所有的資源的空閑和使用情況都由 ResourceManager 管理, ResourceManager 也負責監控任務的執行
  2. 集群中的每個節點都運行著一個 NodeManager,這個 NodeManager 管理本地的資源佔用和任務執行並將這些狀態同步給 ResourceManager
  3. 集群中的任務運行在 Container 當中,YARN 使用 Container 作為資源分配的抽象單位,每個 Container 會被分配一些本地資源和運算資源等

熟悉 YARN 的朋友可能知道 YARN 存在一個 ApplicationMaster 的概念,當一個應用被啟動之後, 一個 ApplicationMaster 會先在集群中被啟動,隨後 ApplicationMaster 會向 ResourceMaster 申請新的資源並調度新的任務。這一模型好像看起來和後面介紹的雙層調度器特別是 Mesos 的設計有點相似, 但一般仍認為 YARN 是 Monolithic 設計的調度器。這主要是因為:

  1. ApplicationMaster 其實也是運行在一個 Container 里的 YARN job
  2. ApplicationMaster 雖然決定 Job 如何被激發,但是仍然需要請求 ResourceMaster 申請資源和啟動新的 Job
  3. ApplicationMaster 啟動的 Job 也會由 ResourceMaster 進行監控,其啟動所需的本地資源和運算資源都由 ResourceMaster 負責分配並通知 NodeManager 具體執行

在 YARN 中,為了防止 ResourceManager 出錯退出,可以設計多個 Stand-By Master, Stand-By Master 一直處於運行狀態並和 ResourceManager 註冊在同一個 ZooKeeper 集群中。

Active 的 ResourceManager 會定期保存自己的狀態到 ZooKeeper,當其失敗退出後, 一個 Stand-By Master 會被選舉出來成為新的 Manager。

Hadoop YARN: High Availability

作為一個分散式資源管理和調度器, YARN 與其競爭對手相比功能其實比較薄弱。 譬如默認情況下 YARN 只能對 Memory 資源施加限制(如果一個 Job 使用了超過許可的 Memory, YARN 會直接殺死進程)。儘管其調度介面提供了對 CPU Cores 的抽象, 但 YARN 默認情況下對任務使用 CPU 核數並沒有任何限制。

不過若運行在 Linux 環境下, 在較新版本的 YARN 中可以配置 cgroup 限制資源使用。

雙層調度器

前面提到,集中式調度器的主要缺點在於單點模型容錯性和可擴展性較差,容易成為性能瓶頸。在一般的數據密集型應用當中, 解決這一問題的主要方法是分區。下圖是雙層調度器的一般模型:

2-level Scheduler

在雙層調度器當中,資源的使用狀態同時由分區調度器和中央調度器管理,但是中央調度器一般只負責宏觀的大規模的資源分配, 因此業務壓力較小。分區調度器負責管理自己分區的所有資源和任務,一般只有當所在分區資源無法滿足需求時, 才將任務冒泡到中央調度器處理。

相比集中式調度器,雙層調度器某一分區內的資源分配和工作安排可以由具體的任務本身進行定製, 因此大大增強了使用的靈活性,可以同時對高吞吐和低延遲的兩種場景提供良好的支持。每個分區可以獨立運行, 降低了單點故障導致系統崩潰的概率,增加了可用性和可擴展性。但是反過來也導致狀態同步和維護變得比較困難。

儘管主要思路是一致的,但雙層調度器在實現上的變種比較豐富,本文接下來使用案例進行介紹。

雙層調度器案例1: 協程調度器

單機操作系統的單個進程為了避免系統級多線程上下文切換的成本,可以自行實現進程內的調度器,如 Golang 運行時的 Goroutine 調度器。在這一模型下,一個進程內部的資源就相當於一個分區,分區內的資源由運行時提供的調度器預先申請並自行管理。 運行時環境只有當資源耗盡時才會向系統請求新的資源,從而避免頻繁的系統調用。

提出這個例子的主要目的在於說明類似的優化思路其實也被應用於分散式系統,再次證明了系統設計分層重複的特點

雙層調度器案例2: Mesos

Mesos 是和 YARN 幾乎同一時間發展起來的任務和資源調度系統。這一調度系統實現了完整的資源調度功能, 並使用 Linux Container 技術對資源的使用進行限制。和 YARN 一樣,Mesos 系統也包括一個獨立的 Mesos Master 和運行在每個節點上的 Mesos Agent,而後者會管理節點上的資源和任務並將狀態同步給 Master。 在 Mesos 里,任務運行在 Executor 里。下圖是 Mesos 的主要架構

Mesos Scheduler

值得注意的是,Mesos 分區的單位並不是單個節點,是可以將一個節點當中的資源劃分到多個區的。 也就是說,在 Mesos 里,分區是邏輯的和動態的。

把 Mesos 看作一種雙層的資源調度系統設計主要基於以下幾點:

  1. 與一般通過 Master 請求資源不同,Mesos 提出了 Framework 的概念,每個 Framework 相當於一個獨立的調度器, 可以實現自己的調度策略
  2. Master 掌握對整個集群資源的的狀態,通過 Offer (而不是被動請求) 的方式通知每個 Framework 可用的資源有哪些
  3. Framework 根據自己的需求決定要不要佔有 Master Offer 的資源,如果佔有了資源,這些資源接下來將完全由 Framework 管理
  4. Framework 通過靈活分配自己佔有的資源調度任務並執行,並不需要通過 Master 完成這一任務

同樣,Mesos 也可以通過 Stand-By Master 的方法提供 Master 節點的高可用性。 Mesos 已經被廣泛應用於各類集群的管理,但是其 Offer-Accept 的資源申請可能不是特別容易理解。 對於想要自行編寫調度策略的人,Frameworks 的抽象比較並不容易掌握。由於 Framework 要先佔有了資源才能使用, 設計不夠良好的 Framework 可能會導致資源浪費和資源競爭/死鎖。

雙層調度器案例3: Spark 和 Spark Drizzle

Spark 為了調度和執行自己基於 DAG 模型的計算,自己實現了一個集中式的調度器, 這個調度器的 Master 被稱為 Driver,當 Driver 運行起來的之後,會向上層的 Scheduler 申請資源調度起 Executor 進程。Executor 將會一直保持待機,等候 Driver 分配任務並執行,直到任務結束為止。

Spark 傳統的調度模型

Spark 和 YARN 這樣的集中式調度器放在一起可以認為是通過迂迴的方式實現了雙層調度器。 就好像單機進程自己實現協程調度器一樣,Spark Driver 預先申請的資源可以認為是在申請分區資源, 申請到的資源將由 Driver 自行管理和使用。

有趣的是,在 2017 年的 SOSP 上,Spark 為了解決流處理計算當中調度延遲較大的問題, 提出了一種新的調度模型 Drizzle,在原來調度模型的基礎上,又再次實現了雙層調度。 下圖是 Spark Drizzle 的設計模型

Spark Drizzle 的調度模型

Drizzle 使得調度 Spark Streaming 任務的延遲由最低 500ms 降低到 200ms 左右,讓 Spark Streaming 在低延遲處理的問題上獲得了突破性的進展。要搞清楚 Drizzle 提出的目的和解決的問題, 首先要理解以下幾點:

  1. Spark Streaming 的實現方式實際上是 Micro Batch,也就是說流式輸入的數據在這裡仍然被切分成一個一個 Batch 進行處理
  2. 傳統的 Spark 調度器會在前序任務完成之後,根據之前任務輸出的規模和分布,通過一定的演算法有策略地調度新的任務, 以便於獲得更好的處理速度和降低資源浪費
  3. 在這一過程中,Exector 需要在前續任務完成後通知 Scheduler,之後由 Scheduler 調度新的任務。 在傳統 Batch 處理模式下,這種模型效果很好,但是在 Streaming 的場景下存在很多問題
  4. 在 Streaming 場景下,每個 Batch 的數據量較小,因此任務可能會需要頻繁與 Scheduler 交互, 因為存在這一交互過程的 Overhead,Streaming 處理的過程中最低的延遲也要 500ms 以上

為了得到更低的延遲性且保留 Micro Batch 容錯性強且易於執行 Checkpoint 的優點,Drizzle 在原來的模型上做了一些優化:

  1. 在每個節點運行一個 LocalScheduler
  2. 中央調度器 Driver 在執行 Streaming 處理任務時,根據計算的 DAG 圖模型,預先調度某一個 Job 的後序 Job,後序 Job 會被放置在 LocalScheduler 上
  3. 後序 Job 默認在 LocalScheduler 上是沉睡狀態,但是前面的 Job 可以知道後序 Job 在哪個節點上, 因此當前面的任務完成後,可以直接激活後序任務
  4. 當後序任務被激活之後,前序任務和後序任務可以直接通過網路請求串流結果

可以看到 Drizzle 的主要思路就是根據用戶程序生成的圖模型,預先 Schedule 一些任務, 使得前序任務知道後序任務的位置,在調度時避免再請求中央調度器 Driver。 同時 Drizzle 也採取了其他一些方法,比如將多個 Micro Batch 打包在一起,藉由 LocalScheduler 自行本地調度等等方式減少延遲。

Drizzle 模型可以說是雙層模型的又一種另類體現。然而這種模型主要的缺點是必須要預先知道計算任務的圖模型和依賴關係, 否則就無法發揮作用。

共享狀態調度器

通過前面兩種模型的介紹,可以發現集群中需要管理的狀態主要包括以下兩種:

  1. 系統中資源分配和使用的狀態
  2. 系統中任務調度和執行的狀態

在集中式調度器里,這兩個狀態都由中心調度器管理,並且一併集成了調度等功能。 雙層調度器模式里,這兩個狀態分別由中央調度器和次級調度器管理。 集中式調度器可以容易地保證全局狀態的一致性但是可擴展性不夠, 雙層調度器對共享狀態的管理較難達到好的一致性保證,也不容易檢測資源競爭和死鎖。

為了解決這些問題,一種新的調度器架構被設計出來。 這種架構基本上沿襲了集中式調度器的模式,通過將中央調度器肢解為多個服務以提供更好的伸縮性。 這種調度器的核心是共享的集群狀態,因此可以被稱為共享狀態調度器

共享狀態調度器

共享狀態調度架構為了提供高可用性和可擴展性,將除共享狀態之外的功能剝離出來成為獨立的服務。 這種設計可以類比為單機操作系統的微內核設計。在這種設計中,內核只負責提供最核心的資源管理介面, 其他的內核功能都被實現為獨立的服務,通過調用內核提供的 API 完成工作。

共享狀態調度器的設計近些年來越來越受歡迎,這兩年炙手可熱的 Kubernetes 和它的原型 Borg 都是採用這種架構。最近由加州大學伯克利分校知名實驗室 RISELab 提出的號稱要取代 Spark 分散式計算系統 Ray 也是如此。下面將對這些案例進行介紹。

共享狀態調度器案例1: Borg/Kubernetes

根據相關論文,Borg 在初期開發的時候使用的是集中式調度器的設計,所有功能都被集中在 BorgMaster 當中,之後隨著對靈活性和可擴展性的要求,逐步切換到共享狀態模型或者說微內核模型上面去。 Google 的工程師們總結了 Borg 的經驗教訓,將這些概念集合在 Kubernetes 當中開源出來, 成為了近些年來最炙手可熱的資源管理框架。

在這裡我們依然以 Borg 為例進行介紹,Kubernetes 在具體的設計上是與 Borg 基本一致的。 下圖是 Borg 設計架構示意圖:

Borg 資源調度架構

Borg 資源調度架構的設計可以總結為以下幾點:

  1. 一個數據中心的集群可以被組織成一個 Borg 當中的 Cell
  2. 在一個 Borg 的 Cell 當中,資源的管理類似於集中式調度器的設計——集群資源由 BorgMaster 統一管理, 每一個節點上運行著 Borglet 定時將本機器的狀態與 BorgMaster 同步
  3. 為了增加可用性,BorgMaster 使用了 Stand-By Master 的模式。也就是說同時運行著 BorgMaster 的多個熱備份, 當 Active 的 BorgMaster 出現失敗,新的 Master 會被選取出來
  4. 為了增加可擴展性和靈活性,BorgMaster 的大部分功能被剝離出來成為獨立的服務。最終,BorgMaster 只剩下維護集群資源和任務狀態這唯一一個功能,包括 Scheduler 在內的所有其他服務都獨立運行
  5. 獨立運行的每個 Scheduler 可以運行自己的調度策略,它們定時從 BorgMaster 同步集群資源狀態, 根據自己的需要做出修改,然後通過 API 同步回 BorgMaster,從而實現調度功能

可以看到,Borg 的共享狀態調度架構其實是集中式調度的改進,由於承載調度邏輯的調度器都運行在獨立的服務里, 對於 BorgMaster 的請求壓力得到了某種程度的緩解。使用微內核設計模式,BorgMaster 自己包含的邏輯就比較簡單了, 系統的魯棒性、靈活性和可擴展性得到了增強。

在 Borg 中,任務的隔離和資源限制使用了 Linux 的 cgroup 機制。在 Kubernetes 當中,這一機制被 Container 技術替代, 實際上的功能是等價的。

Borg 的共享狀態設計看似簡單,其實具體實現仍然比較複雜。事實上,集中式的狀態管理仍然會成為瓶頸。 隨著集群規模的擴展和狀態的規模擴大,State Storage 必須使用分散式數據儲存機制來保證可用性和低延遲。

共享狀態架構的設計和雙層設計的最大區別是, 共享狀態被抽取出來由一個統一的組件管理。從其他的各種服務的角度來看, 共享狀態提供的調用介面和集中式調度的狀態管理是一樣的。 這種設計通過封裝內部細節的方式降低了外部服務編寫的複雜度,體現了系統設計里封裝複雜模塊的思想。

共享狀態調度器案例2: Omega

上面介紹的 Borg 是共享狀態最典型的一個示例。儘管 BorgMaster 已經為其他服務的編程提供了簡單的介面, 但是仍然沒有降低狀態一致性同步的難度——BorgMaster 和服務的編寫著仍然需要考慮很多並發控制的方法, 防止對共享狀態的修改出現 Race Condition 或死鎖的現象。如何為其他服務和調度策略提供一層簡單的抽象, 使得任務的調度能兼顧吞吐量、延遲和並發安全呢?

Omega 使用事務(Transaction)解決共享狀態一致性管理的問題。這一思路非常直觀——如果將資料庫儲存的數據看作共享狀態, 那麼資料庫就是是共享狀態管理的最成熟、最通用的解決方案!事務更是早已被開發者們熟悉而且證明非常成熟和好用的並發抽象。

事務調度策略

Omega 將集群中資源的使用和任務的調度看作資料庫中的條目,在一個應用執行的過程當中, 調度器可以分步請求多種資源,當所有資源依次被佔用並使任務執行完成,這個 Transaction 就會被成功 Commit。

Omega 的設計借鑒了很多資料庫設計的思路,比如:

  1. Transaction 設計保留了一般事務的諸多特性,如嵌套 Transaction 或者 Checkpoint。 當資源無法獲取或任務執行失敗,事務將會被回滾到上一個 Checkpoint 那裡
  2. Omega 可以實現傳統資料庫的死鎖檢測機制,如果檢測到死鎖,可以安全地撤銷一個任務或其中的一些步驟
  3. Omega 使用了樂觀鎖,也就是說申請的資源不會立刻被加上排他鎖,只有需要真正分配資源或進行事務提交的時候才會檢查鎖的狀態, 如果發現出現了 Race Condition 或其他錯誤,相關事務可以被回滾
  4. Omega 可以像主流資料庫一樣定義 Procedure ,這些 Procedure 可以實現一些簡單的邏輯, 用於對用戶的資源請求進行合法性的驗證(如優先順序保證、一致性校驗、資源請求許可權管理等)

Omega 使用事務管理狀態的想法非常新穎,這一設計隨著分散式資料庫以及分散式事務的逐漸發展和成熟而逐漸變得可行, 它一度被認為將成為 Google 的下一代調度系統。

然而近期的一些消息表明為了達到設計目標,Omega 的實現邏輯變得越來越複雜。 在原有的 Borg 共享狀態模型已經能滿足絕大部分需要的情況下,Omega 的前景似乎沒有那麼樂觀。

總結

這篇文章介紹了多種調度器結構設計的模型並討論了在相關模型下進行任務調度的一些特點。 通過這些模型的對比,我想提出自己對調度系統設計的幾點看法:

  1. 在小規模的應用和需要自己設計調度器的場景,我們應該盡量採取中心化的調度模型。這是因為這種模型設計和使用都比較簡單, 調度器容易對整個系統的狀態有全面的把握,狀態同步的困難也不高
  2. 在機群和應用規模繼續擴大或者對調度演算法有定製要求的情況下,可以考慮使用雙層調度器設計。 雙層調度器調度策略的編寫較為複雜,隨著新一代共享狀態調度器的發展,在未來可能會慢慢退出主流
  3. 共享狀態調度器因為其較為簡單的編程介面以及適應多種需要的特點,正隨著 Kubernetes 的流行而漸漸變成主流。 如果應用規模比較大或需要在一個集群上運行多種定製調度策略,這種調度器架構設計是最有前景的

最後,通過學習和親自設計一套調度系統,我深刻的領會到一些個人在編程的時候非常重要的經驗:

  1. Keep things simple 。在實現任何程序的時候,簡單的設計往往比複雜的設計更好。 比如說盡量減少系統中相互獨立的各種模塊,盡量統一編程語言,盡量減少相互隔離的系統狀態。 這樣的設計可以減少 Bug 出現的概率,降低維護狀態同步的難度
  2. Move fast。在設計複雜系統的時候很容易陷入對細節的不必要追究上,從而導致需要管理的細節越來越多, 增加了很多心智壓力。最後系統完成的進度也是難上加難。更好的辦法是先從宏觀上進行大概的設計, 在進行實現的時候忽略具體的細節(比如代碼如何組織、函數如何相互調用、代碼如何寫得好看等), 快速迭代並實現功能。當然,在這個過程中也仍然要把握好功能和質量的平衡
  3. 技術發展的循環上升軌跡。 回憶起當初 Linux 和 Minix 在宏內核和微內核之間的世紀論戰, 儘管以 Linux 這種 Monolithic 內核設計的勝出而告終,但是 Minix 的作者在其著述的《Modern Operating System》 教科書上指出了這種循環上升的軌跡,預言了微內核設計的歸來。看一看共享狀態調度系統的設計就會發現, 這一預言已經應驗在了分散式系統上

展望

在本文中,並沒有特別涉及到任務調度的具體演算法,比如如何準確地定時激發任務,如何更高效地分配資源等等。 調度演算法所要解決的問題本質上只有兩個:

  1. 全面掌握當前系統的狀態
  2. 準確預測未來的任務需求

很多調度器模型在設計上已經對這兩方面有所考量,但調度器演算法本身可以說又是一個巨大的主題, 筆者本身對其了解也非常有限,因此不敢在這篇文章中繼續展開。從直覺上講,上述需求二可能是一個和 AI 技術相結合很好的切入點,在未來可能會有很多研究。

在前文還提到了很多調度器也會附帶管理本地文件資源的分發,比如像 Kubernetes 啟動任務的時候需要將 Docker 鏡像分發到各個宿主機上。作為其原型,Borg 在這一過程中甚至利用了 P2P 技術加快分發速度和充分利用帶寬。 在開源世界中似乎還沒有類似的解決方案。當然,這一需求也只有在機群規模非常大的時候才有價值, 但未來仍可能成為一個不錯的發展方向。

推薦閱讀:

人類對人工智慧的嚮往和幻象由來已久,那麼,這次有什麼不同?——Yann LeCun上海紐約大學講座及座談精華
【原著解讀】丹尼特的《心靈的演化》:兩種奇怪的倒置推理
CS224N Lecture3 筆記
CS224N Lecture2 筆記

TAG:分散式系統 | 調度演算法 | 計算機科學 |