System Notes


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

ceph解读之PGLog

发表于 2015-12-18 | 分类于 ceph

ceph的PGLog是由PG来维护,记录了该PG的所有操作,其作用类似于数据库里的undo log。PGLog通常只保存近千条的操作记录(默认是3000条),但是当PG处于降级状态时,就会保存更多的日志(默认是10000条),这样就可以在故障的PG重现上线后用来恢复PG的数据。本文主要从PGLog的格式、存储方式、如何参与恢复来解析PGLog。

1.PGLog的格式

ceph使用版本控制的方式来标记一个PG内的每一次更新,每个版本包括一个(epoch,version)来组成:其中epoch是osdmap的版本,每当有OSD状态变化如增加删除等时,epoch就递增;version是PG内每次更新操作的版本号,递增的,由PG内的Primary OSD进行分配的。

PGLog在代码实现中有3个主要的数据结构来维护:pg_info_t,pg_log_t,pg_log_entry_t。三者的关系示意图如下。从结构上可以得知,PGLog里只有对象更新操作相关的内容,没有具体的数据以及偏移大小等,所以后续以PGLog来进行恢复时都是按照整个对象来进行恢复的(默认对象大小是4MB)。
pglog

阅读全文 »

重启osd对ceph性能影响的分析

发表于 2015-12-05 | 分类于 ceph

最近测试ceph恢复的时候对业务io的影响,发现修复时ceph集群的性能骤降,比如正常情况下一个卷的随机4k写的iops约10k,但是恢复的时候骤降到1k左右。从ceph设计来说,恢复涉及到peering,backfill,remap等操作,对性能必然会存在影响,这里先不深究ceph的恢复机制,后续研究清楚了再进行分析,本文以重启osd为例,从现象入手来简要分析重启osd对ceph性能的影响。

1.环境配置

1)硬件配置:使用openstack + ceph后端,计算和存储分离,6台物理机作为ceph存储节点,每台机器6块ssd,每块ssd上运行一个osd(其中journal和data各占一个分区),整个集群的数据流使用万兆网络;
2)测试时在6台kvm虚拟机里分别挂载一块20GB的ceph块设备,然后使用fio分别跑6种I/O模型:4KB块大小的随机写,4KB块大小的随机读,4KB块大小的随机混合读写(1:1比例),64KB块大小的顺序写,64KB块大小的顺序读,64KB块大小的顺序混合读写(1:1比例)。使用这样的模型来模拟集群负载;

2.重启osd进行测试

1)先将6个卷的I/O跑起来,然后重启一个osd;
2)观察到随机写的iops从10k降到低于1k,并且波动还比较大,随机读的iops从10k降到几百;
3)性能下降持续了10几秒后又正常,是因为恢复完了;

阅读全文 »

ceph OSD读写流程(3)–副本OSD的消息处理及主OSD的响应处理

发表于 2015-11-25 | 分类于 ceph

replica

  • 副本OSD收到主OSD的写请求时,也是按照消息处理的流程从队列里取出来进行处理,先写PGLog,然后注册两个回调函数,然后进行写journal和写本地缓存的操作,在处理完成后发送响应给主OSD(op_modify_commit和op_modify_applied),具体流程参考上一篇文章;
  • 主OSD收到副本OSD的响应后,从in_progress_ops中找到op,op里就有注册的回调函数,判断flag如果是CEPH_OSD_FLG_ONDISK,则从waiting_for_commit中删除,否则从waiting_for_applied中删除;然后根据waiting_for_commit/waiting_for_applied是否为空,来调用对应的回调函数,在完成的情况下发送消息给客户端;

对于0.80.10版本,没有MSG_OSD_REPOP,只有MSG_OSD_SUBOP

ceph OSD读写流程(2)–主OSD的写操作事务的处理

发表于 2015-11-25 | 分类于 ceph

