咱們的Rancher官方技術社區已經創立些許時日了,相信經過咱們的線下meetup和線上佈道工做,不少朋友對Rancher的使用已經掌握得很純熟了。一些高級用戶開始真正把本身的業務進行微服務化並向Rancher遷移,在遷移的過程當中,因爲業務自己的複雜性特殊性,可能須要利用Rancher的一些高級特性甚至要對Rancher進行必定的擴展,這就須要對Rancher的一些組件的實現機制有些許瞭解。java
本次分享就介紹一下Rancher的event機制,因爲相關內容文檔極其欠缺,本人也只是經過實踐和代碼閱讀分析其原理,若有謬誤歡迎指正。python
在大規模系統架構中,event機制一般採用消息驅動 ,它對提高分佈式架構的容錯性靈活性有很大幫助,同時也是各個組件之間解耦的利器。Rancher可以管理N多的agent同時又拆分出各類服務組件,event機制是必不可少的。爲實現event機制,一般咱們會採用RabbitMQ、ActiveMQ、ZeroMQ等中間件來實現。而Rancher則採用了基於websocket協議的一種很是輕量級的實現方式,它的好處就是極大程度的精簡了Rancher的部署,Rancher無需額外維護一個MQ集羣,畢竟websocket的消息收發實現是很是簡單的,各類語言庫都可以支持。mysql
這裏咱們會考慮一個問題,websocket畢竟不是真正工業級MQ的實現,消息不能持久化,一旦某個event的處理出現問題,或者發生消息丟失,Rancher如何保證各個資源的原子性一致性?Rancher中有一個processpool的概念,它能夠看作一個全部event的執行池,當API/UI/CLI有操做時,Rancher會把操做分解成多個event並放入processpool中。好比刪除一個容器時會把 compute.instance.remove 放入processpool中,這個event會發送到對應的host agent上,agent處理完成後會發送reply給rancher-server。若是在這個過程當中,因爲網絡問題消息丟失,或者agent上執行出現問題,rancher-server沒有收到reply信息,cattle會把這個event從新放到processpool中再次重複上面的過程,直到 compute.instance.remove 完成操做,這個容器的狀態纔會在DB中更新,不然該容器狀態會一直處於lock不能被其餘服務更新。固然cattle不會把這些event不停的重複執行下去,一般會設置一下TIMEOUT超出後便再也不執行(有些資源沒有TIMEOUT機制)。git
上面的表述,咱們其實能夠在UI上看到這個過程,RancherUI上的Processes的Running Tab頁上就能實時得看到這些信息,Processes 在排查一些Rancher的相關問題是很是有用的,你們能夠養成 」查問題先查Processes「的好習慣:github
那麼監聽event的URL怎麼設定呢?很是簡單:web
ws://<rancher-server-ip>:8080/v1/projects/<projectId>/subscribe?eventNames=xxxx
除此以外還須要加上basic-auth的header信息spring
Authorization: Basic +base64encode(<cattle-access-key>:<cattle-secret-key>)
若是是Host上的agent組件,還須要添加agentId參數sql
ws://<rancher-server-ip>:8080/v1/projects/<projectId>/subscribe?eventNames=xxxx&agentId=xxxx
agentId 是註冊Host時生成的,若是沒有agentId參數,任何有關無關的event都會發送到全部的Host agent上,這樣就會發生相似「廣播風暴」的效果。api
Host agent上運行不少組件,其中python-agent是負責接收和回執event信息的,其運行日誌能夠在Host上的/var/log/rancher/agent.log文件中查看。websocket
細心的朋友可能會有疑問,咱們在添加Host時執行agent容器時並無指定cattle-access-key和cattle-secret-key,也就是說python-agent運行時如何獲取這兩個祕鑰信息呢?
其實Rancher有兩種apikey:一種是咱們熟知的在UI上手動建立的apikey;另一種就是agentApikey,它是系統級的,專門爲agent設定,添加Host時會先把agentApikey發送到Host上。在cattle的credential表中能夠查詢到相關信息:
eventNames都定義了哪些呢?下面兩個文件能夠參考:
https://github.com/rancher/ca...系統級的event定義
https://github.com/rancher/ca... 詳細到每種資源(host、volume、instance、stack、service等)的event定義。
此外,一旦咱們在UI/CLI/API上的某個操做都會被分解成多個event來執行,每一個event信息都會被保存在mysql中,每一個event執行成功後會設置成purged狀態,因此表中記錄並不會真正刪除,這就會致使相應的表(container_event表、service_event表、process_instance表)會無限膨脹下去。
Rancher爲解決此問題提供了週期性清理機制
events.purge.after.seconds 能夠清理container_event和service_event,每兩週清理一次;process_instance.purge.after.seconds能夠清理process_instance,天天清理一次。這兩個配置咱們均可以在 http://<rancher-server-ip&... 動態修改。
下面咱們來實踐一下,看看如何在程序中實現對Rancher event的監聽。
Rancher提供了resource.change事件,這個事件是不用reply的,就是說不會影響Rancher系統的運行,它是專門開放給開發者用來實現一些本身的定製功能,因此咱們就以resource.change做爲例子實踐一下。
Rancher的組件大部分都是基於Golang編寫,因此咱們也採用相同的語言。
爲了可以快速實現這個程序,咱們須要瞭解一些輔助快速開發的小工具。
Trash,Golang package管理的小工具,能夠幫助咱們定義依賴包的路徑和版本,很是輕量且方便
Dapper,這個是基於容器實現Golang編譯的工具,主要是能夠幫助咱們統一編譯環境
go-skel,它能夠幫咱們快速建立一個Rancher式的微服務程序,能夠爲咱們省去不少的基本代碼,同時還集成了Trash和Dapper這兩個實用的小工具
使用詳情能夠參考我以前寫的一篇內容:
回到本文的主題,首先咱們基於go-skel建立一個工程名爲 scale-subscriber (名字很隨意),執行過程須要耐心的等待:
執行完畢後,咱們把工程放到GOPATH中,開始添加相關的邏輯代碼。
在這以前咱們能夠考慮添加一個healthcheck的服務端口,其實縱觀Rancher全部的微服務組件,基本上除了主程序以外都會添加healthcheck port,這個主要是爲了與Rancher中的healthcheck功能配合,經過設定對這個端口的檢查機制來保證微服務的可靠性。
咱們利用Golang的goroutine機制,分別添加主服務和healthcheck服務:
主服務的核心就是監聽resource.change的信息,同時註冊handler來獲取event的payload信息,從而能夠定製擴展本身的邏輯,這裏須要利用Rancher提供的 https://github.com/rancher/ev... 庫來快速實現。
以下圖,在 reventhandlers.NewResourceChangeHandler().Handler 中就能夠實現本身的邏輯:
這裏咱們只是演示監聽event的機制,因此咱們就不作過多的業務邏輯處理,打印輸出event信息便可。
而後在項目根目錄下執行make,make會自動調用dapper,在bin目錄下生成scale-subscriber,咱們執行scale-subscriber就能夠監聽resource.change的信息:
這裏咱們能夠看到分別啓動了healthcheck功能和event listener。而後能夠在UI上隨意刪除一個stack,scale-subsciber就能夠獲取event信息:
如要將其部署在Rancher中,那就能夠將scale-subsciber執行程序打包放到鏡像中,經過compose來啓動。
可是咱們知道scale-subsciber啓動須要指定CATTLE_URL、CATTLE_ACCESS_KEY、CATTLE_SECRET_KEY,正常的作法就是咱們須要先生成好apikey,而後啓動service的時候設置對應的環境變量。
這樣作就會有弊端,就是apikey這種私密的信息不得不對外暴露,並且還要手動維護這些apikey,很是不方便。
Rancher提供了一個很是方便的作法,就是在service中添加兩個label,
io.rancher.container.create_agent: true
io.rancher.container.agent.role: environment
設定這兩個label後,Rancher引擎會自動建立apikey,並把相應的值設置到容器的ENV中,只要你的程序經過系統環境變量來讀取這些值,就會很是順利的運行起來。