理解 OpenStack Swift (2):架构、原理及功能 [Architecture, Implementation and Features]...
本系列文章著重學習和研究OpenStack Swift,包括環境搭建、原理、架構、監控和性能等。
(1)OpenStack + 三節點Swift 集群+ HAProxy + UCARP 安裝和配置?
(2)原理、架構和性能
(3)監控
1. 架構
1.1 總體架構
Swift 的總體架構非常的清晰和獨立:
| # | 分層(Tier) | 組件(Service) | 功能(Function) | 特性 | 部署考量 |
| 1 | 訪問層(Access Tier) | Load Balancer | 硬件(比如F5)或者軟件(比如HAProxy)負載均衡器,將客戶端的請求按照配置的策略分配到無狀態的 proxy service。 | ? | ?按照實際需求選擇硬件還是軟件LB |
| 2 | Proxy Server |
| 是?CPU 和網絡帶寬敏感的 |
| |
| 3 | 存儲層 (Capactity Tier) | Account?Server | 提供 Account 操作 REST API | 是磁盤性能和網絡帶敏感的 |
|
| 4 | Container Server | 提供 Container 操作 REST API | |||
| 5 | Object Server | 提供 Object 操作 REST API | |||
| ?6 | Consistency Servers | 包括?Replicators, Updaters 和 Auditors 等后臺服務,用于保證 object 的一致性 |
這是一張比較經典的 Swift 物理部署圖:
1.2 網絡架構
以一個對外提供對象存儲服務的集群為例,其網絡架構可以為:
- 外部流量被放在一個單獨的(上圖中紫色)VLAN 中,終點為 LB
- 控制(管理)網絡連接所有節點
- Swift 前端(front end / public)網絡連接 LB 和 所有 Proxy server 節點
- Swift 后端 (backend / private) 網絡連接所有 Proxy server 節點和 存儲節點
- 需要的話,還可以從后端網絡中分離出復制(replication)網絡
在網絡帶寬選擇上,
- 考慮到復制數據的容量較大(往往是幾個TB起步),后端網絡往往是用 10GbE 網絡
- 根據前端負載,前端網絡可以使用 1GbE 網絡,或者有條件時使用 10GbE 網絡
- 管理/IPMI網絡往往是用 1GbE 網絡
這是 SwiftStack 的一個例子:
2. 數據存放
2.1 Swift 的數據存放
2.1.1 Swift 的數據模型
Swift 的數據模型使用了以下三個概念來(見下圖1):
- Account: 賬戶/租戶。Swift 是天生支持多租戶的。如果使用 OpenStack Keystone 做用戶校驗的話,account 與 OpenStack project/tenant 的概念相同。Swift 租戶的隔離性體現在metadata上,而不是體現在 object data 上。數據包括自身元數據 和 container 列表,被保存在 SQLite 數據庫中。
- Container: 容器,類似于文件系統中的目錄,由用戶自定義,它包含自身的元數據和容器內的對象列表。數據保存在 SQLite 數據庫中。在新版中,Swift 支持在容器內添加文件夾。
- Object: 對象,包括數據和數據的元數據,以文件形式保存在文件系統上。
? ? ?
?(圖1) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (圖2)
?
- Containers 是用戶創建的,用來 hold objects。
- objects 可以是 0 bytes 長度,或者包含數據。
- container 中的 object 最大大小為 5GB;超過的話,會做特殊處理
- 每個 object 使用它的 name 來被 referenced;Swift 沒有目錄概念
- 在 object name 中可以使用任意的可以被 ‘URL-encoded’ 的?字符,最大長度為 URL - coded 之后 1034 個字符
- object name 中可以帶 '/' 字符,它會帶來目錄結構的幻覺(illusion),比如?dir/dir2/name。即使看起來象個目錄,但是它仍然只是一個 object name。此時,不需要 dir 或者 dir/dir2 container 的存在。
- 如果一個 container 所有 objects 的大小為0,那么它將看起來象一個目錄。
- 客戶端使用 HTTP 或者 HTTPS 訪問 Swift,包括讀、寫、刪除 objects。也支持 COPY 操作,它會創建一個新的 object,使用一個新的 object name,包含老 object 的 data。沒有 rename 操作,它會首先 copy 出一個新的,然后再將老的刪除。
2.1.2 選擇數據存放位置
? Swift 保存每個對象為多分拷貝,它按照物理位置的特點,盡量將這些拷貝放在不同的物理位置上,來保證數據的地理位置上的可靠性。它主要考慮以下幾種位置屬性:
- Region:地理位置上的區域,比如不同城市甚至不同國家的機房,這主要是從災備方面考慮的。
- Zone:一個數據中心根據物理網絡、供電、空調等基礎設施分開的獨立的域,往往將一個機架(Rack)內的服務器分在一個 Zone 內。
- Node (節點):物理服務器
- Disk (磁盤):物理服務器上的磁盤
Swift 在確定對象的放置位置時,會盡量將對象及其拷貝放在不會同時損失的物理位置上。見上圖2.
2.1.3 保證數據一致性
? ? 對象及其拷貝放置在某個磁盤上后,Swift 會使用Replicators, Updaters 和 Auditors 等后臺服務來保證其數據的最終一致性。
- Replicator – 拷貝對象,確保系統的最終一致性(Replicate objects and make a system in a consistent state);恢復磁盤和網絡錯誤(Recover disk failure, network outages situation)
- Updater – 更新元數據(Update metadata),從容器和賬戶元數據高負載導致的問題上恢復(Recover failure caused by container, account metadata high load)
- Auditor – 刪除問題賬戶,容器和對象,然后從別的服務器上拷貝過來(Delete problematic account, container or objects and replicate from other server);恢復數據庫和文件數據錯誤(Recover dbs or files which have bit rot problem.
其中,Replicator 服務以可配置的間隔來周期性啟動,默認是 30s,它以 replication 為最小單位,以 node 為范圍,周期性地執行數據拷貝。詳細過程請參考文末的參考文檔。考慮到Swift 實現的是最終一致性而非強一致性,它不合適于需要數據強一致性的應用,比如銀行存款和訂票系統等。需要做 replication 的情形包括但不限于:
- Proxy server 在寫入第三份時失敗,它依然會向客戶端返回成功,后臺服務會寫第三份拷貝
- 后臺進程發現某個replication 數據出現損壞,它會在新的位置重新寫入
- 在跨 Region 的情況下,Proxy server 只會向它所在 Region 的存儲上寫入,遠處 region 上的數據由后臺進程復雜寫入
- 在更換磁盤或者添加磁盤的情況下,數據需要重新平衡時
2.2 Swift ?是如何實現這些需求的:使用 Ring + 哈希算法
? ? Swift 根據由管理員配置的 Ring 使用相對簡單直接的算法來確定對象的存放位置。對象會以文件的形式保存在本地文件系統中,使用文件的擴展屬性來保存對象的元數據,因此,Swift 需要支持擴展屬性的文件系統,目前官方推薦是 XFS。
2.2.1 Ring 的內容和算法
? ? 簡單來說,Swift 的 Proxy Server 根據account,container 和 object 各自的 Ring 來確定各自數據的存放位置,其中 account 和 container 數據庫文件也是被當作對象來處理的。
因此,Swift 需要 Ring 的配置文件分布在所有 Proxy節點上。同時,Consistency Servers 需要它來確定后臺對象拷貝的位置,因此也需要部署在所有存儲節點上。Ring 以文件的形式保存:
- object.ring.gz
- container.ring.gz
- account.ring.gz
在分析 Ring 是如何工作的之前,先來看看 Ring 的幾個關鍵配置:
- Region,zone 和 disk:前面說過了,略過
- partition:Swift 再將每個磁盤分成若干 partition (分區)。這是后端一致性檢查服務處理拷貝(replication)的基本單位。
- Replica:對象和拷貝的總份數,常規推薦值是 3。
管理員使用 Swift 提供的 ring 生成工具(swift-ring-builder,位于源碼的bin目錄下,是swift最基本的命令,它與swift/common/ring/下的文件一起實現ring文件創建,添加,平衡,等操作),加上各種配置參數,得出該ring的內容。以 Object ring 為例,
root@swift1:/etc/swift# swift-ring-builder object.builder object.builder, build version 6 1024 partitions, 3.000000 replicas, 1 regions, 3 zones, 6 devices, 0.00 balance, 0.00 dispersion The minimum number of hours before a partition can be reassigned is 1 The overload factor is 0.00% (0.000000) Devices: id region zone ip address port replication ip replication port name weight partitions balance meta0 1 1 9.115.251.235 6000 9.115.251.235 6000 sdb1 100.00 512 0.001 1 1 9.115.251.235 6000 9.115.251.235 6000 sdc1 100.00 512 0.002 1 2 9.115.251.234 6000 9.115.251.234 6000 sdb1 100.00 512 0.003 1 2 9.115.251.234 6000 9.115.251.234 6000 sdc1 100.00 512 0.004 1 3 9.115.251.233 6000 9.115.251.233 6000 sdb1 100.00 512 0.005 1 3 9.115.251.233 6000 9.115.251.233 6000 sdc1 100.00 512 0.00該 Ring 的配置為:1 個 region,3 個 zone,3 個 node,6 個磁盤,每個磁盤上 512 個分區。
內部實現上,Swift 將該 Ring 的配置保存在其?_replica2part2dev?數據結構中:
其讀法是:
- 行:將集群所有的分區都順序編號,每個分區有唯一的一個ID
- 列:包含 Ring 中的 id,該 id 唯一確定了一個 disk;和 replica 的編號。
因此,Swift 通過該數據結構可以方便地查到某個 replica 應該通過哪些節點上的存儲服務放在哪個 disk 上。
除了生成 Ring 外,對 Ring 的另一個重要操作是 rebalance(再平衡)。在你修改builder文件后(例如增減設備),該操作會重新生成ring文件,使系統中的partition分布平衡。當然,在 rebalance 后,需要重新啟動系統的各個服務。 詳情可以參考?OpenStack Swift源碼分析(二)ring文件的生成。
2.2.2 數據放置和讀取過程
當收到一個需要保存的 object 的 PUT 請求時,Proxy server 會:
?
當 Proxy server 收到一個獲取對象的 GET 請求時,它:
(1)(2)(3)(4)同前面的 PUT 請求,確定存放所有 replica 的 所有磁盤
(5)排序這些 nodes,嘗試連接第一個,如果成功,則將二進制數據返回客戶端;如果不成功,則嘗試下一個。直到成功或者都失敗。
應該說該過程蠻簡單直接,這也符合Swift的總體設計風格。至于具體的哈希算法實現,有興趣可以看相關論文。大致來說,它實現的是 “unique-as-possible” 即 “盡量唯一” 的算法,按照 Zone,Node 和 Disk 的順序。對于一個 replica,Swift 首先會去選擇一個沒有該對象 replica 的 zone,如果沒有這樣的 zone,選擇一個已使用 zone 中的沒用過的 node,如果沒有這樣的 node,就選擇已使用的 node 上的一個沒使用過的 disk。
2.2.3 Hash 計算和對象位置查找
(1)存放的目錄取決于哈希值
這里也表明,對象的存放目錄和其名稱是直接關聯的。因此,在Swift中的,對對象重命名,意味著對象位置的修改,該過程會產生數據拷貝,而且在很多時候是需要跨節點的遠程拷貝。在某些應用中,比如 Hadoop 大數據應用,如果采用 Swift 作為存儲,在其mapreduce 過程中,是會產生文件 rename 操作的,這在 Swift 中會帶來嚴重的性能下降。
(2)獲取某對象的存放路徑
root@swift1:~/s1# swift-get-nodes /etc/swift/object.ring.gz AUTH_dea8b51d28bf41599e63464828102759/container1/1Account AUTH_dea8b51d28bf41599e63464828102759 Container container1 Object 1 Partition 277 Hash 456a95e2e66aad55d72756c6b0cd3b75Server:Port Device 9.115.251.235:6000 sdb1 Server:Port Device 9.115.251.234:6000 sdc1 Server:Port Device 9.115.251.233:6000 sdc1curl -I -XHEAD "http://9.115.251.235:6000/sdb1/277/AUTH_dea8b51d28bf41599e63464828102759/container1/1" curl -I -XHEAD "http://9.115.251.234:6000/sdc1/277/AUTH_dea8b51d28bf41599e63464828102759/container1/1" curl -I -XHEAD "http://9.115.251.233:6000/sdc1/277/AUTH_dea8b51d28bf41599e63464828102759/container1/1"(3)遠程登錄后者 ssh 后可以看到保存對象的文件
root@swift1:~/s1# ls /srv/node/sdb1/objects/277/b75/456a95e2e66aad55d72756c6b0cd3b75 -l total 8 -rw------- 1 swift swift 12 Nov 8 17:17 1447003035.84393.dataroot@swift1:~/s1# cat /srv/node/sdb1/objects/277/b75/456a95e2e66aad55d72756c6b0cd3b75/1447003035.84393.data 2222222222222.2.4 對象分段
? Swift 對于小的文件,是不分段直接存放的;對于大的文件(大小閾值可以配置,默認是 5G),系統會自動將其分段存放。用戶也可以指定分段的大小來存放文件。比如對于 590M 的文件,設置分段大小為 100M,則會被分為 6 段被并行的(in parallel)上傳到集群之中:
root@controller:~/s1# swift upload container1 -S 100000000 tmpubuntu tmpubuntu segment 5 tmpubuntu segment 2 tmpubuntu segment 4 tmpubuntu segment 1 tmpubuntu segment 3 tmpubuntu segment 0 tmpubuntu root@controller:~/s1# swift list container1 1 admin-openrc.sh cirros-0.3.4-x86_64-disk.raw tmpubuntu從 stat 中可以看出它使用一個 manifest 文件來保存分段信息:
root@controller:~/s1# swift stat container1 tmpubuntuAccount: AUTH_dea8b51d28bf41599e63464828102759Container: container1Object: tmpubuntuContent Type: application/octet-stream Content Length: 591396864Last Modified: Fri, 13 Nov 2015 18:31:53 GMTETag: "fa561512dcd31b21841fbc9dbace118f"Manifest: container1_segments/tmpubuntu/1446907333.484258/591396864/100000000/Meta Mtime: 1446907333.484258Accept-Ranges: bytesConnection: keep-aliveX-Timestamp: 1447439512.09744X-Trans-Id: txae548b4b35184c71aa396-0056462d72但是 list 的時候依然只看到一個文件,原因是因為 manifest 文件被保存到一個獨立的 container (container1_segments)中。這里可以看到 6 個對象:
root@controller:~/s1# swift list container1_segments tmpubuntu/1446907333.484258/591396864/100000000/00000000 tmpubuntu/1446907333.484258/591396864/100000000/00000001 tmpubuntu/1446907333.484258/591396864/100000000/00000002 tmpubuntu/1446907333.484258/591396864/100000000/00000003 tmpubuntu/1446907333.484258/591396864/100000000/00000004 tmpubuntu/1446907333.484258/591396864/100000000/00000005每個對象大小是100M(考慮到存儲效率,不建議每個對象大小小于100M):
root@controller:~/s1# swift stat container1_segments tmpubuntu/1446907333.484258/591396864/100000000/00000000Account: AUTH_dea8b51d28bf41599e63464828102759Container: container1_segmentsObject: tmpubuntu/1446907333.484258/591396864/100000000/00000000Content Type: application/octet-stream Content Length: 100000000而且用戶可以單獨操作比如修改某一段。Swift 只會負責將所有段連接為用戶所見的大的對象。
關于大文件支持的更多細節,可以參考?官方文檔?和 Rackspace 的文檔。從上面的描述可以看出,Swift 對文件分段的支持是比較初級的(固定,不靈活),因此,已經有人提出 Object stripping (對象條帶化)方案,比如下面的方案,不知道是否已經支持還是將要支持。
2.3 Region
? ? 通過將對象存放在不同物理位置上的 Region 內,可以進一步增強數據的可用性。其基本原則是:對于 N 份 replica 和 M 個 region,每個 region 中的 replica 數目為 N/M 的整數,剩余的 replica 在 M 個region 中隨機選擇。以 N = 3, M = 2 為例,一個 region 中有 1 個 replica,另一個 region 中有兩個 replica,如下圖所示:
對于一個 PUT 操作來說,Proxy server 只會將 replica 寫入它所在的 region 中的 node,遠端 region 中的 replica 由 replicator 寫入。因此,Swift 的算法應該盡量保證 proxy server 所在的 region 中的 replica 份數相對多一些,這也稱為 replica 的 proxy server 親和性。
顯然,跨 region 的數據復制加重了對網絡帶寬的要求。
兩種形式的 Region:
(1)遠端 region 實時寫入 replica
(2)遠端 region 的 replica 異步寫入
2.4 Storage Polices (存儲策略)
? ? 上面的描述中,一個Swift 集群只支持一套 Ring 配置,這意味著整個機器的配置是唯一的。類似 Ceph 中 pool 的定義,Swift 在 2.0 版本(包含在 OpenStack Juno 版本中)中,添加了一個非常大的功能:Storage policy。在新的實現中,一個 Swift 可以由多套 Ring 配置,每套 Ring 的配置可以不相同。比如,Ring 1 保存 3 份對象拷貝,Ring 2 保存 2 份對象拷貝。幾個特點:
- Policy 被實現在 container 級別
- 創建 container 時可以指定 policy。一旦被指定,不可以修改。
- 一個 policy 可以被多個 container 共享使用
? ? 通過應用該新的功能,Swift 用戶可以制定不同的存儲策略,來適應不同應用的存儲需求。比如對關鍵應用的數據,制定一個存儲策略使得數據被保存到 SSD 上;對于一般關鍵性的數據,指定存儲策略使得數據只保存2份來節約磁盤空間。比如說:
詳細信息,請參考 OpenStack?官方文檔?和 SwiftStack 官方文檔。
3. 版本及主要功能
3.1 Juno以及之前主要版本和功能
?
(1)Large object support
- Swift limitation:Single object: 5GB
- Split object & manage large object
- Manage segmented objects by manifest file
- Ref:http://docs.openstack.org/developer/swift/overview_large_objects.html
(2)Static web hosting
- Upload static web file and make web site;?Upload web site file with index and error files
- Use statiscweb middleware
- Ref:?http://docs.openstack.org/developer/swift/middleware.html#staticweb
(3)?S3 compatible API
- Support S3 API Support limited API less than 40%Use swift3 middleware
- Ref:?https://github.com/stackforge/swift3
(4)?Object expiration
- Schedule deletion of objects
- Use X-Delete-At and X-Delete-After header while using an object PUT or POST
- X-Delete-At: Delete object at specified time
- X-Delete-After: Delete object after specified time
- Ref:?http://docs.openstack.org/developer/swift/overview_expiring_objects.html
(5)?Temp url
- Provide url to access in limited time
- Need temp_url_expires time in header
- Use temporary URL middleware
- Ref:?http://docs.openstack.org/developer/swift/api/temporary_url_middleware.html
(6)?Global cluster
- Make a single cluster in distant region Read/Write affinity
- Deferred replication
- Ref:?http://docs.openstack.org/developer/swift/admin_guide.html,https://swiftstack.com/blog/2012/09/16/globally-distributed-openstack-swift-cluster/
(7)?Storage policy
- Support various policy in sing storage cluster
- Use multiple ring file
- Ref:?http://docs.openstack.org/developer/swift/admin_guide.html
3.2 Kilo 版本中的更新
新功能
糾刪碼(beta)
Swift現在支持糾刪碼(EC)存儲策略類型。這樣部署人員、以極少的RAW容量達到極高的可用性,如同在副本存儲中一樣。然而,EC需要更多的CPU和網絡資源,所以并不適合所有應用場景。EC非常適合在一個獨立的區域內極少訪問的、大容量數據。
Swift糾刪碼的實現對于用戶是透明的。對于副本存儲和糾刪碼存儲的類型,在API上沒有任何區別。
為了支持糾刪碼,Swift現在需要依賴PyECLib和liberasurecode。liberasurecode是一個可插件式的庫,允許在你選擇的庫中實現EC算法。
更詳細文檔請參閱?http://swift.openstack.org/overview_erasure_code.html
復合型令牌(Composite tokens)
復合型令牌允許其他OpenStack服務以客戶端名義將數據存儲于Swift中,所以無論是客戶端還是服務在更新數據時,都不需要雙方彼此的授權。
一個典型的例子就是一個用戶請求Nova存放一個VM的快照。Nova將請求傳遞給Glance,Glance將鏡像寫入Swift容器中的一組對象中。在這種場景下,用戶沒有來自服務的合法令牌時,無法直接修改快照數據。同樣,服務自身也無法在沒有用戶合法令牌的情況下更新數據。但是數據的確存在于用戶的Swift賬戶中,這樣使得賬戶管理更簡單。
更詳細的文檔請參閱http://swift.openstack.org/overview_backing_store.html
更小規模、不平衡集群的數據位置更新
Swift數據的存放位置現在根據硬件權重決定。當前,允許運維人員逐漸的添加新的區域(zones)和地域(regions),而不需要立即觸發大規模數據遷移。同時,如果一個集群是非平衡的(例如,在一個區域(zones)的集群中,其中一個的容量是另外一的兩倍),Swift會更有效的使用現有空間并且當副本在集群空間不足時發出警告。
全局性集群復制優化
區域(regions)之間復制時,每次復制只遷移一個副本。這樣遠程的區域(region)可以在內部復制,避免更多的數據在廣域網(WAN)拷貝。
已知問題
- 作為beta更新,糾刪碼(EC)的功能接近完成,但是對于某些功能仍然不完整(像多范圍(multi-range)讀取),并且沒有一個完整的性能測算。這個功能為了持久性依賴于ssync。部署人員督促我們做更大規模的測試,并且不要在生產環境部署中使用糾刪碼存儲策略。
升級提示
像往常一樣,你能在不影響最終用戶體驗的前提下,升級到這個版本的Swift。
- 為了支持糾刪碼,Swift需要一個新的依賴PyECLib(和liberasurecode等)。并且eventlet的最低版本要求也升高了。
3.3 Liberty 版本中的更新
L版本中Swift 沒有加入大的新功能,詳細情況可以參考 官方文檔。
3.4 優勢
?
?
其它參考文檔:
http://www.florentflament.com/blog/openstack-swift-ring-made-understandable.html
https://www.mirantis.com/blog/configuring-multi-region-cluster-openstack-swift/
OpenStack Swift源碼分析
?
總結
以上是生活随笔為你收集整理的理解 OpenStack Swift (2):架构、原理及功能 [Architecture, Implementation and Features]...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 的23种设计模式 之装B者模式
- 下一篇: 填平新版本Xcode安装插件不成功的坑