journal-trans

  • OSD中写操作的处理中涉及到很多回调函数(这也是ceph本身的一个特点),这些回调函数追溯到Context类,这个类是回调函数类的抽象基类,继承它的类只需要在子类中实现它的finish函数,然后再finish函数中调用自己注册的回调函数,就能完成回调;
  • 上面说到的调用Context类的finish函数就能进行回调,那么具体调用它的地方就在Finisher类的finisher_thread_entry里,这个是finisher的线程处理函数,并且finisher还有一个finisher_queue,实现生产者消费者模型,生产者往finisher_queue里放东西,并通过条件变量通知finisher的线程处理函数来进行处理,在这个线程处理函数里就能通过调用Context类的complete函数,然后调用掉其子类实现的finish函数,从而完成回调的处理;
  • OSD的写操作先放到writeq里,通知FileJournal的线程处理函数进行写journal的处理,然后再放入到journal的finisher_queue里,然后对应的finisher线程处理函数调用之前注册的回调函数(C_JournaledAhead对应的函调函数是FileStore::_journaled_ahead)进行处理,这里就分成两条线路进行下去:
    • 放入ondisk_finisher,进而触发ondisk_finisher的线程处理函数进行处理,就会调用到C_OSD_OnOpCommit的函调函数ReplicatedBackend::op_commit,在这里面会检查waiting_for_commit是否为空(如果是3副本,这里面就有3项,每完成一个副本的写journal操作,就会从waiting_for_commit里删除一个),如果为空,才调用C_OSD_RepopCommit的回调函数repop_all_committed,从而调用ReplicatePG::eval_repop发给客户端写操作完成;
    • 放入op_wq里,FileStore::op_tp线程池就会从op_wq中取出进行操作,在FileStore::OpWQ::_process去进行写到本地缓存的操作(FileStore::_write),并且在完成后FileStore::OpWQ::_process_finish中处理,类似的也是放到一个finisher_queue里(op_finisher),然后finisher的线程调用C_OSD_OnOpApplied的回调函数(ReplicatedBackend::op_applied)来处理,如果waiting_for_applied为空(如果是3副本,这里面就有3项,每完成一个副本的写本地操作,就会从waiting_for_applied里删除一个),才调用C_OSD_RepOpApplied的回调函数repop_all_applied,进而调用ReplicatePG::eval_repop发给客户端数据可读;

ceph OSD读写流程(1)–主OSD的处理流程

发表于 2015-11-25 | 分类于 ceph

本文基于hammer版本对OSD的读写流程进行分析,对于消息的接收处理后续会单独进行分析。
primary-path
具体的处理流程从图中就可以体现出来,这里就不作过多的描述,需要提的几点:

  • 1)读请求由Primary OSD来进行处理;
  • 2)对于写请求,Primary OSD先发消息到副本OSD,然后记录PGLog(在log_operation里只是构造transaction,真正写到盘是和journal一起写的),然后再生成本地事务进行本地写的处理;
  • 3)写请求涉及到两步操作,一个是写journal,一个是写到本地缓存(page cache),对于每一个副本都有这两步操作,每个副本都是先写journal,然后再写到本地缓存,如果是3副本,就涉及到6次写操作;
  • 4)Primary OSD创建了2个回调函数来处理写journal和写到缓存(分别是C_OSD_RepopCommit和C_OSD_RepOpApplied),主副本的写和从副本的写没有先后顺序,有可能主的journal先写完,也有可能从的journal先写完,ceph不管这个顺序,只要保证3副本都写完了之后才返回客户端响应(degrade情况下例外),3个副本的journal写完成(all_commit),会返回客户端“写操作完成”,而3个副本都写本地缓存完成后(all_applied),才返回客户端“数据可读”;

ceph rbd中的对象解析

发表于 2015-10-20 | 分类于 ceph

在使用ceph rbd块设备时,创建一个rbd image可以指定格式:–image-format=1或者–image-format=2,官方文档给出了如下解释

  • format 1 - Use the original format for a new rbd image. This format is understood by all versions of librbd and the kernel rbd module, but does not support newer features like cloning.
  • format 2 - Use the second rbd format, which is supported by librbd (but not the kernel rbd module) at this time. This adds support for cloning and is more easily extensible to allow more features in the future.

默认的image格式是format 1,这种格式支持所有版本的librbd和内核rbd模块,但是不支持新的功能如克隆;而format 2由librbd支持,但是不支持内核rbd模块,这种格式支持克隆,并且便于扩展后续其他功能。

1. format 1格式的对象解析

首先创建一个format 1格式的image

