1. 程式人生 > >Kubernetes(K8s)容器設計模式實踐案例

Kubernetes(K8s)容器設計模式實踐案例

Kubernetes與雲原生應用系列之六 – K8s容器設計模式實踐案例 – 工作佇列模式

K8s與容器設計模式

目前K8s社群推出的容器設計模式主要分為三大類:第一類,單容器管理模式;第二類,單節點多容器模式;第三類,多節點多容器模式;一類比一類更復雜。

根據複雜性的不同,本系列文章給出不同篇幅的實踐案例介紹。所有在雲端計算平臺上執行的應用業務都可以分成兩大類,即長時執行服務和批處理業務。工作佇列模式是一系列容器設計模式中第一個面向批處理業務的模式,在Kubernetes叢集中,以API物件Job為基礎處理批處理任務。

本文將藉助示例介紹多節點多容器模式中的工作佇列模式。

工作佇列模式

分散式系統的一個重要作用是能夠充分利用多個物理計算資源的能力,特別是在動態按需調動計算資源完成計算任務。

設想如果有大量的需要處理的任務隨即的到來,對計算資源需要的容量是不確定地;顯然,按照最大可能計算量和最小可能計算量設定計算節點都是不合理的。

這種情況下,可以把需要處理的任務放到一個待處理的佇列裡,根據需要啟動計算節點從佇列讀取任務進行處理。在容器技術廣泛應用之前,也有諸多的分散式處理系統依靠佇列來處理大量計算任務,例如大資料處理系統Hadoop和Spark等。

這些系統的一個限制是實現佇列處理模式大多要遵循特定的程式設計模式和特定的程式語言,同時搭建基礎設施也大多複雜而耗時。而基於容器和Kubernetes編排技術的工作佇列模式的好處在於,利用非常簡單的編排指令碼就可以實現工作佇列模式,而用Pod作為輕量級處理節點的模式,使得動態的排程計算資源變得非常容易。

在Kubernetes中應用工作佇列模式的邏輯示意圖如Fig01。

tu1

在Kubernetes叢集中,可以啟動一個佇列服務,用來儲存等待處理的任務。這種佇列可以用通用的訊息佇列RabbitMQ,ActiveMQ或Kafka來實現,也可以用Redis這類支援集合儲存的記憶體資料庫實現。

向工作佇列輸入待處理任務的是工作佇列前端服務,這可以是一個REST服務,一個視覺化的介面服務,也可以是一個可以提交任務的命令列或客戶端API。

無論是何種,工作佇列前端服務的作用是向工作佇列新增任務。真正處理任務的是包含了應用程式的應用Pod。由於在Kubernetes中,Pod有能力支援多容器,這使得在同一個Pod中,從工作佇列裡讀取任務的容器和處理任務的容器可以來自不同的容器映象,其中處理應用任務的容器根據不同的任務各不相同,而讀取任務的容器是可以在任何應用處理中重用的。

正如圖Fig01所顯示的,在這樣一個工作佇列模式的分散式系統中,工作佇列服務、工作佇列前端服務、工作佇列讀取容器這幾個模組都是可以重用的,只有跟具體應用工作處理相關的部分要根據不同應用編寫。

利用Redis作為工作佇列驗證工作佇列模式

本文用一個簡單案例來展示在k8s叢集中應用工作佇列模式的方法。在k8s叢集中用Job這個API物件來執行批處理任務,如圖Fig02所示。

tu2

我們建立一個Job用來處理工作任務,它會產生Job Controller並控制生成多個處理任務的Pod。同時,我們建立一個Redis Pod和一個Service用來儲存工作任務。為了向工作佇列輸入任務和察看工作佇列中的任務,我們建立一個redis-cli pod作為客戶端操作工作佇列。

本文中案例的程式碼

本文中使用的程式碼和構建映象的Dockerfile在Github倉庫https://github.com/xwangqingyuan/kube-templates/tree/master/examples/job
本文中使用的容器映象在https://github.com/xwangqingyuan/kube-templates/tree/master/examples/job

