基於Kubernetes(k8s)的RabbitMQ 集羣

目前,有不少種基於Kubernetes搭建RabbitMQ集羣的解決方案。今天筆者今天將要討論咱們在Fuel CCP項目當中所採用的方式。這種方式加以轉變也適用於搭建RabbitMQ集羣的通常方法。因此若是你想要設計本身的解決方案,你應該收集一些更符合你定製化需求的文章。node

命名你的集羣

Kubernetes內部運行RabbitMQ集羣會遇到一系列有意思的問題。最早會遇到的問題是爲了使各個節點之間互相可見,咱們應該如何命名各個節點。如下是一些符合規範的不一樣的命名方法:git

  • rabbit@hostname
  • rabbit@hostname.domainname
  • rabbit@172.17.0.4

在你嘗試着啓動第一個節點以前,你須要肯定容器之間能夠經過選取名字的方式互通。例如,Ping命令能夠訪問@符號後面的節點名稱。github

Erlang分佈式方案(RabbitMQ所基於的實現方式)能夠運行在兩種命名方案當中的一種:短節點名或者長節點名。區別的關鍵點在於:名字中若是存在「.」,就屬於長節點名;不然就是短節點名。在以上節點名舉例當中,第一個就屬於短節點名;第二個和第三個則屬於長節點名。docker

綜合以上要求,咱們能夠有如下節點命名規則以供選擇:json

  • 使用Pet集合(或者叫有狀態集合):咱們可使用相對固定的DNS名字。與通常能夠「丟棄」的副本出故障時能夠輕易的踢出集羣相對應,Pet集合是一組有狀態的Pod,每一個Pod有着能夠表示其狀態的標識符。
  • 使用IP地址以及一些具備自動發現集羣節點的工具(例如,autocluster插件可使RabbitMQ節點自動發現集羣子節點)。

以上的命名規則均須要採用長名字模式。可是DNS/節點名在K8S Pod內部配置的方式須要RabbitMQ 3.6.6之後版本才能夠支持。因此若是你採用這種方式,請確認你的RabbitMQ的版本。安全

Erlang的Cookie問題

第二個成功的關鍵問題是RabbitMQ節點須要共享一個密鑰Cookie。默認狀況下RabbitMQ從一個文件當中讀取這個Cookie(若是該文件不在,則自動生成一個)。爲確保該Cookie在全部節點一致,咱們有如下方案:cookie

  • 在製做Docker鏡像的時候生成該Cookie文件。這種方法並不推薦,由於該Cookie可讓你對整個RabbitMQ內部有完整的訪問權限。
  • 用一個Entrypoint腳本文件來生成該Cookie文件。生成時能夠用環境變量來傳輸密文。若是咱們還須要Entrypoint腳本文件來作其餘事情,這個方案和下一個方案均可以使用。
  • 經過環境變量的方式給RabbitMQ傳遞:
    RABBITMQ_CTL_ERL_ARGS=」-setcookie 」
    RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS=」-setcookie 」

集羣的缺陷

於RabbitMQ集羣另一個必須知道是事情是當一個節點加入集羣時,該節點的數據將會丟失,不管是什麼樣的數據。在常見的用例當中,這點可能可有可無。例如,當一個空節點加入到集羣當中的時候,這時候該節點不存在任何數據丟失的問題。可是,當咱們有兩個節點,他們已經相對獨立的運行過一段時間以後,而且已經累積了一些數據時。這時候咱們沒法在不損失任何數據的前提下將他們合併(注意:在網絡中斷或者節點宕機後回覆一個集羣都會有一樣的問題,一樣會有數據丟失)。對於特殊的數據,你可使用一些其餘的解決方案。例如,把須要重置的節點中的數據先備份出來。可是,目前沒有一個健壯的、自動的、全局的解決方案。網絡

因此咱們的自動化集羣解決方案受到咱們能容忍什麼樣的數據丟失這一方面的影響。app

Cluster formation集羣信息

假設你已經解決了全部命名規則相關的問題,而且用rabbitmqctl命令手工的創建了集羣。如今是把咱們的RabbitMQ封裝成自動化集羣的時候了。就像咱們之前遇到的問題同樣,沒有一個解決方案能夠解決全部的問題。dom

一個特定的解決方案只適合咱們並不關心服務中止或者連接丟失時數據丟失的問題。這種場景的一個例子就是當你使用RPC發送請求時,客戶端能夠在超時或者收到錯誤時重發請求。當服務恢復以後,RPC請求將再也不有效,相關數據也再也不有任何意義。幸運的是各個OpenStack組件之間的RPC調用正是這種狀況。

在思考過以上全部問題以後,如今咱們能夠搭建咱們本身的解決方案了。無狀態是咱們的首選,因此咱們選擇了IP地址而不是Pet集合。Autocluster插件將是咱們組織動態節點集羣的首選。

查看過Autocluster的文檔以後,咱們得出瞭如下配置項:

  • {backend, etcd}: 這幾乎是咱們惟一的選擇。Consul或者K8S能夠很好的工做。選擇它的惟一理由也是由於測試起來很簡單。你能夠下載etcd的二進制版本,不須要任何參數就能夠運行,這樣能夠搭建起一個基於本地的集羣。
  • {autocluster_failure, stop}:若是一個pod沒法加入集羣對於咱們來講就沒有任何價值。這個pod將會被移除集羣,並在一個相對安全的環境中重啓。
  • {cluster_cleanup, true}, {cleanup_interval, 30},{cleanup_warn_only, false}, {etcd_ttl, 15}:一個節點只有在徹底啓動,而且成功加入集羣以後才能夠在etcd中註冊。只要該節點還可用,註冊用的TTL將會不間斷的被更新。若是該節點停掉(或者因爲某種緣由沒法更新TTL),他將會被強制從集羣中刪除。即便該節點重啓後得到了相同的IP地址,它也會被認爲是從新加入集羣。

