Apache Flink on K8s:四種執行模式,我該選擇哪種?

騰訊雲原生發表於2020-09-29

1. 前言

Apache Flink 是一個分散式流處理引擎,它提供了豐富且易用的API來處理有狀態的流處理應用,並且在支援容錯的前提下,高效、大規模的執行此類應用。通過支援事件時間(event-time)、計算狀態(state)以及恰好一次(exactly-once)的容錯保證,Flink迅速被很多公司採納,成為了新一代的流計算處理引擎。

2020 年 2 月 11 日,社群釋出了 Flink 1.10.0 版本, 該版本對效能和穩定性做了很大的提升,同時引入了 native Kubernetes 的特性。對於 Flink 的下一個穩定版本,社群在 2020 年 4 月底凍結新特性的合入,預計在 2020 年 5 月中旬會推出 Flink 1.11,在新版本中將重點引入新特性,以擴容 Flink 的使用場景。

Kubernetes 專案源自 Google 內部 Borg 專案,基於 Borg 多年來的優秀實踐和其超前的設計理念,並憑藉眾多豪門、大廠的背書,時至今日,Kubernetes 已經成長為容器管理領域的事實標準。在大資料及相關領域,包括 Spark,Hive,Airflow,Kafka 等眾多知名產品正在遷往 Kubernetes,Apache Flink 也是其中一員。

Flink 選擇 Kubernetes 作為其底層資源管理平臺,原因包括兩個方面:

1)Flink 特性:流式服務一般是常駐程式,經常用於電信網質量監控、商業資料即席分析、實時風控和實時推薦等對穩定性要求比較高的場景;

2)Kubernetes 優勢:為線上業務提供了更好的釋出、管理機制,並保證其穩定執行,同時 Kubernetes 具有很好的生態優勢,能很方便的和各種運維工具整合,如 prometheus 監控,主流的日誌採集工具等;同時 K8S 在資源彈性方面提供了很好的擴縮容機制,很大程度上提高了資源利用率。

在 Flink 的早期發行版 1.2 中,已經引入了 Flink Session 叢集模式,使用者得以將 Flink 叢集部署在 Kubernetes 叢集之上。

隨著 Flink 的逐漸普及,越來越多的 Flink 任務被提交在使用者的叢集中,使用者發現在 session 模式下,任務之間會互相影響,隔離性比較差,因此在 Flink 1.6 版本中,推出了 Per Job 模式,單個任務獨佔一個 Flink 叢集,很大的程度上提高了任務的穩定性。

在滿足了穩定性之後,使用者覺得這兩種模式,沒有做到資源按需建立,往往需要憑使用者經驗來事先指定 Flink 叢集的規格,在這樣的背景之下,native session 模式應用而生,在 Flink 1.10 版本進入 Beta 階段,我們增加了 native per job 模式,在資源按需申請的基礎上,提高了應用之間的隔離性。

本文根據 Flink 在 Kubernetes 叢集上的執行模式的趨勢,依次分析了這些模式的特點,並在最後介紹了 Flink operator 方案及其優勢。

2. Flink執行模式

本文首先分析了 Apache Flink 1.10 在 Kubernetes 叢集上已經GA(生產可用)的兩種部署模式,然後分析了處於 Beta 版本的 native session 部署模式和即將在 Flink 1.11 釋出的 native per-job 部署模式,最後根據這些部署模式的利弊,介紹了當前比較 native kubernetes 的部署方式,flink-operator。

我們正在使用的 Flink 版本已經很好的支援了 native session 和 native per-job 兩種模式,在 flink-operator 中,我們也對這兩種模式也做了支援。

接下來將按照以下順序分析了 Flink 的執行模式,讀者可以結合自身的業務場景,考量適合的 Flink 執行模式。

  • Flink session 模式
  • Flink per-job 模式
  • Flink native session 模式
  • Flink native per-job 模式

這四種部署模式的優缺點對比,可以用如下表格來概括,更多的內容,請參考接下來的詳細描述。

2.1 Session Cluster 模式

2.1.1 原理簡介

Session 模式下,Flink 叢集處於長期執行狀態,當叢集的Master元件接收到客戶端提交的任務後,對任務進行分析並處理。使用者將Flink叢集的資源描述檔案提交到 Kubernetes 之後,Flink 叢集的 FlinkMaster 和 TaskManager 會被建立出來,如下圖所示,TaskManager 啟動後會向 ResourceManager 模組註冊,這時 Flink Session 叢集已經準備就緒。當使用者通過 Flink Clint 端提交了 Job 任務時,Dispatcher 收到該任務請求,將請求轉發給 JobMaster,由 JobMaster 將任務分配給具體的 TaskManager。