1
2
3
root@ceph1:~# rbd create test_vol --size=10
root@ceph1:~# rbd -p rbd ls
test_vol

然后使用rados命令看到对应产生一个test_vol.rbd的对象

1
2
3
root@ceph1:~# rados -p rbd ls
test_vol.rbd
rbd_directory

其中rbd_directory是保存该存储池里的image列表
从下面的命令可以看到对于format 1格式的image,没有将image name和id的映射保存到rbd_directory里

1
2
root@ceph1:~#rados -p rbd listomapvals rbd_directory
root@ceph1:~#

阅读全文 »

如何使用新的glibc来编译自己的程序

发表于 2015-08-25 | 分类于 tools

通常情况下我们都是直接使用glibc提供的一些库函数,但是某些特殊的情况,比如要修改glibc的一些代码或者加入glibc的一些编译选项或者要使用其他版本的glibc,我们就需要重新编译glibc。
编译glibc时特别要注意,不能去替换系统自带的glibc,因为glibc作为linux系统的核心库,很多底层模块都依赖它,稍有不慎就会把系统搞挂掉。因此我们编译glibc时最好要指定prefix=/new/path,
这样编译完了之后make install的时候就不会去覆盖系统自带的版本。那么问题来了,编译好glibc后,如何让我们的程序使用这个新编译的glibc呢?下面就以我们实际编译glibc的步骤来说明。

1.下载glibc源代码

http://www.gnu.org/software/libc/,在官网上有各个glibc的发行版,这里我选用的是glibc-2.17

2.解压glibc到当前目录

1
root@xxx:~# tar zxvf glibc-2.17.tar.gz

3.创建glibc的build目录

不能在glibc-2.17源码目录下./configure,会报错“configure: error: you must configure in a separate build directory”

1
root@xxx:~# mkdir -p build/glibc-build

阅读全文 »

IET架构解析

发表于 2015-08-20 | 分类于 storage , iscsi

IET(iSCSI Enterprise Target)是内核态实现的iscsi target,相比于用户态实现的target(比如tgt),iet比较稳定,并且也算是历史悠久,io都直接经过内核态,性能比较好。本文主要针对IET的程序架构,从程序模块,命令操作处理流程,initiator与target的交互,I/O处理流程进行分析。

1.程序模块

IET包含3部分:ietadm,ietd,iscsi_trgt,其中ietadm是命令行工具,提供target,lun的创建、删除、参数设置等操作;而ietd是一个用户态程序,用于和ietadm以及initiator的交互,并进行对应的处理,iscsi相关的发现、登录、认证、登出等操作都在用户态实现;iscsi_trgt是一个内核模块,iscsi协议的解析处理,I/O处理都在这个内核态模块内完成。

1)ietdadm与ietd通过IPC进行交互,IPC在ietd程序启动的时候进行初始化;
2)ietd启动的时候还会创建监听socket(默认的端口是3260,并且会为该机器上的每个ip都生成一个监听socket,initiator在发现target的时候会指定ip和port),initiator就通过socket与ietd进行交互,一个initiator和一个target建立起一个长连接(iscsi协议标准说的是至少1个连接),后续的I/O都是通过这个连接进行交互;
3)ietd与iscsi_trgt使用ioctl进行交互,一些操作(比如创建删除target,创建删除lun等)都由ietd在用户态进行处理后,将命令通过ioctl发到iscsi_trgt,然后iscsi_trgt在内核态下进行相关处理;

阅读全文 »

Journal-guided Resynchronization for Software RAID

发表于 2015-08-06 | 分类于 paper

该文章主要讲把日志文件系统(如ext3)与底层的软raid结合起来,引入declared mode,在写入数据前,先记录日志,然后在意外崩溃的时候能够根据日志来判断哪些数据是不一致的,从而减少了全量同步,缩短了处于故障恢复状态的时间。