用於操作redis佇列的python程式碼清單。

tu3

在這個python檔案中定義了RedisWQ類,用來操作redis的佇列。在建立RedisWQ物件的時候,會傳入所要連線的Redis的主機URL和佇列名稱。

在類RedisWQ中定義了lease方法,用來鎖定佇列中的一個任務,這樣可以保證每個任務只被一個Pod處理,在處理時,任務被這個Pod鎖定,其它Pod就不會再取得這個任務進行處理了。

在處理完成後,處理的Pod呼叫complete方法,標誌這個任務已經被處理完的同時,將這個任務從佇列中刪除。為了保證同一個任務不被一個Pod永遠鎖定,在呼叫lease方法時,同時可以設定一個過期時間,超過這個過期時間,萬一正在處理的Pod已經死掉了,佇列可以自己解鎖這個任務,讓其它Pod來處理。

呼叫RedisWQ來處理任務的程式碼。

tu4

利用rediswq.py和work.py,我們可以構建一個容器映象,這個映象專門用來處理redis佇列中的任務。本文作者已經構建了一個映象在Docker hub的景象倉庫xwangqingyuan/rediswq。該映象的Dockerfile如下:

tu5

用redis準備任務工作佇列

用於建立redis的Pod的yaml檔案清單。

tu6

用kubectl create命令建立一個redis的Pod。

kubectl create -f /git/github.com/xwangqingyuan/kube-templates/examples/job/redis.yaml

用於建立redis服務的yaml檔案清單。

tu7

用kubectl create命令建立一個redis的Service。

kubectl create -f /gitws/github.com/xwangqingyuan/kube-templates/examples/job/redis-service.yaml

用kubectl run命令啟動一個redis作為客戶端操作redis佇列。

kubectl run -i --tty temp --image redis --command "/bin/sh"

在redis客戶端用rpush命令向工作佇列job中新增工作任務,我們新增5個任務。

tu8

在redis客戶端用lrange命令檢視工作佇列job中的任務,我們可以查詢到5個任務。

tu9

用rediswq映象處理工作佇列中的任務

我們在同一個k8s的namespace裡,建立一個job,來處理工作佇列中的任務。處理任務的Job的Yaml檔案清單如下:

tu10

我們用kubectl create命令建立一個Job。

kubectl create -f /gitws/github.com/xwangqingyuan/kube-templates/examples/job/job.yaml

檢視job的處理結果

首先利用kubectl describe命令檢視對應job-wq-1的兩個pod的名字,我們檢視到分別為job-wq-1-45qeb和job-wq-1-dpnnj。

tu11

然後我們用kubectl logs命令分別察看兩個pod的日誌,在日誌中我們可以看到兩個pod的分別處理的任務。我們可以看到:job-wq-1-45qeb處理了fig, cherry和apple,而job-wq-1-dpnnj處理了date和banana。兩個加起來剛好處理了所有的工作任務。

tu12

然後,我們在管理redis工作佇列的客戶端上,用lrange命令檢視當前工作佇列job的任務數量,可以發現所有任務已經被處理完了。

10.0.0.180:6379> lrange job 0 -1
 (empty list or set)

總結

本文主要介紹了K8s叢集中,多節點容器模式中的工作佇列模式。工作佇列模式,是分散式計算系統的一種基本的工作模式

通過將待處理的任務放入佇列,由應用處理模組自發去佇列裡讀取任務並處理任務,提交任務請求的模組和處理任務請求的模組之間得到了解耦。在K8s系統中,原生用Job API物件來支援批處理任務選舉的目的,Job控制器可以控制同時執行任務的工作Pod數量。

由於在一個節點上啟動一個Pod來處理任務要比分配一整個節點來處理任務開銷小得多,基於kubernetes的工作佇列模式比傳統基於物理機或虛擬機器的分散式工作佇列處理系統更加敏捷高效。另一方面,因為Pod支援多容器組合的模式,使得工作佇列任務獲取模組和應用邏輯處理模組可以獨立交付但組合部署,增強了工作佇列模式中的模組重用性。