2.1.2 特點分析

這種型別的 Flink 叢集,FlinkMaster 和 TaskManager 是以Kubernetes deployment的形式長期執行在 Kubernetes 叢集中。在提交作業之前,必須先建立好 Flink session 叢集。多個任務可以同時執行在同一個叢集內,任務之間共享 K8sResourceManager 和 Dispatcher,但是 JobMaster 是單獨的。這種方式比較適合執行短時作業、即席查詢、任務提交頻繁、或者對任務啟動時長比較敏感的場景。

優點:作業提交的時候,FlinkMaster 和 TaskManager已經準備好了,當資源充足時,作業能夠立即被分配到 TaskManager 執行,無需等待 FlinkMaster,TaskManager,Service 等資源的建立;

缺點:1)需要在提交 Job 任務之前先建立 Flink 叢集,需要提前指定 TaskManager 的數量,但是在提交任務前,是難以精準把握具體資源需求的,指定的多了,會有大量 TaskManager 處於閒置狀態,資源利用率就比較低,指定的少了,則會有任務分配不到資源,只能等叢集中其他作業執行完成後,釋放了資源,下一個作業才會被正常執行。

  1. 隔離性比較差,多個 Job 任務之間存在資源競爭,互相影響;如果一個 Job 異常導致 TaskManager crash 了,那麼所有執行在這個 TaskManager 上的 Job 任務都會被重啟;進而,更壞的情況是,多個 Jobs 任務的重啟,大量併發的訪問檔案系統,會導致其他服務的不可用;最後一點是,在 Rest interface 上是可以看到同一個 session 叢集裡其他人的 Job 任務。

2.2 Per Job Cluster 模式

顧名思義,這種方式會專門為每個 Job 任務建立一個單獨的 Flink 叢集,當資源描述檔案被提交到 Kubernetes 叢集, Kubernetes 會依次建立 FlinkMaster Deployment、TaskManagerDeployment 並執行任務,任務完成後,這些 Deployment 會被自動清理。

2.2.1 特點分析

優點:隔離性比較好,任務之間資源不衝突,一個任務單獨使用一個 Flink 叢集;相對於 Flink session 叢集而且,資源隨用隨建,任務執行完成後立刻銷燬資源,資源利用率會高一些;

缺點:需要提前指定 TaskManager 的數量,如果 TaskManager 指定的少了會導致作業執行失敗,指定的多了仍會降低資源利用率;資源是實時建立的,使用者的作業在被執行前,需要先等待以下過程:

· Kubernetes scheduler為FlinkMaster和 TaskManager 申請資源並排程到宿主機上進行建立;

· Kubernetes kubelet拉取FlinkMaster、TaskManager 映象,並建立出FlinkMaster、TaskManager容器;

· TaskManager啟動後,向Flink ResourceManager 註冊。

這種模式比較適合對啟動時間不敏感、且長時間執行的作業。不適合對任務啟動時間比較敏感的場景。

2.3 Native Session Cluster 模式

2.3.1 原理分析

  1. Flink提供了Kubernetes模式的入口指令碼 kubernetes-session.sh,當使用者執行了該指令碼之後,Flink 客戶端會生成 Kubernets 資源描述檔案,包括 FlinkMaster Service,FlinkMasterDeloyment,Configmap,Service並設定了owner reference,在 Flink 1.10 版本中,是將 FlinkMaster Service 作為其他資源的 Owner,也就意味著在刪除 Flink 叢集的時候,只需要刪除 FlinkMaster service,其他資源則會被以及聯的方式自動刪除;

  2. Kubernetes 收到來自 Flink 的資源描述請求後,開始建立FlinkMaster Service,FlinkMaster Deloyment,以及 Configmap 資源,從圖中可以看到,伴隨著 FlinkMaster 的建立,Dispatch 和K8sResMngr 元件也同時被建立了,這裡的 K8sResMngr 就是 Native 方式的核心元件,正是這個元件去和 Kubernetes API server 進行通訊,申請 TaskManager 資源;當前,使用者已經可以向Flink 叢集提交任務請求了;

  3. 使用者通過 Flink client 向 Flink 叢集提交任務,flink client 會生成 Job graph,然後和 jar 包一起上傳;當任務提交成功後,JobSubmitHandler 收到了請求並提交給 Dispatcher並生成 JobMaster, JobMaster 用於向 KubernetesResourceManager 申請 task 資源;

  4. Kubernetes-Resource-Manager 會為 taskmanager 生成一個新的配置檔案,包含了 service 的地址,這樣當 Flink Master 異常重建後,能保證 taskmanager 通過 Service 仍然能連線到新的 Flink Master;

  5. TaskManager 建立成功後註冊到 slotManager,這時 slotManager向TaskManager 申請 slots,TaskManager 提供自己的空閒 slots,任務被部署並執行;