为了实现这个功能,为软raid增加了一个新的接口,verify read,在收到这种请求时,先进行数据的校验判断,如果有不一致的数据,则重写镜像或校验数据来保持一致。
如果把日志记录到该阵列中,会降低阵列的性能,高端阵列一般是采用一个NVRAM来记录日志,
另一种方式是把日志记录到阵列的bitmap中,不过这样都会影响性能。(就是mdraid的bitmap机制,http://www.linuxsymposium.org/archives/OLS/Reprints-2003/LinuxSymposium2003.pdf#page=109)

这篇文章主要讲的就是利用文件系统的恢复机制,来解决软raid崩溃时全量同步的问题。
这里是基于日志的文件系统(如ext3之类的),在写数据到磁盘之前,先记录日志,然后再写数据,写成功后再记录commit日志。
这样在文件系统崩溃时可以根据日志来进行恢复,日志中已经commit成功的是不需要恢复的。
为了利用这个机制,为软raid引入veryfy read,就是恢复的时候,文件系统向软raid发veryfy read请求,就是在buffer head中增加一个raid synchronize的标记,软raid在收到带有这个标记的请求时,才进行检查(raid1是检查源盘和镜像盘对应的数据是否一致,raid5是检查校验块和由数据块计算的校验值是否一致),如果不一致,就进行同步(raid1让各个镜像盘的对应数据一样,raid5是将新计算的校验值写入校验块中)。这样就避免了软raid崩溃时,全盘扫描进行比对了。
局限在于:
1)必须是基于日志的文件系统,而且必须是软raid上建的文件系统,如果软raid上没有文件系统,而直接裸盘使用,那么就不适用。
2)对于跨网络的软raid(使用iscsi或nbd等方式),文章中的在请求上加了一个synchronize标记还不一定可行,因为需要iscsi协议或nbd里有空闲字段用于存放synchronize标记才行。
值得借鉴的地方:
1)能够根据请求的类型来判断是否进行软raid的数据恢复,而不是进行全盘扫描;
2)对于云硬盘来说,目前采用的bitmap机制其实就是把日志记录到bitmap中(文章中也有提到),不过在整个机房断电重启这种极端情况,bitmap也丢失不可用了,目前就只能全盘扫描进行同步(这里的全盘扫描进行同步是直接将一块盘的数据完全拷贝到另一块盘,还是需要根据chunk块大小进行比对,如果是一样的,就不进行数据拷贝,不过也必须先从两个镜像中都读出数据来,目前认为是第二种),因为我们使用的extern bitmap,这种极端情况下,bitmap不可用,如果是intern bitmap,那对于这种情况也是可以不用全量同步的,只不过intern bitmap是存放在阵列本身,对于正常请求来说开销较大。

文件系统磁盘布局与I/O映射

发表于 2015-08-06 | 分类于 storage

本文主要对文件系统的磁盘布局进行概要的梳理,并在此基础上分析文件系统I/O到块I/O的映射机制。

1.文件系统磁盘布局

首先文件系统层面来阐述文件与块设备的关系。

1.1文件描述符与inode

应用程序在访问文件时都会先打开文件,在内核中,对应每个进程,都会有一个文件描述符表表示这个进程打开的文件,但是用户程序不能直接访问内核中的文件描述符表,而只能使用文件描述符表的索引(一个整数),这些索引就被称为文件描述符。当调用open 打开一个文件或创建一个新文件时,内核分配一个文件描述符并返回给用户程序,该文件描述符表项中的指针指向新打开的文件。

文件描述表中每一项都是一个指针,指向一个用于描述打开的文件的数据块–file对象,file对象中描述了文件的打开模式,读写位置等重要信息,当进程打开一个文件时,内核就会创建一个新的file对象。需要注意的是,file对象不是专属于某个进程的,不同进程的文件描述符表中的指针可以指向相同的file对象,从而共享这个打开的文件。file对象有引用计数,记录了引用这个对象的文件描述符个数,只有当引用计数为0时,内核才销毁file对象,因此某个进程关闭文件,不影响与之共享同一个file对象的进程。

file对象中包含一个指针,指向dentry对象。dentry对象代表一个独立的文件路径,如果一个文件路径被打开多次,那么会建立多个file对象,但它们都指向同一个dentry对象。inode对象代表一个独立文件,inode 对象包含了最终对文件进行操作所需的所有信息,如文件系统类型、文件的操作方法、文件的权限、访问日期等。

阅读全文 »
1234
Wu Dong

Wu Dong

Diary Notebook

32 日志
6 分类
27 标签
RSS
© 2017 Wu Dong
由 Hexo 强力驱动
主题 - NexT.Mist