深入理解ceph-disk prepare 源码逻辑
生活随笔
收集整理的這篇文章主要介紹了
深入理解ceph-disk prepare 源码逻辑
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- CEPH-DISK代碼邏輯
- DEF MAIN:
- DEF PARSE_ARGS:
- DEF Prepare.set_subparser(subparsers)
- def _prepare(self):PrepareBluestore的_prepare函數
- def prepare(self, *to_prepare_list):PrepareData類中的prepare函數
- def prepare_device(self, *to_prepare_list): #prepare_device
- def prepare(self):prepare_device的prepare函數,執行基本分區的prepare工作`db,wal`
- def populate_data_path_device(self, *to_prepare_list):創建臨時文件系統,寫入Ceph_fsid以及fsid,磁盤屬性等信息
CEPH-DISK代碼邏輯
ceph L版本12.2.1
本文以最新的BLUESTORE為例進行邏輯梳理
主要分為:prepare和activate,本文主要從源碼描述prepare的工作過程
ceph-disk -v prepare /dev/sdb
ceph-disk -v activate /dev/sdb1
DEF MAIN:
ceph-disk的main函數入口
def main(argv):#python強大的命令行解析模塊,將輸入命令解析為具體的對應項,并調用對應的函數做處理,如prepare,activateargs = parse_args(argv)#啟動部署的日志級別,分為DEBUG,STDOUT,BASICsetup_logging(args.verbose, args.log_stdout)#設置python的全局變量,創建對應的文件,包括prepare鎖文件,activate鎖文件,suppress_prefix抑制標記setup_statedir(dir)#同步配置文件的目錄為/etc/cephsetup_sysconfdir(args.sysconfdir) #設置ceph的全局用戶組和用戶權限global CEPH_PREF_USERCEPH_PREF_USER = args.setuserglobal CEPH_PREF_GROUPCEPH_PREF_GROUP = args.setgroup#啟動執行args傳入的函數if args.verbose:args.func(args)else:main_catch(args.func, args)
DEF PARSE_ARGS:
通過強大的parser命令行解析器模塊對輸入的參數進行解析
def parse_args(argv):#構建一個ArgrmentParser對象parser = argparse.ArgumentParser('ceph-disk',)#add_arguemnt添加命令行參數,`-`和`--`開頭的參數表示可選參數,其他的表示必須輸入的參數parser.add_argument('-v', '--verbose',action='store_true', default=None,help='be more verbose',)#python強大的命令行解析模塊,添加一個子命令解析器subparsers = parser.add_subparsers(title='subcommands',description='valid subcommands',help='sub-command help',)#對prepare 子命令進行解析,并設置參數的函數屬性為Prepare.mainPrepare.set_subparser(subparsers)#對activate子命令進行解析,并設置參數的函數屬性為main_activatemake_activate_parser(subparsers)#對activate-lockbox子命令進行解析,并設置參數的函數屬性為main_activate_lockboxmake_activate_lockbox_parser(subparsers)#對激活數據(data)子命令進行解析,設置參數的函數屬性main_activate_space(name, args)make_activate_block_parser(subparsers)#對激活日志(jounal)子命令進行解析,設置參數屬性為main_activate_space(name, args)make_activate_journal_parser(subparsers)#對激活所有的osd分區,激活所有的/dev/disk/by-parttypeuuid中找到的分區,使用main_activate_all函數make_activate_all_parser(subparsers)#對顯示系統上所有的分區以及與ceph有關聯的分區信息,list命令進行解析,main_listmake_list_parser(subparsers)#對supress-activate函數進行解析,使得磁盤不會被激活,使用main_suppress make_suppress_parser(subparsers)#對deactivate子命令進行解析,停止osd服務并將osd標記為out,刪除/var/lib/ceph/osd/ceph-id目錄make_deactivate_parser(subparsers)#破壞osd,破壞的前提是該osd進程必須是down狀態。如果完成破壞,那么一個新的osd就可以在當前位置進程創建,并且可以使用相同的osd_idmake_destroy_parser(subparsers)#對格式化命令進行解析,主要功能是使用sgdisk刪除一個設備的分區表和目錄,主要參數是‘--zap-all’可以破壞gpt和MBR數據結構,以便磁盤可以為下次分區做準備make_zap_parser(subparsers)#對觸發命令進行解析,激活給定的osd分區,主要是在系統重啟時拉起一些系統進程或者systemd服務,通過異步執行該命令可以縮短udev動作的執行時間make_trigger_parser(subparsers)#解析修復命令,主要用來修復selinux,ceph相關標簽、系統文件權限、修復ceph數據的可訪問資源(selinux)make_fix_parser(subparsers)
DEF Prepare.set_subparser(subparsers)
解析prepare命令,并獲取prepare時的執行函數Prepare.main,prepare時的步驟如下
-
判定部署參數中是否有對osd目錄加密
--dmcrypt,該參數用于ceph的ansible自動化工具- 有加密,核對當前設備是否在正在使用中,如果正在使用中則返回錯誤。(由于我們有共享盤技術,所以部分磁盤會在prepare之前被占用,我們會注釋掉這部分代碼)
- 主要通過檢測設備是否被掛載
- 檢測是否有分區且分區被掛載
- 設備未被使用,則創建一個10M的五分區用來做加密的盒子
lockbox - 格式化分區為ext4文件系統并掛載到目錄
/var/lib/ceph/osd-lockbox目錄下
- 有加密,核對當前設備是否在正在使用中,如果正在使用中則返回錯誤。(由于我們有共享盤技術,所以部分磁盤會在prepare之前被占用,我們會注釋掉這部分代碼)
-
部署參數中沒有對osd目錄加密,則開始增加prepare list內容
- 先在磁盤設備上做出數據分區1,默認是100MB
- 根據設定的參數,如
ceph-disk -v prepare /deb/sdb --block.db /dev/sdb --block.wal /dev/sdb指定了擁有db/wal分區的設備,會先做出第一分區data,其次第三分區db,第四分區wal - 創建uuid類型設備鏈接到分區,因為udev無法可靠地注意到對現有分區的GUID的更改
- 使用partprobe更新分區表
- mkfs使用默認設定的文件系統格式化第一分區,并創建臨時文件夾掛載到第一分區將ceph_fsid,fsid,設備屬性等信息寫入文件系統
到目前為止所有分區就都prepare成功了,下一步就是activate
源碼如下:def set_subparser(subparsers):#設置幾個主要的參數解析器用來添加基礎prepare命令集選項parents = [Prepare.parser(),PrepareData.parser(),Lockbox.parser(),]#添加prepare的子命令解析器subparsers.add_parser('prepare',parents=parents,...)#如果命令為prepare,最終主函數會執行如下prepare的主函數def main(args):Prepare.factory(args).prepare()#使用prepare類的工廠函數來執行#這里我們默認選擇L版本之后bluestore的prepare函數def factory(args):if args.bluestore:return PrepareBluestore(args)else:return PrepareFilestore(args)
def _prepare(self):PrepareBluestore的_prepare函數
def _prepare(self)#對部署參數中是否有osd目錄的加密進行判斷,有的話則需要prepare單獨的lockbox分區if self.data.args.dmcrypt:self.lockbox.prepare()to_prepare_list = []if getattr(self.data.args, 'block.db'):to_prepare_list.append(self.blockdb)if getattr(self.data.args, 'block.wal'):to_prepare_list.append(self.blockwal)to_prepare_list.append(self.block)#如果沒有則增加完prepare_list中的內容之后開始進行osd主要分區的準備工作#當前prepare中的函數prepare_device()被PrepareBluestoreData繼承self.data.prepare(*to_prepare_list)
def prepare(self, *to_prepare_list):PrepareData類中的prepare函數
def prepare(self, *to_prepare_list)if self.type == self.DEVICE:#當前使用的prepare_device函數為PrepareBluestoreData類中的函數self.prepare_device(*to_prepare_list)elif self.type == self.FILE:self.prepare_file(*to_prepare_list)else:raise Error('unexpected type ', self.type)
def prepare_device(self, *to_prepare_list): #prepare_device
def prepare_device(self, *to_prepare_list): super(PrepareBluestoreData, self).prepare_device(*to_prepare_list)#先做出第一分區,默認100MBself.set_data_partition()for to_prepare in to_prepare_list:#當前prepare根據PrepareBluestore的_prepare函數def _prepare(self):中的prepare_list中的對象prepare對應的分區,此時先prepare db分區,其次wal分區to_prepare.prepare()#使用默認文件系統格式化第一分區并創建臨時文件self.populate_data_path_device(*to_prepare_list)
def prepare(self):prepare_device的prepare函數,執行基本分區的prepare工作db,wal
def prepare(self)if self.type == self.DEVICE:#如果是Device,一般是bluestore,否則為filestore#根據傳入的設備類型進行prepare_device,先是db高速設備,其次是wal超高速設備self.prepare_device()elif self.type == self.FILE:self.prepare_file()elif self.type == self.NONE:passelse:raise Error('unexpected type ', self.type)#上一個函數的prepare_device,在該函數過程中會根據設備類型創建uuid鏈接到分區,并使用partprobe更新分區表def prepare_device(self):reusing_partition = False#判定當前設備是否有分區if is_partition(getattr(self.args, self.name)):LOG.debug('%s %s is a partition',self.name.capitalize(), getattr(self.args, self.name))partition = DevicePartition.factory(path=None, dev=getattr(self.args, self.name), args=self.args)if isinstance(partition, DevicePartitionCrypt):raise Error(getattr(self.args, self.name) +' partition already exists'' and --dmcrypt specified')LOG.warning('OSD will not be hot-swappable' +' if ' + self.name + ' is not' +' the same device as the osd data')if partition.get_ptype() == partition.ptype_for_name(self.name):LOG.debug('%s %s was previously prepared with ''ceph-disk. Reusing it.',self.name.capitalize(),getattr(self.args, self.name))reusing_partition = True# Read and reuse the partition uuid from this journal's# previous life. We reuse the uuid instead of changing it# because udev does not reliably notice changes to an# existing partition's GUID. See# http://tracker.ceph.com/issues/10146setattr(self.args, self.name + '_uuid', partition.get_uuid())LOG.debug('Reusing %s with uuid %s',self.name,getattr(self.args, self.name + '_uuid'))else:LOG.warning('%s %s was not prepared with ''ceph-disk. Symlinking directly.',self.name.capitalize(),getattr(self.args, self.name))self.space_symlink = getattr(self.args, self.name)returnself.space_symlink = '/dev/disk/by-partuuid/{uuid}'.format(uuid=getattr(self.args, self.name + '_uuid'))#如果有對設備映射表的加密標記,則對加密分區創建鏈接if self.args.dmcrypt:self.space_dmcrypt = self.space_symlinkself.space_symlink = '/dev/mapper/{uuid}'.format(uuid=getattr(self.args, self.name + '_uuid'))if reusing_partition:# confirm that the space_symlink exists. It should since# this was an active space# in the past. Continuing otherwise would be futile.assert os.path.exists(self.space_symlink)returnnum = self.desired_partition_number()if num == 0:LOG.warning('OSD will not be hot-swappable if %s ''is not the same device as the osd data',self.name)device = Device.factory(getattr(self.args, self.name), self.args)#創建對應分區num = device.create_partition(uuid=getattr(self.args, self.name + '_uuid'),name=self.name,size=self.space_size,num=num)partition = device.get_partition(num)LOG.debug('%s is GPT partition %s',self.name.capitalize(),self.space_symlink)if isinstance(partition, DevicePartitionCrypt):partition.format()partition.map()command_check_call(['sgdisk','--typecode={num}:{uuid}'.format(num=num,uuid=partition.ptype_for_name(self.name),),'--',getattr(self.args, self.name),],)#partprobe更新分區表update_partition(getattr(self.args, self.name), 'prepared')LOG.debug('%s is GPT partition %s',self.name.capitalize(),self.space_symlink)
def populate_data_path_device(self, *to_prepare_list):創建臨時文件系統,寫入Ceph_fsid以及fsid,磁盤屬性等信息
prepare_device的最后一步是創建臨時文件系統,寫入Ceph_fsid以及fsid,磁盤屬性等信息到該文件系統
def populate_data_path_device(self, *to_prepare_list):partition = self.partitionif isinstance(partition, DevicePartitionCrypt):partition.map()try:args = ['mkfs','-t',self.args.fs_type,]if self.mkfs_args is not None:args.extend(self.mkfs_args.split())#如果默認文件系統為xfs,則使用mkfs -t xfs -f強制執行,否則不需要-f參數if self.args.fs_type == 'xfs':args.extend(['-f']) # always forceelse:args.extend(MKFS_ARGS.get(self.args.fs_type, []))args.extend(['--',partition.get_dev(),])LOG.debug('Creating %s fs on %s',self.args.fs_type, partition.get_dev())command_check_call(args, exit=True)#執行掛載path = mount(dev=partition.get_dev(),fstype=self.args.fs_type,options=self.mount_options)try:self.populate_data_path(path, *to_prepare_list)finally:path_set_context(path)unmount(path)finally:if isinstance(partition, DevicePartitionCrypt):partition.unmap()if not is_partition(self.args.data):command_check_call(['sgdisk','--typecode=%d:%s' % (partition.get_partition_number(),partition.ptype_for_name('osd')),'--',self.args.data,],exit=True,)#parprobe更新分區表,且在此過程中會執行兩次udevadm settle --timeout=600,第一次是為了解決正在等待進行的udev事件完成,以防其中有事件依賴于設備上的現有分區;第二次執行是為了向調用者保證已經處理了與分區表變化相關的所有udev事件(95-ceph-osd.rules動作和模式改變,組改變等)已完成。update_partition(self.args.data, 'prepared')command_check_call(['udevadm', 'trigger','--action=add','--sysname-match',os.path.basename(partition.rawdev)])
總結
以上是生活随笔為你收集整理的深入理解ceph-disk prepare 源码逻辑的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ceph-bluestore-tool基
- 下一篇: 大家有没默默诅咒过一个人,然后那个人真的