深入解析Ceph分布式存儲(chǔ)的內(nèi)核客戶端
隨著云計(jì)算的發(fā)展,Ceph已經(jīng)成為目前最為流行的分布式存儲(chǔ)系統(tǒng),儼然存儲(chǔ)界的Linux操作系統(tǒng)。Ceph集塊存儲(chǔ)、文件存儲(chǔ)和對(duì)象存儲(chǔ)于一身,適用場(chǎng)景廣泛,用戶眾多。本文是介紹Ceph客戶端在內(nèi)核部分的實(shí)現(xiàn)的文章,本號(hào)特以翻譯成中文。
Ceph是一個(gè)開(kāi)源,統(tǒng)一的分布式存儲(chǔ)系統(tǒng),我們?cè)赟alesforce中用作塊存儲(chǔ)服務(wù)。Ceph的塊存儲(chǔ)通過(guò)一個(gè)客戶端模塊實(shí)現(xiàn),這個(gè)客戶端可以直接從數(shù)據(jù)守護(hù)進(jìn)程讀寫(xiě)數(shù)據(jù)(不需要經(jīng)過(guò)一個(gè)網(wǎng)關(guān))。根據(jù)客戶端整合生態(tài)系統(tǒng)的差異(譯者注:也就是應(yīng)用場(chǎng)景),客戶端有兩種實(shí)現(xiàn)方式:
- librbd (用戶態(tài))
- krbd (內(nèi)核態(tài))
Librbd是一個(gè)典型的應(yīng)用就是在虛擬機(jī)環(huán)境中(例如KVM或者QEMu),而krbd則是用在容器和裸金屬環(huán)境。在Salesforce, 我們將Ceph作為Docker容器并且在應(yīng)用容器運(yùn)行主機(jī)上安裝krbd模塊。
本文將描述Ceph內(nèi)核客戶端的一些內(nèi)部實(shí)現(xiàn)。下面是本文涵蓋的內(nèi)容:
- 初始化
- Ceph的配置是如何被內(nèi)核模塊獲取的
- 處理配置的代碼入口點(diǎn)
- 連接處理
- 安全
- 連接狀態(tài)機(jī)
krbd 初始化
krbd是一個(gè)內(nèi)核模塊。其在內(nèi)核中以一個(gè)塊設(shè)備的方式加以實(shí)現(xiàn)。整個(gè)Ceph客戶端都是以內(nèi)核模塊的方式實(shí)現(xiàn)(沒(méi)有與之相關(guān)的用戶態(tài)進(jìn)程或者守護(hù)進(jìn)程)。
Ceph客戶端需要一個(gè)配置文件(ceph.conf)以知道如何連接集群,并且需要知道集群的屬性。該配置文件通常包括Monitor的IP地址、用戶證書(shū)和Ceph認(rèn)證的安全屬性(Cephx)。在初始化的時(shí)候,所有這些配置都需要加載到內(nèi)核模塊當(dāng)中。一旦完成這一步,剩下的諸如鏡像或者卷生命周期、連接管理、認(rèn)證和狀態(tài)機(jī)等所有實(shí)現(xiàn)都可以在內(nèi)核模塊中完成。
將CEPH配置推送到內(nèi)核模塊
當(dāng)你運(yùn)行ceph-deploy去安裝客戶端的時(shí)候,它將安裝在Ceph客戶端(以rbd為前綴的命令)CLI命令調(diào)用的二進(jìn)制文件。
rbd CLI
第一個(gè)客戶端命令是:
- rbd map <device-name> --pool <pool_name>
默認(rèn)情況下,rbd從配置文件/etc/ceph/ceph.conf中讀取配置信息。該文件包含諸如Monitor地址(客戶端通過(guò)該地址與Monitor聯(lián)系)、OSD閑時(shí)ttl、OSD超時(shí)時(shí)間、OSD請(qǐng)求超時(shí)和加密等內(nèi)容。關(guān)于配置項(xiàng)的全量列表可以從這個(gè)地址獲?。篽ttp://docs.ceph.com/docs/jewel/man/8/rbd/#kernel-rbd-krbd-options。
OSD

