基於Kubernetes(k8s)的RabbitMQ 集羣

目前,有很多種基於Kubernetes搭建RabbitMQ集羣的解決方案。今天筆者今天將要討論我們在Fuel CCP項目當中所採用的方式。這種方式加以轉變也適用於搭建RabbitMQ集羣的一般方法。所以如果你想要設計自己的解決方案,你應該收集一些更符合你定製化需求的文章。

命名你的集羣

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

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

Erlang分佈式方案(RabbitMQ所基於的實現方式)可以運行在兩種命名方案當中的一種:短節點名或者長節點名。區別的關鍵點在於:名字中如果存在“.”,就屬於長節點名;否則就是短節點名。在以上節點名舉例當中,第一個就屬於短節點名;第二個和第三個則屬於長節點名。

綜合以上要求,我們可以有以下節點命名規則以供選擇:

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

以上的命名規則均需要採用長名字模式。但是DNS/節點名在K8S Pod內部配置的方式需要RabbitMQ 3.6.6以後版本纔可以支持。所以如果你採用這種方式,請確認你的RabbitMQ的版本。

Erlang的Cookie問題

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

  • 在製作Docker鏡像的時候生成該Cookie文件。這種方法並不推薦,因爲該Cookie可以讓你對整個RabbitMQ內部有完整的訪問權限。
  • 用一個Entrypoint腳本文件來生成該Cookie文件。生成時可以用環境變量來傳輸密文。如果我們還需要Entrypoint腳本文件來做其他事情,這個方案和下一個方案都可以使用。
  • 通過環境變量的方式給RabbitMQ傳遞:
    RABBITMQ_CTL_ERL_ARGS=”-setcookie ”
    RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS=”-setcookie ”

集羣的缺陷

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

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

Cluster formation集羣信息

假設你已經解決了所有命名規則相關的問題,並且用rabbitmqctl命令手工的建立了集羣。現在是把我們的RabbitMQ封裝成自動化集羣的時候了。就像我們以前遇到的問題一樣,沒有一個解決方案可以解決所有的問題。

一個特定的解決方案只適合我們並不關心服務停止或者鏈接丟失時數據丟失的問題。這種場景的一個例子就是當你使用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 '[email protected]' ... [{nodes,[{disc,['[email protected]','[email protected]', '[email protected]']}]}, {running_nodes,['[email protected]','[email protected]','[email protected]']}, {cluster_name,<<"rabbit@rabbitmq-deployment-861116474-cmshz">>}, {partitions,[]}, {alarms,[{'[email protected]',[]}, {'[email protected]',[]}, {'[email protected]',[]}]}]

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

本文轉自中文社區-基於Kubernetes(k8s)的RabbitMQ 集羣

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章