天天看點

别讓bug跑了,通過問題了解ceph的克隆過程

寫在前面

     剛剛過去的9月,人工智能、雲計算和物聯網界熱鬧非凡,接連迎來了世界物聯網博覽會、世界人工智能大會和阿裡雲栖大會。2018世界物聯網博覽會就在家門口舉行,抓了空去現場看展覽,外行人看看熱鬧,有感于科技的日新月異給生活帶來的便利。

     話題扯遠了,回到這篇文章,文章的标題包含“複盤”,顧名思義,是對以前的發生的現象或問題進行回顧,學而不思則罔,目的第一是從問題中總結經驗,最大化發掘它的價值;第二是不斷鍛煉自己分析問題的能力;最後是希望強化在某項知識上的運用能力,畢竟以今天分析昨天,有思維就ok了,但是以今天設想未來,需要長期深厚的積累才行。

1 背景

    雲硬碟的快照、克隆,屬于塊存儲RBD基本功能,之前我并沒有太多關注這兩塊,保證功能能用、好用就過去了。

    不過在最近遇到幾個與克隆或快照相關聯的問題,涉及到rbd clone、rbd flatten等,正好是之前未遇到過,是以找了時間對産生的問題複盤,也是希望借bug觀察ceph對克隆和快照的處理流程。

    整篇文章分以下幾小章節:

    1 、背景

    2 、bug回顧

    3、 利用rbd_max_clone_depth觸發flatten

    4 、cinder和ceph層面對clone、flatten的實作

2 bug回顧

  2.1  bug1 通過快照建立雲硬碟,删除父快照失敗

      這個問題複現步驟很簡單,如下流程圖所示:

别讓bug跑了,通過問題了解ceph的克隆過程

     遇到的問題就是在删除快照時發生失敗,禁止該删除操作。

     由快照建立出的雲硬碟,一般情況與快照是父子關系,在ceph下通過rbd info或rbd children可以查詢到鍊的關系。 如下圖紅色框中所示,在parent一項中,可以看到volume-8e81a7b0-4fdc-49b0-a9ed-4124c8e61f7d是volume-117078c1-c724-44b5-a271-e0f708e9d6b3下的快照克隆得來:

别讓bug跑了,通過問題了解ceph的克隆過程

                                                                                                                                              圖1

     既然克隆盤與快照存在父子關系,要删除父快照的首要條件是斬斷這種依賴關系,這個就需要通過rbd_flatten_volume_from_snapshot配置項來實作,見/etc/cinder/cinder.conf配置檔案,如下圖中:

别讓bug跑了,通過問題了解ceph的克隆過程

     在我們的存儲下,rbd_flatten_volume_from_snapshot=false,在false條件下volume和snapshot之間的關系是什麼?

     在解決問題之前,先來了解下rbd_flatten_volume_from_snapshot在true、false下的不同作用,見下表:

别讓bug跑了,通過問題了解ceph的克隆過程

     一目了然,在false條件下,volume和snapshot存在依賴,要想解決該bug,隻要開啟為true即可。

     不過先别急着改/etc/cinder/cinder.conf,在此之前我們通過flatten手動解除依賴關系,再重複bug的步驟看是否能夠成功,這個小實驗分5個步驟複現,如下圖3中:

别讓bug跑了,通過問題了解ceph的克隆過程

       (1)建立volume和快照   PASS

       (2)執行克隆    PASS

       (3)删除父快照   FAILED

                錯誤提示需要對快照先去除保護,unprotect後再執行删除快照,錯誤提示:cannot unprotect: at least 2 child(ren) [1a5e266b8b4567,1aa62d6b8b4567] in pool

       (4)執行flatten操作,解除依賴   PASS

       (5)再次删除父快照     PASS

     小實驗OK,證明了解除克隆盤與snapshot的依賴後再删除快照成功。如果在控制節點的cinder配置檔案中,開啟了rbd_flatten_volume_from_snapshot = true,則 由快照建立出的雲硬碟,會自動合并,清除依賴關系,這樣一來這個雲硬碟就變為扁平的沒有層級的volume。最後記得重新開機cinder服務,使其生效!

  2.2  bug2 大容量的空盤建立快照,再通過該快照建立雲硬碟,耗時過長

     這個bug提的比較優秀,功能本身屬于正常流程,但之前遺漏了大容量雲硬碟這個場景,比如1T、2T。

     問題複現步驟同bug1,隻是少了删除快照的步驟,如下流程圖所示:

