目錄
聲明
摘要
多節點集羣
參考文獻
聲明
官網的文檔一直在變化,可能翻譯時的文檔跟目前文檔有出入,請以官網文檔爲準。
摘要
集羣是使多個進程和程序作爲一個整體工作的機制。舉例,當在google.com網站上查詢資料時,查詢請求好像只有一臺web服務器在處理。實際上,查詢請求是被一個集羣中多臺相互連接的web服務器處理的。類似地,OpenDaylight也可以有多個實例作爲一個整體對外提供服務。
集羣的優勢:
- 可伸縮性:如果有多個OpenDaylight實例在運行,一般情況下比僅有一臺實例會做更多的任務和存儲更多的數據。也可以把數據分解成塊(片),然後把數據散佈到整個集羣,或者在集羣中的某個實例執行某個操作。
高可用性:如果有多個OpenDaylight實例在運行,其中一個實例宕機,其它實例仍然可以工作和提供服務。
數據持久化:手動重啓或者宕機,不會丟失存儲在OpenDaylight的任何數據.
下面這部分描述如何搭建集羣。
多節點點集羣
以下部分描述瞭如何用OpenDaylight搭建多節點集羣。
部署注意事項
實現集羣,需要注意如下事項:
推薦最少3臺機子來搭建多節點集羣。也可以搭建只有兩個節點的集羣。但是,如果2臺中有一臺宕機,則集羣就不可用。
注意:這是因爲OpenDaylight要求大部分節點需要運行,而一個節點顯然不能稱作是大部分節點。
集羣中的每個實例都需要有一個標識符。OpenDaylight通過使用節點role來達到這個目的。當在akka.conf文件中定義第一個節點的role爲member-1時,OpenDaylight就使用member-1來標識該節點。
數據片(譯註:這個概念對於文章理解很重要,請查看wiki Share)用於存儲OpenDaylight MD-SAL數據庫所有數據或者某個片段的數據。例如,一個數據片可以存儲所有的inventory數據,而另外一個數據片可以存儲所有的topology數據。
如果沒有在modules.conf文件中指定一個module,且在module-shards.conf文件中也沒有指定一個分片。默認所有的數據存放在default數據片中(該default分片必須在module-shares.conf文件預先定義好)。每個分片都有一個可配置的replicas。在module-shares.conf中可以指定replicas詳情,replicas表示分片位於哪個節點(譯註:比如memeber-1,memeber-2)。
如果有一個3節點的集羣,想要能夠容忍任意節點的宕機,那麼每個數據片的副本必須運行在3節點的集羣上。(譯註:意思是如果想在集羣中某臺機子宕機的情況下還能正常服務,沒有數據缺失,那麼你必須把每一個數據片複製到3個節點上)
注意:這是因爲OpenDaylight的集羣實現要求大部分數據片都運行才能提供服務。如果只在集羣中的2個節點定義數據片副本(譯註:即該數據片只有兩個複製)且其中一個節點宕機,相應的該數據片無法運作(譯註:一個數據片無法成爲大部分。雖然還有一個節點正常運行,但是也不運行的意思)
如果有3個節點的集羣且某個數據片在所有節點上定義副本,即使該集羣只剩下兩個節點在運行,該數據片仍然能夠運行。注意如果剩下的兩個節點又宕機一個,則該數據片將不可操作。
推薦配置多個節點。集羣中的某個節點啓動,它會發送一個消息給所有的節點。然後該節點會發送一個加入命令給第一個響應它的節點。如果沒有節點回應,該節點重複這個過程直到成功建立一個連接或者自己關閉這個過程。
集羣中某個節點持續一段時間(默認10秒,可配置)還是不可達後(譯註:不能連接),該節點自動下線。一旦節點下線,你需要重新啓動它才能再次加入集羣。一旦重新啓動的節點加入集羣,它將自動從主節點同步數據。
集羣腳本
OpenDaylight包含一些用於配置集羣的腳本。
注意:腳本是位於OpenDaylight distribution/bin 目錄下,該腳本在倉庫的位置是distribution-karaf/src/main/assembly/bin/
使用集羣腳本
這個腳本被用於設置控制器集羣中某個節點的集羣參數(指akka.conf,module-shares.conf中的參數)。用戶重啓節點才能生效設置的參數。
注意:這個腳本可以在任意時刻使用,甚至在控制器第一次被啓動之前。
用法:
bin/configure_cluster.sh <index> <seed_nodes_list>
index:1..N之間的整數,N是節點的數量。這個表示腳本配置的是哪一個控制器節點。
seed_nodes_list:集羣中所有節點組成的ip地址列表,用逗號或者空格分隔。
index指向的IP地址應該是正在執行腳本的節點的ip地址。當在多個節點運行這個腳本時,保證seed_node_list這個參數值一樣,而index在1到N之間改變。
可選,數據片可以通過修改“custom_shard_configs.txt”文件,在更多粒度進行配置,該文件和該腳本在同一個目錄下。想要更多信息可以查看此文件。
舉例:
bin/configure_cluster.sh 2 192.168.0.1 192.168.0.2 192.168.0.3
上面的命令將配置節點2(ip地址192.168.0.2),該節點位於由192.168.0.1 192.168.0.2 192.168.0.3三臺機子組成的集羣中
搭建多節點集羣
譯註:這裏需要注意一下步驟順序,第10個步驟需要移到第2個步驟的下面進行,不然你將找不到第3步所說的配置文件。即你需要先啓動karaf,然後運行feature:install odl-mdsal-clustering,退出karaf命令行界面後,在繼續進行下面的步驟。
爲了運行3節點集羣的OpenDaylight,執行如下步驟:
首先選擇3臺機子用於搭建集羣。之後在每臺機子下做如下操作:
1.拷貝OpenDaylight發佈文件zip(譯註:linux爲tar.bz2)到機子上。
2.解壓發佈版本文件。
3.打開下面的.conf文件:
configuration/initial/akka.conf configuration/initial/module-shards.conf
4.每個配置文件做如下修改(譯註:這裏說的是akka.conf這個文件):
每個文件找到如下行的情況,然後用該配置文件所在機子即OpenDaylight運行所在機子的主機名或者ip地址替換127.0.0.1:
netty.tcp { hostname = "127.0.0.1"
注意:對於集羣中的每個節點該值是不一樣的。
5.找到如下所示的行,然後用集羣中的所有節點的機子的主機名或ip地址替換127.0.0.1(譯註:這裏的意思是集羣有幾個節點,數組中就有幾個記錄,只是ip做相應的修改):
cluster { seed-nodes = ["akka.tcp://[email protected]:2550"]
6.找到如下部分,給每一個節點指定一個角色名。這裏我們用member-1賦值給第一個節點,第二節點用member-2,第三個節點用member-3:
roles = [ "member-1" ]
注意:這個步驟,在每個節點應該使用不同的節點名。
7.打開configuration/initial/module-shards.conf文件,修改replicas屬性,讓每一個數據片複製到所有的節點
replicas = [ "member-1", "member-2", "member-3" ]
僅供參考,可以查看下面給出的==配置文件示例==
8.切換到/bin目錄下。
9.運行如下命令:
JAVA_MAX_MEM=4G JAVA_MAX_PERM_MEM=512m ./karaf
10.在karaf命令行中運行如下命令啓用集羣功能(譯註:這步移到第三步)
feature:install odl-mdsal-clustering
OpenDaylight此時應該運行在3個節點的集羣上。你可以通過任意的節點訪問存儲在數據庫中的數據。
配置文件示例
==akka.conf== 示例文件:
odl-cluster-data {
bounded-mailbox {
mailbox-type = "org.opendaylight.controller.cluster.common.actor.MeteredBoundedMailbox"
mailbox-capacity = 1000
mailbox-push-timeout-time = 100ms
}
metric-capture-enabled = true
akka {
loglevel = "DEBUG"
loggers = ["akka.event.slf4j.Slf4jLogger"]
actor {
provider = "akka.cluster.ClusterActorRefProvider"
serializers {
java = "akka.serialization.JavaSerializer"
proto = "akka.remote.serialization.ProtobufSerializer"
}
serialization-bindings {
"com.google.protobuf.Message" = proto
}
}
remote {
log-remote-lifecycle-events = off
netty.tcp {
hostname = "10.194.189.96"
port = 2550
maximum-frame-size = 419430400
send-buffer-size = 52428800
receive-buffer-size = 52428800
}
}
cluster {
seed-nodes = ["akka.tcp://opendaylight-cluster-data@10.194.189.96:2550",
"akka.tcp://opendaylight-cluster-data@10.194.189.98:2550",
"akka.tcp://opendaylight-cluster-data@10.194.189.101:2550"]
auto-down-unreachable-after = 10s
roles = [
"member-2"
]
}
}
}
odl-cluster-rpc {
bounded-mailbox {
mailbox-type = "org.opendaylight.controller.cluster.common.actor.MeteredBoundedMailbox"
mailbox-capacity = 1000
mailbox-push-timeout-time = 100ms
}
metric-capture-enabled = true
akka {
loglevel = "INFO"
loggers = ["akka.event.slf4j.Slf4jLogger"]
actor {
provider = "akka.cluster.ClusterActorRefProvider"
}
remote {
log-remote-lifecycle-events = off
netty.tcp {
hostname = "10.194.189.96"
port = 2551
}
}
cluster {
seed-nodes = ["akka.tcp://opendaylight-cluster-rpc@10.194.189.96:2551"]
auto-down-unreachable-after = 10s
}
}
}
==module-shards.conf== 示例文件:
module-shards = [
{
name = "default"
shards = [
{
name="default"
replicas = [
"member-1",
"member-2",
"member-3"
]
}
]
},
{
name = "topology"
shards = [
{
name="topology"
replicas = [
"member-1",
"member-2",
"member-3"
]
}
]
},
{
name = "inventory"
shards = [
{
name="inventory"
replicas = [
"member-1",
"member-2",
"member-3"
]
}
]
},
{
name = "toaster"
shards = [
{
name="toaster"
replicas = [
"member-1",
"member-2",
"member-3"
]
}
]
}
]
集羣監控
OpenDaylight通過MBeans暴露數據片信息,可以通過JConsole,VisualVM或者其他JMX客戶端來查看數據片信息,也可以通過使用Jolokia的REST API來暴露數據片信息,該API是安裝karaf feature ==odl-jolokia==組件纔有。
列出所有的可用的MBeans方案的URI:
GET /jolokia/list
使用下面REST,查詢OpenDaylight實例的本地數據片信息。
比如config數據庫信息:
GET /jolokia/read/org.opendaylight.controller:type=DistributedConfigDatastore,Category=ShardManager,name=shard-manager-config
比如operational數據庫信息:
GET /jolokia/read/org.opendaylight.controller:type=DistributedOperationalDatastore,Category=ShardManager,name=shard-manager-operational
以下輸出顯示了該節點數據片信息:
{
"request": {
"mbean": "org.opendaylight.controller:Category=ShardManager,name=shard-manager-operational,type=DistributedOperationalDatastore",
"type": "read"
},
"value": {
"LocalShards": [
"member-1-shard-default-operational",
"member-1-shard-entity-ownership-operational",
"member-1-shard-topology-operational",
"member-1-shard-inventory-operational",
"member-1-shard-toaster-operational"
],
"SyncStatus": true,
"MemberName": "member-1"
},
"timestamp": 1483738005,
"status": 200
}
當需要進一步的查看來自“LocalShards”列表中的某個分片信息,可以把該分片的完整名稱當作URI的一部分,來查詢該分片的詳細信息。如下是member-1-shard-default-operational信息輸出的例子:
{
"request": {
"mbean": "org.opendaylight.controller:Category=Shards,name=member-1-shard-default-operational,type=DistributedOperationalDatastore",
"type": "read"
},
"value": {
"ReadWriteTransactionCount": 0,
"SnapshotIndex": 4,
"InMemoryJournalLogSize": 1,
"ReplicatedToAllIndex": 4,
"Leader": "member-1-shard-default-operational",
"LastIndex": 5,
"RaftState": "Leader",
"LastCommittedTransactionTime": "2017-01-06 13:19:00.135",
"LastApplied": 5,
"LastLeadershipChangeTime": "2017-01-06 13:18:37.605",
"LastLogIndex": 5,
"PeerAddresses": "member-3-shard-default-operational: akka.tcp://[email protected]:2550/user/shardmanager-operational/member-3-shard-default-operational, member-2-shard-default-operational: akka.tcp://[email protected]:2550/user/shardmanager-operational/member-2-shard-default-operational",
"WriteOnlyTransactionCount": 0,
"FollowerInitialSyncStatus": false,
"FollowerInfo": [
{
"timeSinceLastActivity": "00:00:00.320",
"active": true,
"matchIndex": 5,
"voting": true,
"id": "member-3-shard-default-operational",
"nextIndex": 6
},
{
"timeSinceLastActivity": "00:00:00.320",
"active": true,
"matchIndex": 5,
"voting": true,
"id": "member-2-shard-default-operational",
"nextIndex": 6
}
],
"FailedReadTransactionsCount": 0,
"StatRetrievalTime": "810.5 μs",
"Voting": true,
"CurrentTerm": 1,
"LastTerm": 1,
"FailedTransactionsCount": 0,
"PendingTxCommitQueueSize": 0,
"VotedFor": "member-1-shard-default-operational",
"SnapshotCaptureInitiated": false,
"CommittedTransactionsCount": 6,
"TxCohortCacheSize": 0,
"PeerVotingStates": "member-3-shard-default-operational: true, member-2-shard-default-operational: true",
"LastLogTerm": 1,
"StatRetrievalError": null,
"CommitIndex": 5,
"SnapshotTerm": 1,
"AbortTransactionsCount": 0,
"ReadOnlyTransactionCount": 0,
"ShardName": "member-1-shard-default-operational",
"LeadershipChangeCount": 1,
"InMemoryJournalDataSize": 450
},
"timestamp": 1483740350,
"status": 200
}
輸出信息標識分片狀態(主/從,投票中/非投票中)、同級節點信息、從節點信息,分片是否是主節點、其他統計信息和計數信息。
集成團隊正在維護一個基於Python開發的工具,這個工具通過Jolokia組件,使用了上文提到MBeans暴露的接口,且systemmetrics項目也提供了一個基於DLUX UI來顯示相同的信息。
空間分佈式的active/backup設置
節點之間的延遲時間最小的時候OpenDaylight集羣工作在最佳狀態,實踐表明它們應該在同一個數據中心(譯註:指在同一個機房)。但是假設所有節點宕機,能夠在不同的數據中心實現故障轉移更滿足需求。爲了獲的這種效果,在不影響主數據中心節點(譯註:這裏應該是主數據中心,即同一個機房的所有節點)的延遲時間的方法下,集羣可以通過在不同數據中心擴展節點的方式來達到目的。如果這樣做,備份數據中心節點必須處在“non-voting” 狀態。
在controller項目cluster-admin.yang中定義了一個RPCs,用於修改分片的voting狀態的API;該API已經很好地被文檔化。該API的概要如下:
注意:除非另外指出,下面的POST請求都被髮送到集羣中的任意一個節點。
創建一個6個節點集羣的active/backup設置(分別位於兩個機房,3個活躍和3個備份節點)。這裏有一個RPC可以根據給定狀態設置指定節點中的所有分片的投票狀態:
POST /restconf/operations/cluster-admin:change-member-voting-states-for-all-shards
這個RPC需要一個節點列表和期望的投票狀態作爲輸入。爲了創建備份節點,如下輸入示例可以被使用:
{
"input": {
"member-voting-state": [
{
"member-name": "member-4",
"voting": false
},
{
"member-name": "member-5",
"voting": false
},
{
"member-name": "member-6",
"voting": false
}
]
}
}
當一個active/backup部署存在時,且備份節點處於non-voting狀態,這些被用於從active子集羣到backup子集羣的故障轉移的分片將翻轉每一個分片(包含每一個節點,活躍和備份)的投票狀態。通過下面的RPC調用可以很容易實現這個過程(無需參數):
POST /restconf/operations/cluster-admin:flip-member-voting-states-for-all-shards
如果是主投票節點(譯註:這裏應該指的是active數據中心機房)因意外斷電導致宕機,“flip” RPC請求必須被髮送到某個非投票狀態的備份節點(譯註:backup數據中心機房的某個節點)。這種情況下,沒有分片進行投票選舉。但是這裏有一個特例,如果收到RPC請求的這個節點處在non-voting狀態,且即將改變爲voting狀態,且沒有主節點,則這個節點將狀態改爲voting並嘗試成爲主節點。如果成功,該節點保存狀態並複製這些變動信息給其餘的節點。
當主中心被修復且你想要自動恢復到之前狀態,你必須謹慎對待主中心的恢復。因爲次級中心的投票狀態被翻轉的時候,主中心是處於下線狀態,主中心的數據庫並沒有保存這些變化。如果主中心在這種狀態被恢復,主中心的節點將仍然處於投票狀態。如果主中心節點連接到次級中心,它們找出次級中心的主節點並同步。但是如果沒有發生這種情況,主中心將可能選舉出它們自己的主節點,從而被分割出2個集羣,這種情況會導致不可預測的結果。因此推薦在恢復主中心時,先清除主中心節點數據庫數據(例如,日誌和快照目錄)。另外一種選擇是從次級中心一個最新備份中恢復主中心的數據(詳情查閱Backing Up and Restoring the Datastore)
如果想要從集羣中溫和的移除一個節點也是可以的,查看如下RPC API:
POST /restconf/operations/cluster-admin:remove-all-shard-replicas
請求的示例數據:
{
"input": {
"member-name": "member-1"
}
}
或者僅僅是移除一個特定的分片:
POST /restconf/operations/cluster-admin:remove-shard-replica
請求的實例數據:
{
"input": {
"shard-name": "default",
"member-name": "member-2",
"data-store-type": "config"
}
}
注意在運行時某個故障節點被移除,也可以添加一個新的節點,不用修改正常節點的配置文件(需要重啓)。
POST /restconf/operations/cluster-admin:add-replicas-for-all-shards
該請求不需要任何請求數據,但是這個RPC需要被髮送到新的節點,指示該節點去集羣中複製所有分片數據。
注意
雖然集羣admin API允許動態地添加和移除分片,但是module-shard.conf和modules.conf文件仍然使用啓動時定義的初始化配置。使用API修改並不會存儲到這些靜態文件中,但是會存到journal中。