krbd 初始化
rbd map命令行命令實(shí)際上讀取ceph.conf文件的內(nèi)容,并且通過(guò)Linux的“總線”接口(/sys/bus/
定義依賴這里: https://github.com/torvalds/linux/blob/master/include/linux/device.h
總線(bus)是處理器和一個(gè)或者多個(gè)設(shè)備間的通道。其目的是實(shí)現(xiàn)設(shè)備模型,所有設(shè)備由總線連接,甚至可以是一個(gè)內(nèi)部的、虛擬的平臺(tái)(platform)總線。總線之間可以彼此連接,比如一個(gè)USB控制器通常是一個(gè)PCI設(shè)備。設(shè)備模型代表總線和它們所控制設(shè)備的實(shí)際連接。
在Linux設(shè)備模型中,bus_type結(jié)構(gòu)體代表一個(gè)總線,在linux/device.h中定義。該結(jié)構(gòu)體如下:

本實(shí)現(xiàn)的源代碼在ceph/ceph git庫(kù),具體如下:https://github.com/ceph/ceph/blob/master/src/krbd.cc

KRBD 配置解析
在內(nèi)核模塊中,處理配置文件的入口點(diǎn)定義在drivers/block/rbd.c 中(https://github.com/ceph/ceph-client/blob/for-linus/drivers/block/rbd.c):
static ssize_t rbd_add(struct bus_type *bus, const char *buf, size_t count)
連接設(shè)置
內(nèi)核模塊全權(quán)負(fù)責(zé)建立與Monitor和OSD的TCP連接,檢測(cè)這些連接,并且進(jìn)行在出現(xiàn)問(wèn)題的時(shí)候重建連接,以及認(rèn)證工作。這些工作都是在內(nèi)核中進(jìn)行的。

Mon 客戶端初始化
入口函數(shù): ceph_monc_init()
該函數(shù)首先通過(guò)掛載時(shí)提供的IP地址構(gòu)建一個(gè)臨時(shí)的monmap(build_initial_monmap()) ,這個(gè)操作發(fā)生在客戶端與Monitor建立新連接的時(shí)候。之后,其初始化認(rèn)證數(shù)據(jù)結(jié)構(gòu),進(jìn)一步準(zhǔn)備如下消息以完成連接后的初步握手:
- CEPH_MSG_MON_SUBSCRIBE (msg Id: 15)
- CEPH_MSG_MON_SUBSCRIBE_ACK (msg Id: 16)
- CEPH_MSG_AUTH (msg Id: 17)
- CEPH_MSG_AUTH_REPLY (msg Id: 18)
連接初始化
入口函數(shù): ceph_con_init()
這時(shí),調(diào)用ceph_con_init() (在net/ceph/messenger.c中) 函數(shù)完成與Monitor連接的初始化,并且通過(guò)一個(gè)狀態(tài)機(jī)轉(zhuǎn)換連接。ceph_con_init() 初始化套接字,發(fā)起一個(gè)工作隊(duì)列函數(shù),該函數(shù)通過(guò)異步的方式實(shí)現(xiàn)連接的設(shè)置。

ceph_con_workfn() 函數(shù)通過(guò)如下套接字狀態(tài)(ceph_connection -> sock_state)遍歷連接,并將其轉(zhuǎn)換為ESTABLISHED狀態(tài)。
- /*
- * Socket state - transitions (connection state is different from socket state)
- * --------
- * | NEW* | transient initial state
- * --------
- * | con_sock_state_init()
- * v
- * ----------
- * | CLOSED | initialized, but no socket (and no
- * ---------- TCP connection)
- * ^ \
- * | \ con_sock_state_connecting()
- * | ----------------------
- * | \
- * + con_sock_state_closed() \
- * |+--------------------------- \
- * | \ \ \
- * | ----------- \ \
- * | | CLOSING | socket event; \ \
- * | ----------- await close \ \
- * | ^ \ |
- * | | \ |
- * | + con_sock_state_closing() \ |
- * | / \ | |
- * | / --------------- | |
- * | / \ v v
- * | / --------------
- * | / -----------------| CONNECTING | socket created,
- * | | / -------------- TCP connect
- * | | | initiated
- * | | | con_sock_state_connected()
- * | | v
- * -------------
- * | CONNECTED | TCP connection established
- * -------------
- *
- * State values for ceph_connection->sock_state; NEW is assumed to be 0.
- */
- /*
- * Connection state - transitions
- * --------
- * | CLOSED |<----<-----------------<-----------<-
- * -------- | | |
- * | | On failures | |
- * v | | |
- * -------- | | |
- * ---->| PREOPEN |---- | |
- * | -------- ^ ^
- * | | ceph_tcp_connect() | |
- * | v | |
- * ^ ------------- | |
- * | | CONNECTING |----------------------- |
- * | ------------- |
- * | | Read and process banner ("ceph v027"), |
- * | | prepare capabilities to send to peer |
- * | | |
- * | v |
- * | -------------- |
- * | | NEGOTIATING |----------------------------------
- * | --------------
- * ------- | Read connect_reply, auth capabilites
- * |STANDBY| | from peer
- * ------- |
- * | v
- * | -----
- * <----- |OPEN |--------------------------------------->
- * ----- (Complete final handshake ack)
- *
Connection Callback Registrations
下面是在include/linux/ceph/messenger.h中定義的數(shù)據(jù)結(jié)構(gòu),這個(gè)數(shù)據(jù)結(jié)構(gòu)定義了在Monitor連接中處理連接事件的回調(diào)函數(shù)(包括連接到OSD的連接):



一旦客戶端完成連接,它將收到如下內(nèi)容:
Mon map (ceph_monc_handle_map() 在 net/ceph/mon_client.c)
Osd map (ceph_osdc_handle_map() 在 net/ceph/osd_client.c)
如果Ceph客戶端的配置文件中包含集群中所有Monitor的信息,客戶端將與所有的Monitor建立連接。但是,如果在你的集群中有5個(gè)Monitor,但是配置文件中只有一個(gè)Monitor。這時(shí),客戶端與配置文件中的這個(gè)Monitor建立連接。如果與Monitor的連接中斷,客戶端將隨機(jī)選取一個(gè)Monitor建立連接(客戶端通過(guò)其接收的monmap獲得所有Monitor的列表)。
當(dāng)Monitor連接觸發(fā)創(chuàng)建OSD客戶端數(shù)據(jù)結(jié)構(gòu)和連接的時(shí)候,客戶端會(huì)收到OSD map的信息。
krbd在內(nèi)核的源碼目錄
源文件:
- drivers/block/rbd.c
- drivers/block/rbd_types.h
- net/ceph/
頭文件:
include/linux/ceph
包裝
本文深入介紹了Ceph客戶端krbd,并且介紹了其在內(nèi)核中的實(shí)現(xiàn)。同時(shí),還介紹了在內(nèi)核中實(shí)現(xiàn)的客戶端的各種功能的入口函數(shù)。
總結(jié)(譯者注)
本文介紹了Ceph客戶端塊存儲(chǔ)部分的初始化和實(shí)現(xiàn)的部分細(xì)節(jié)。Ceph在內(nèi)核中還有另外一部分,這部分就是大名鼎鼎的Ceph文件系統(tǒng)。這部分內(nèi)容比較復(fù)雜,后面譯者將另外寫(xiě)一篇文章進(jìn)行介紹。
另外,這里需要補(bǔ)充的是,在Linux內(nèi)核中Ceph的內(nèi)容主要涉及3個(gè)方面,一個(gè)是krbd的實(shí)現(xiàn)(在 drivers/block/rbd.c中),也就是塊設(shè)備的實(shí)現(xiàn);第二個(gè)是Ceph文件系統(tǒng)(在 fs/ceph目錄中)的實(shí)現(xiàn),這里類似與NFS,它通過(guò)網(wǎng)絡(luò)的方式實(shí)現(xiàn)對(duì)Ceph集群分布式文件系統(tǒng)的訪問(wèn);第三個(gè)是網(wǎng)絡(luò)部分(在 net/ceph目錄中),這部分是公用的,塊設(shè)備和文件系統(tǒng)都用到了該部分的內(nèi)容。