别讓bug跑了,通過問題了解ceph的克隆過程

     通常,我們在雲平台上對雲硬碟建立快照後,會同時建立快照卷,由于精簡配置的屬性,隻需配置設定相對少量的存儲空間即可,當再通過該快照clone出雲硬碟,快照處于隻讀保護, 在cow的機制下,克隆操作會很迅速。下面引用一張ceph官方的圖來解釋:

别讓bug跑了,通過問題了解ceph的克隆過程

   上圖4中,parent是指源雲硬碟的快照,而child是從快照克隆出來的雲硬碟。

   這個bug 2與“2.1 bug 1”對比,相同點都是由rbd_flatten_volume_from_snapshot造成的bug,不同的地方在于true和false。

   在bug 2 的雲環境下,rbd_flatten_volume_from_snapshot=true,在上文的bug1中曾說過解除volume和snapshot的依賴關系,取消這種依賴關系叫做flatten,這個flatten花費的時間和源雲硬碟(volume)的大小成正比。

   回到bug本身,内部在做排查時,依照以下的順序:

     (1)檢查volume qos

        眼光先放在了volume qos上,在有資料條件下,qos速率大小(比如write=100MB/S)肯定是會影響到快照建立雲硬碟的速度的。轉念一想,雲硬碟是空盤,并不存在任何object,是以克隆速度應該是很快的。

     (2)檢查父雲硬碟、快照、子雲硬碟的實際容量

        我們環境中雲硬碟是空盤,不存在任何資料,同樣的由快照建立出的新盤也不會有任何資料。實際是否如此,通過下面的驗證步驟來證明一下: 

 建立雲硬碟和快照,并擷取真實存儲空間

别讓bug跑了,通過問題了解ceph的克隆過程

                                                                                           圖5

       圖5中,建立1G的volume,并建立該盤的快照後,rados查詢實際存在的object,找不到任何資料,這是正确的。

由快照建立雲硬碟,并擷取真實存儲空間

别讓bug跑了,通過問題了解ceph的克隆過程

                                                                                                                 圖6

    圖6中,快照建立出新的雲硬碟,叫volume-d7199f3d-ed96-446a-83c8-25083a752e23,可以看到在雲硬碟建立過程中,新的雲硬碟和快照時父子關系,建立成功後,新的雲硬碟和快照時父子關系被解除。

别讓bug跑了,通過問題了解ceph的克隆過程

   圖7所示是擷取新的雲硬碟的實際資料對象,發現已經存在256個object(父雲硬碟總容量為1GB,根據order 22 (4096 kB objects)來切分)

别讓bug跑了,通過問題了解ceph的克隆過程

     圖8中,随機抽查幾個object,發現其實這些object的容量都是0,并不存在真實的資料。一般而言,從快照建立雲硬碟,代碼實作很簡單,先克隆再flatten,Fill clone with parent data (make it independent),此時flatten會将所有塊從父節點複制到child,但父雲硬碟中沒有資料,flatten操作是不應該産生object的。

     這個bug問題就在于flatten會對建立雲盤的每一個對象進行一個寫操作,進而建立無數個大小為0的對象,又在qos的限制下,是以耗時較長。

3 利用rbd_max_clone_depth觸發flatten

      麥子邁在《解析Ceph: Librbd 的克隆問題》一文中提到 “Librbd 在卷的克隆時會形成子卷對父卷的依賴,在産生較長的克隆依賴鍊後會有嚴重的性能損耗”。這個理論其實和cow下多快照産生的性能衰減是一樣的,對ceph的雲硬碟做快照,每次做完快照後再對雲硬碟進行寫入時就會觸發COW操作, 即1次讀操作、2次寫操作,volume→volume的克隆本質上就是将 volume 的某一個 Snapshot 的狀态複制變成另一個volume。

      為解決在産生較長的克隆依賴鍊後會有嚴重的性能損耗問題,在OpenStack Cinder 的/etc/cinder/cinder.conf中提供一個參數,可以解除父子依賴關系,在超過自定義設定的閥值後選擇強制 flatten。