2.3.2. 特點分析

之前我們提到的兩種部署模式,在 Kubernetes 上執行 Flink 任務是需要事先指定好 TaskManager 的數量,但是大部分情況下,使用者在任務啟動前是無法準確的預知該任務所需的 TaskManager 數量和規格。

指定的多了會資源浪費,指定的少了會導致任務的執行失敗。最根本的原因,就是沒有 Native 的使用 Kubernetes 資源,這裡的 Native,可以理解為 Flink 直接與 Kuberneter 通訊來申請資源。

這種型別的叢集,也是在提交任務之前就建立好了,不過只包含了 FlinkMaster 及其 Entrypoint(Service),當任務提交的時候,Flink client 會根據任務計算出並行度,進而確定出所需 TaskManager 的數量,然後 Flink 核心會直接向 Kubernetes API server 申請 taskmanager,達到資源動態建立的目的。

  • 優點:相對於前兩種叢集而言,taskManager 的資源是實時的、按需進行的建立,對資源的利用率更高,所需資源更精準。
  • 缺點:taskManager 是實時建立的,使用者的作業真正執行前, 與 Per Job叢集一樣, 仍需要先等待 taskManager 的建立, 因此對任務啟動時間比較敏感的使用者,需要進行一定的權衡。

2.4 Native Per Job 模式

在當前的 Apache Flink 1.10 版本里,Flink native per-job 特性尚未釋出,預計在後續的 Flink 1.11 版本中提供,我們可以提前一覽 native per job 的特性。

2.4.1 原理分析

當任務被提交後,同樣由 Flink 來向 kubernetes 申請資源,其過程與之前提到的 native session 模式相似,不同之處在於:

  1. Flink Master是隨著任務的提交而動態建立的;
  2. 使用者可以將 Flink、作業 Jar 包和 classpath 依賴打包到自己的映象裡;
  3. 作業執行圖由 Flink Master 生成,所以無需通過 RestClient 上傳 Jar 包(圖 2 步驟 3)。

2.4.2. 特點分析

native per-job cluster 也是任務提交的時候才建立 Flink 叢集,不同的是,無需使用者指定 TaskManager 資源的數量,因為同樣藉助了 Native 的特性,Flink 直接與 Kubernetes 進行通訊並按需申請資源。

  • 優點:資源按需申請,適合一次性任務,任務執行後立即釋放資源,保證了資源的利用率;
  • 缺點:資源是在任務提交後開始建立,同樣意味著對於提交任務後對延時比較敏感的場景,需要一定的權衡;

3.1 簡介

分析以上四種部署模式,我們發現,對於 Flink 叢集的使用,往往需要使用者自行維護部署指令碼,向 Kubernetes 提交各種所需的底層資源描述檔案(Flink Master,TaskManager,配置檔案,Service)。

在 session cluster 下,如果叢集不再使用,還需要使用者自行刪除這些的資源,因為這類叢集的資源使用了 Kubernetes 的垃圾回收機制 owner reference,在刪除 Flink 叢集的時候,需要通過刪除資源的 Owner 來進行及聯刪除,這對於不熟悉 Kubernetes 的 Flink 使用者來說,就顯得不是很友好了。

而通過 Flink-operator,我們可以把 Flink 叢集描述成 yaml 檔案,這樣,藉助 Kubernetes 的宣告式特性和協調控制器,我們可以直接管理 Flink 叢集及其作業,而無需關注底層資源如 Deployment,Service,ConfigMap 的建立及維護。

當前 Flink 官方還未給出 flink-operator 方案,不過 GoogleCloudPlatform 提供了一種基於 kubebuilder 構建的 flink-operator方案。接下來,將介紹 flink-operator 的安裝方式和對 Flink 叢集的管理示例。

當 Fink operator 部署至 Kubernetes 叢集后, FlinkCluster 資源和 Flink Controller 被建立。其中 FlinkCluster 用於描述 Flink 叢集,如 JobMaster 規格、TaskManager 和 TaskSlot 數量等;Flink Controller 實時處理針對 FlinkCluster 資源的 CRUD 操作,使用者可以像管理內建 Kubernetes 資源一樣管理 Flink 叢集。

