單一的應用程序,就是以前在單個節點上運行的單個實例,包括了不少在數據庫更新狀態的調度jobs(如今也一樣發佈商務events)。單體程序建立在java中,而且大量使用Spring,因此job看起來是這個樣子的:html
Spring以後會確認提到過的 doSomethingEveryMinute方法每分鐘執行一次。問題是,若是咱們目前不在Kubernetes上主持單體程序,而且跟多個實例一塊兒運行,這個job就每分鐘會在每一個實例上被執行一次,而不只僅只是每分鐘執行了一次而已。若是job有相似發送通知郵件或更新數據庫這樣的反作用的話,這就是一個問題了。因此咱們要怎樣避免這個?固然,解決方案仍是不少的,顯而易見的選擇就是利用Kubernetes Jobs,讓Kubernetes本身週期性調度jobs。問題就是這個做用只在Kubernetes1.3版本及以上版本中可用,可是1.3尚未發佈。可是即便咱們可以使用這樣一個功能,從技術角度來講,這也是不太可行的。咱們的jobs被高度耦合到已經存在的代碼庫,而且提取每一個job到它本身的應用程序,程序可能會有錯誤,並且若是一次性完成的話會很是耗費時間。因此咱們最初的計劃是提取全部的調度jobs到一個應用程序,這個應用程序在Kubernetes中只能做爲一個實例來運行。但因爲現有代碼的本質,和高耦合性,即便是這樣也很難實現。那麼,有沒有一種很輕鬆的辦法容許咱們目前在單體應用中保持jobs,而且當咱們從這個應用中提取功能到獨立的服務的時候,逐漸替代他們呢?其實仍是有的。java
要解決這個,咱們須要作一些分佈式協調,好比,當jobs被Spring執行的時候,若是這個節點不是「leader節點」,爲運行調度jobs負責,咱們就只須要傳回信息(並且,不要和job一塊兒運行代碼)。有一些項目可以幫助咱們來處理諸如zookeeper和hazelcast之類的東西。可是僅僅只是爲了決定哪一個節點執行調度jobs,以此來設置、保留zookeeper集羣就太勞師動衆了。咱們須要一些易於管理的東西,假如咱們可以利用Kubernetes會怎麼樣呢?Kubernetes已經在cover下(使用 RAFT consensus algorithm)處理了leader選舉。結果證實,這個功能經過使用gcr.io/google_containers/leader-elector Docker鏡像已經被暴露給了終端用戶。以前已經有一個很棒的博客帖子很細節地描述過這個是如何運行的了,點擊這個網址查看:http://blog.kubernetes.io/2016/01/simple-leader-election-with-Kubernetes.html。因此在這裏我就很少加贅述了,可是我會講一講咱們是如何利用鏡像來解決咱們的問題的。數據庫
咱們作的就是帶來gcr.io/google_containers/leader-elector容器到咱們的pod,這樣就可讓單體應用的實例都運行一個leader選舉的實例。這是證實Kubernetes pod有用的典型例子。服務器
如下是一個在咱們的配置resource中定義好的pod的摘錄:分佈式
咱們開啓leader選舉以及咱們的單體應用程序。注意,咱們將 --election=monolith-jobs看成第一個參數。這就意味着leader選舉知道容器屬於哪個組。因此指定這個組的容器會是leader選舉進程中的一部分,這個組之中只有一個容器會被選舉爲leader。 --http=localhost:4040的第二個參數一樣是很是重要的。它在4040端口打開了一個網頁服務器,在這個端口,咱們能夠查詢到目前leader的pod名字,而後以這個格式返回:google
這是咱們決定要不要運行咱們的job的一個小把戲。咱們要作的事情就是檢查即將執行調度pod的名字是否跟選舉出來的leader一致,若是一致,咱們就應該繼續執行,而且執行job,或者其它的咱們應該返回的東西。好比:spa
因此咱們來看看ClusterLeaderService是如何被實施的。首先,咱們必須從應用程序得到pod的名字。Kubernetes將pod名字存儲在/etc/hostname ,Java將這個/etc/hostname暴露在HOSTNAME環境變量,這就是咱們將在這個例子中引用的。另外一個方法就是使用 Downward API將pod名字暴露到環境變量選擇。好比:htm
從這裏,咱們能夠看到 metadata.name (也就是pod的名字)會與 MY_POD_NAME環境變量聯繫在一塊兒。可是如今讓咱們來看看 ClusterLeaderService的實施是看起來是怎麼樣的:blog
在這裏例子中,咱們正在從 RESTAssured 項目使用 JsonPath來查詢選舉者網頁服務,而且從迴應中提取pod的名字。而後咱們簡單地將本地容器的名字跟leader相比較,若是他們是相同的,那麼咱們就知道這個實例就是leader!就是這樣!進程
事實證實,上述工做運行地很不錯!若是leader節點要掛掉了,那麼另外一個就會自動選舉上。但這個過程會花上一點時間,要一分鐘左右。因此這個仍是要權衡一下的。假如你的工做中不容許錯過任意一個job執行,那麼這個選擇對你來講是不合適的。但在咱們上述的例子中,中間有那麼一分鐘job沒有被準確執行,對咱們來講無傷大雅。因此我以爲這個方法十分簡單,並且當移植一個現有的包含調度jobs的應用程序的時候頗有價值,由於調度jobs總有各類各樣很難提取的緣由。