别讓bug跑了,通過問題了解ceph的克隆過程

    在圖9中,通過 rbd_max_clone_depth來控制最大可克隆的層級。

    rbd_max_clone_depth = 5 這個參數控制卷克隆的最大層數,超過的話則使用 fallten。設為 0 的話,則禁止克隆。

    為了驗證這個過程,下面我們做個實驗,建立1個volume,命名為01,依次複制下,即由01複制成02,02複制為03,03複制為04,04複制為05,05複制為06,06複制為07,如下圖流程圖:

别讓bug跑了,通過問題了解ceph的克隆過程

   實驗預期結果,就是當從06複制到07時,滿足rbd_max_clone_depth > 5,此時觸發flatten操作。

别讓bug跑了,通過問題了解ceph的克隆過程

圖10

别讓bug跑了,通過問題了解ceph的克隆過程

圖11

   圖10、圖11是 複制雲硬碟後的查詢到克隆盤資訊

别讓bug跑了,通過問題了解ceph的克隆過程

圖12

   圖12中, 上面的log記錄了複制07時,觸發了flatten操作,對上級雲硬碟06執行flatten操作,開始執行合并。

别讓bug跑了,通過問題了解ceph的克隆過程

圖13

   圖13所示是Flatten成功後,可以看到雲硬碟06 的parent一項消失,此時在頁面上可以删除雲硬碟06

4 cinder和ceph層面對clone、flatten的實作

     現在市面上很多講ceph的書(大多數翻譯自ceph中國社群之手),在RBD塊存儲章節都會對快照、克隆等操作花很多篇幅去描述,基本都是在rbd層通過指令一步步分解rbd clone過程來講原理。

     對于類似我這樣的剛接觸ceph不久的人來說,知識點分散在各處,看了前面忘了後面,很難在腦子裡建立完整的概念,當然主要原因還是自己太菜了,迷霧重重看不透!

别讓bug跑了,通過問題了解ceph的克隆過程

    言歸正傳,我隻是想大概的了解下對雲硬碟執行操作在底層是如何實作的,是以還是由上文中提到的小處(bug)來入手,自頂向下先設計一個思考流程,帶着目标按照這個從上到下的順序去了解,如下圖所示:

别讓bug跑了,通過問題了解ceph的克隆過程

         注:以下涉及的代碼均來自GitHub開源,如有雷同,純屬巧合!

4.1  從快照克隆卷的流程

    (1)openstack cinder

         自頂向下,先從cinder層入手,通過代碼可以看到從快照克隆出volume的思路,從本質上講,快照克隆出新的卷,也是volume create的性質,是以先來了解下volume create過程

         cinder:/cinder/volumes.py

别讓bug跑了,通過問題了解ceph的克隆過程

     volumes.py中def create方法我省略了很多,主要就是通過req、body的參數來擷取建立volume所需要的參數,根據不同參數來發送具體的建立volume請求,因為我是從快照來建立,snapshot id自然必不可少,在 volumes.py最後實際調用new_volume = self.volume_api.create()去實作。

cinder:/cinder/volume/api.py

     經過volume_api.create(),在/cinder/volume/api.py來處理前端發來的卷相關的所有請求,通過create_what{}表示volume的實作參數,然後分别就調用cinder.scheduler的scheduler_rpcapi,cinder.volume的volume_rpcapi建立建立volume的工作流:create_volume.get_flow

别讓bug跑了,通過問題了解ceph的克隆過程

    注:關于create volume flow的流程及具體實作,見/cinder/volume/rpcapi.py:def create_volume(),/cinder/volume/flows/api/create_volume.py,本篇省略過程

cinder:/cinder/volume/manager.py

    對于api來講,隻是做到處理前端發來的卷相關的所有請求,具體實作交由manager下的去完成,rpcapi調用inder/volume/manager.py:def create_volume()去操作