沒法預料的競爭

若是你嘗試着按照以上搭建了幾回集羣,你會發現有時整個集羣會分裂成幾個互相沒法聯通的小集羣。問題產生的緣由是啓動時對於競爭問題的惟一保護措施在節點啓動時會有隨機的延遲現象。在最壞的狀況下,每一個節點會認爲它是第一個節點(例如這時在etcd中沒有任何記錄),因此這個節點就以無集羣模式啓動了。

Autocluster最終爲該問題提出了一個很大的補丁。它在啓動的過程當中增長了鎖機制:節點在啓動時最早申請啓動鎖資源,最後在該節點成功在後臺註冊以後才釋放。目前只有etcd這個平臺支持該功能。可是其它平臺也能夠很容易的支持該功能(在後臺當中增長兩個新的回調函數)。

另外一個問題是:K8S、Pet集合他們能夠在啓動的時候進行編排工做;在任什麼時候間都只有一個節點處於啓動狀態。 該功能只處於初期測試階段。提供該功能的補丁不只僅提供給K8S的用戶,而是提供給全部平臺的開發者。

監控

目前惟一遺留的問題就是爲咱們運行在非看管狀態的集羣增長一個監控系統。咱們須要監控rabbitmq的健康情況以及它是否和集羣其它節點良好的工做在一塊兒。

你也許還記得之前能夠經過運行rabbitmqctl list_queues或者rabbitmqctl list_channels來監控。可是這種方案並不完美,由於它沒法區別本地和遠程的問題。同時它又明顯的增長了網絡負載。爲了彌補這個缺陷,3.6.4版本以後推出了新的、輕量級的rabbitmqctl node_health_check。這是檢查rabbitmq集羣當中單節點健康情況最好的方法。

檢查一個節點是否正確的加入集羣須要作幾方面的檢查:

  • 新加入的節點應當與Autocluster後臺最優節點在一塊兒註冊成集羣。這個最優節點是在註冊成功列表裏按字母順序排在最前面的節點。
  • 即便當節點已經與現有節點造成了集羣,但它的數據可能仍是分開的。對於這個檢查並非分開的,咱們須要在當前節點和新發現節點都檢查分區列表。

全部的這些能夠經過獨立的檢查完成,檢查可使用如下命令:
rabbitmqctl eval ‘autocluster:cluster_health_check_report().’

使用rabbitmqctl命令咱們能夠檢測出rabbitmq節點的任何問題,也能夠經過該命令將該節點停掉。所以,K8S能夠有機會施展魔法重啓該節點。

搭建你本身的RabbitMQ集羣

若是你本身親自按照這個方案搭建這個集羣,你須要最新版本的RabbitMQ以及autocluster插件的定製化版本(目前啓動鎖機制這個補丁尚未合併到主版本當中)。

你能夠查看Fuel CCP如何搭建集羣,或者用你本身的實現方法使用獨立的版原本搭建。

爲了提示你該方案案如何實施,讓咱們假設你已經複製了第二個repository,而且你已經有了一個叫作「demo」的K8S的命名空間。Etcd的服務已經在K8S集羣中運行,而且能夠經過」etcd」這個名字訪問。你能夠經過如下命令來搭建環境:

kubectl create namespace demo kubectl run etcd --image=microbox/etcd --port=4001 \ --namespace=demo -- --name etcd kubectl --namespace=demo expose deployment etcd

完成以上步驟後,用如下命令搭建RabbitMQ:

1. 用合適的RabbitMQ和Autocluster版本建立Docker鏡像,並設置相應配置文件。

$ docker build . -t rabbitmq-autocluster

2. 保存Erlang的Cookie到K8S當中。

$ kubectl create secret generic --namespace=demo erlang.cookie \ --from-file=./erlang.cookie

3. 建立3節點RabbitMQ集羣。爲了簡化操做,大家能夠從如下連接下載rabbitmq.yaml文件https://github.com/binarin/rabbit-on-k8s-standalone/blob/master/rabbitmq.yaml.

$ kubectl create -f rabbitmq.yaml

4. 檢查集羣是否正常工做

$ FIRST_POD=$(kubectl get pods --namespace demo -l 'app=rabbitmq' \ -o jsonpath='{.items[0].metadata.name }') $ kubectl exec --namespace=demo $FIRST_POD rabbitmqctl \ cluster_status

你應該獲得以下輸出:

Cluster status of node 'rabbit@172.17.0.3' ... [{nodes,[{disc,['rabbit@172.17.0.3','rabbit@172.17.0.4',                'rabbit@172.17.0.7']}]}, {running_nodes,['rabbit@172.17.0.4','rabbit@172.17.0.7','rabbit@172.17.0.3']}, {cluster_name,<<"rabbit@rabbitmq-deployment-861116474-cmshz">>}, {partitions,[]}, {alarms,[{'rabbit@172.17.0.4',[]},          {'rabbit@172.17.0.7',[]},          {'rabbit@172.17.0.3',[]}]}]

這裏最關鍵的一點是nodes與running nodes集合均有三個節點

相關文章
相關標籤/搜索