例如,使用者通過 yaml 檔案描述期望的 Flink 叢集並向 Kubernetes 提交,Flink controller 分析使用者的 yaml,得到 FlinkCluster CR,然後呼叫 API server 建立底層資源,如JobMaster Service, JobMaster Deployment,TaskManager Deployment。

通過使用 Flink Operator,有如下優勢:

1. 管理 Flink 叢集更加便捷

flink-operator 更便於我們管理 Flink 叢集,我們不需要針對不同的 Flink 叢集維護 Kubenretes 底層各種資源的部署指令碼,唯一需要的,就是 FlinkCluster 的一個自定義資源的描述檔案。建立一個 Flink session 叢集,只需要一條 kubectl apply 命令即可,下圖是 Flink Session叢集的 yaml 檔案,使用者只需要在該檔案中宣告期望的 Flink 叢集配置,flink-operator 會自動完成 Flink 叢集的建立和維護工作。如果建立 Per Job 叢集,也只需要在該 yaml 中宣告 Job 的屬性,如 Job 名稱,Jar 包路徑即可。通過 flink-operator,上文提到的四種 Flink 執行模式,分別對應一個 yaml 檔案即可,非常方便。

apiVersion: flinkoperator.k8s.io/v1beta1kind: FlinkClustermetadata:  name: flinksessioncluster-samplespec:  image:    name: flink:1.10.0    pullPolicy: IfNotPresent  jobManager:    accessScope: Cluster    ports:      ui: 8081    resources:      limits:        memory: "1024Mi"        cpu: "200m"  taskManager:    replicas: 1    resources:      limits:        memory: "2024Mi"        cpu: "200m"    volumes:      - name: cache-volume        emptyDir: {}    volumeMounts:      - mountPath: /cache        name: cache-volume  envVars:    - name: FOO      value: bar  flinkProperties:    taskmanager.numberOfTaskSlots: "1"

2. 宣告式

通過執行指令碼命令式的建立 Flink 叢集各個底層資源,需要使用者保證資源是否依次建立成功,往往伴隨著輔助的檢查指令碼。藉助 flink operator 的控制器模式,使用者只需宣告所期望的 Flink 叢集的狀態,剩下的工作全部由 Flink operator 來保證。在 Flink 叢集執行的過程中,如果出現資源異常,如 JobMaster 意外停止甚至被刪除,Flink operator 都會重建這些資源,自動的修復 Flink 叢集。

3. 自定義儲存點

使用者可以指定 autoSavePointSeconds 和儲存路徑,Flink operator 會自動為使用者定期儲存快照。

4. 自動恢復

流式任務往往是長期執行的,甚至 2-3 年不停止都是常見的。在任務執行的過程中,可能會有各種各樣的原因導致任務失敗。使用者可以指定任務重啟策略,當指定為 FromSavePointOnFailure,Flink operator 自動從最近的儲存點重新執行任務。

5. sidecar containers

sidecar 容器也是 Kubernetes 提供的一種設計模式,使用者可以在 TaskManager Pod 裡執行 sidecar 容器,為 Job 提供輔助的自定義服務或者代理服務。

6. Ingress 整合

使用者可以定義 Ingress 資源,flink operator 將會自動建立 Ingress 資源。雲廠商託管的 Kubernetes 叢集一般都有 Ingress 控制器,否則需要使用者自行實現 Ingress controller。

7. Prometheus 整合

通過在 Flink 叢集的 yaml 檔案裡指定 metric exporter 和 metric port,可以與 Kubernetes 叢集中的 Prometheus 進行整合。

最後

通過本文,我們瞭解了 Flink 在 Kubernetes 上執行的不同模式,其中 Native 模式在資源按需申請方面比較突出,藉助 kubernetes operator,我們可以將 Flink 叢集當成Kubernetes原生的資源一樣進行 CRUD 操作。限於篇幅,本文主要分析了 Flink 在 Kubernetes 上的執行模式的區別,後續將會有更多的文章來對 Flink 在 Kubernetes 上的最佳實踐進行描述,敬請期待。

參考文件

Kubernetes native integration

https://docs.google.com/document/d/1-jNzqGF6NfZuwVaFICoFQ5HFFXzF5NVIagUZByFMfBY/edit#heading=h.thxqqaj3vxmz

Flink operator 使用文件

https://github.com/tkestack/flink-on-k8s-operator/tree/nativePerJob

【騰訊雲原生】雲說新品、雲研新術、雲遊新活、雲賞資訊,掃碼關注同名公眾號,及時獲取更多幹貨!!

相關文章