别讓bug跑了,通過問題了解ceph的克隆過程

     執行中發現crate voluem 有snapshot id,然後調用/cinder/volume/flows/manager/create_volume.py下的私有方法_create_volume_from_snapshot()

别讓bug跑了,通過問題了解ceph的克隆過程

   最後根據配置檔案指定的RBD後端請求/cinder/volume/drivers/rbd.py的create_volume_from_snapshot()

cinder:/cinder/volume/drivers/rbd.py

     衆所周知,一般cinder使用RBD驅動來對接底層的後端存儲(比如ceph、xsky),在openstack cinder層面最終交由create_volume_from_snapshot()實作,因為是通過快照來建立volume,還需要調用私有方法_clone(),滿足條件的話,還要調用_flatten()和_resize()。

别讓bug跑了,通過問題了解ceph的克隆過程

(2)librbd

     經曆多方接力才結束在cinder層面的流程,這還不算完,真正要實作create volume from snapshot的建立,核心在調用ceph執行。

    ceph:/src/pybind/rbd/rbd.pyx

别讓bug跑了,通過問題了解ceph的克隆過程

/ceph/blob/v10.2.3/src/librbd/librbd.cc

别讓bug跑了,通過問題了解ceph的克隆過程

    在librbd中對外提供api在class RBD中,從librbd.cc函數中看到有多個clone()、clone2()、clone3()函數,差別在于根據傳入的不同參數來調用對應的函數,但這些函數都不像是具體的功能實作,隻是一些相關參數傳值。

    再看看/ceph/blob/v10.2.3/src/librbd/internal.cc函數,同librbd.cc一樣,對應的clone()也是3種,因為篇幅如下展示的是clone3()函數(實際命名并不如此,通過參數來區分得知是clone2):

别讓bug跑了,通過問題了解ceph的克隆過程

    将librbd.cc、internal.cc兩個函數聯系起來看,librbd.cc隻是定義了對外的各種函數接口,接口的具體實作,調用的還是internal.cc中定義的函數内容。

    總結一下,根據自己的了解将整個流程繪成圖,如下圖所示中,需要一提的是,我沒有涉及到librados的實作過程,因為clone等volume的操作,librbd可以說就是rbd的完整實作,rados隻是作為後端的存儲

别讓bug跑了,通過問題了解ceph的克隆過程

4.2  flatten的流程

    在前文“ 利用rbd_max_clone_depth觸發flatten”小節中,我們描述了一個volume clone的過程,通過cinder.conf的一個參數,當滿足rbd_max_clone_depth最大層數後,觸發flatten操作,下面我們通過代碼去看一看具體實作的流程。

     對于上層雲平台而言,從雲硬碟1克隆出雲硬碟2,或者從快照建立雲硬碟,一般是能夠觸發flatten操作的主要場景,其實兩者實作原理基本一緻。

     是以,和之前的由snapshot來實作建立新的雲硬碟一樣,首要都是從create()開始,隻是參數不同,克隆盤在create過程先要擷取parent volume id

别讓bug跑了,通過問題了解ceph的克隆過程

    之後也是一樣經曆api→manager→driver的過程,這裡省掉重複的過程,直接看cinder調用rbd驅動對克隆雲硬碟的實作代碼,如下圖中/cinder/volume/drivers/rbd.py:

别讓bug跑了,通過問題了解ceph的克隆過程

     調用了私有方法_get_clone_depth()來判斷depth,調用_flatten()來實作flatten操作,當然flatten過程經曆一系列過程,在parent volume上建立snapshot,對snapshot加保護、再執行clone,然後flatten,這個過程一樣可以通過rbd 指令來完成。

      建立RADOSClient,連接配接到ceph rados,這裡也是先調用clone()去執行,再觸發flatten()操作,和我預期不同,flatten的過程比想象中還要複雜,才疏學淺,對整個過程的了解還需要更多的時間,隻能先用根據自己的了解畫出一張流程圖表示一下:

别讓bug跑了,通過問題了解ceph的克隆過程

繼續閱讀