分享

System Notes

 hello_worldw6 2017-08-24

发表于 | 分类于

three-iscsitargets-io-flow

three-iscsitargets-io-flow

three-iscsitargets-io-flow

three-iscsitargets-io-flow

三种I/O模型对比

上面3种target的I/O处理流程实际上就是对应常见的3种I/O模型。

  • 1个连接对应1个网络线程+n个I/O线程:这种模型首先需要1个线程来处理所有的connect请求,然后连接建立的时候才能去创建该连接的网络线程及I/O线程(在IET的实现中,用户态的ietd担任这个角色)。这种模型的好处是每个连接有自己的网络线程,各个连接之间不干扰,并且该连接上的I/O处理有专门的I/O线程负责,网络线程只用做好网络收发及协议解析相关的操作,各司其职,并且可以利用多cpu的资源,充分发挥cpu的利用率;缺点就是当连接比较多的时候很耗资源;
  • epoll+多个I/O线程:epoll是现在比较流行的处理模型,一个epoll的处理能力大概10万qps,如果在epoll线程里只是处理网络包及协议的解析之类的,那么epoll的能力足够了,不过如果在里面有涉及到I/O的操作,就有可能处理时间比较长导致其他连接上的请求无法响应,因此将I/O处理单独出来比较好。epoll的有个缺点就是只能利用单个cpu的资源,如果需要更高的处理能力,单epoll的方式会成为瓶颈,一个比较好的扩展方式是多epoll+多个I/O线程,将不同连接分配到不同的epoll线程去处理(比如采用hash的方式分配),然后I/O仍然由另外的I/O线程来处理;
  • 1个连接对应2个线程(receive和send):这个是比较过时的做法,好处就是比较简单,明显的缺点就是连接多了会导致线程资源占用过多给系统带来瓶颈。

发表于 | 分类于

open-iscsi实现了iscsi的自动重连,当target所在机器重启或者target的网络异常时,open-iscsi都会一直尝试connect,一旦当target重建出来后,open-iscsi就能够重新login成功。但是测试时发现,当频繁执行iscsiadm的命令时,会导致open-iscsi不去进行重连了。

观察到的现象

当target异常时,strace跟踪iscsid进程会发现一直尝试重连,不过一直失败。

1
2
3
4
5
6
$ sudo strace -e connect -p 28022 -t
Process 28022 attached - interrupt to quit
15:19:31 connect(10, {sa_family=AF_INET, sin_port=htons(3260), sin_addr=inet_addr("10.180.0.30")}, 128) = -1 EINPROGRESS (Operation now in progress)
15:19:34 connect(10, {sa_family=AF_INET, sin_port=htons(3260), sin_addr=inet_addr("10.180.0.30")}, 128) = -1 EINPROGRESS (Operation now in progress)
15:19:37 connect(10, {sa_family=AF_INET, sin_port=htons(3260), sin_addr=inet_addr("10.180.0.30")}, 128) = -1 EINPROGRESS (Operation now in progress)
15:19:40 connect(10, {sa_family=AF_INET, sin_port=htons(3260), sin_addr=inet_addr("10.180.0.30")}, 128) = -1 EINPROGRESS (Operation now in progress)

模拟频繁执行iscsiadm的命令

1
$ for i in {1..10000};do sudo iscsiadm -m session -P3 2&> /dev/null;done

这个时候去strace跟踪会发现没有去重连target的信息,反而是一些奇怪的记录。

1
2
3
4
5
17:24:12 connect(9, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
17:24:13 connect(9, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
17:24:14 connect(9, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
17:24:14 connect(9, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
17:24:15 connect(9, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)

发表于 | 分类于

经过前面的分析测试,使用fio直接测试裸盘(direct=1)的时候,将后端设备suspend住后,fio的IO hang的时间跟前面计算的一致;但是当在虚拟机里对这块盘做了文件系统,然后fio测试文件系统(direct=1)的时候,将后端设备suspend住后,fio的IO hang的时间多了20~30s。

初步分析

一开始怀疑是由于虚拟机这层的作用,因此还调整过KVM使用的盘的缓存策略(none、writethrough、directsync),同样的测试,仍然是IO hang比预期的多20~30s。

打开iscsi session debug开关,开启scsi error和timeout的日志,然后分析这样测试过程中的日志信息。下面截取部分日志,发现有3个时间点:16:45:21、16:45:31、16:45:41比较奇怪,没有连续的日志,都有10s的空白期。而且从16:45:41到16:45:51后,就是”Device offlined - not ready after error recovery”,而之前从裸盘上测试时是没有这几次10s的空白期,而且是I/O error,而不是device offlined后再I/O error。

发表于 | 分类于

近期线上系统出现坏盘导致IO卡住的问题,有两种情况:

1)一种是出现坏盘过程中raid卡的行为有所异常,在这台机器上的执行raid卡的命令megacli都卡住,观察到这台机器上的物理盘的io都时不时异常繁忙(io不大,但是svctm甚至达到几千ms),从而导致我们的块存储的卷IO hang住,表现就是用户的卷io util 100%较长时间;

2)另外一种情况是坏盘后,将坏盘从raid卡中剔除(做的单盘raid0,默认WB缓存策略,盘坏后需要从raid卡里删除逻辑卷),这台机器上的物理盘都io卡住一会,并且megacli命令也卡住。从而也导致部分用户的卷io util 100%较长时间;

情况1)通过收集日志反馈给厂家确认是由于线上机器的raid卡的固件版本比较低,需要升级raid卡固件;
情况2)因为坏盘后,raid卡就进入保护模式,物理盘的缓存策略都从writeback变成writethrough,然后剔除坏盘时,就会将raid卡缓存里的数据进行回刷到磁盘上,这个过程中是会有短时间的物理盘io卡住;

上面两种情况,物理盘的io卡住都在1分钟内,可是从用户反馈来看用户卷的io卡了好几分钟,比物理盘卡的时间长不少。另外,我们这个线上环境的块存储采用的2副本,有些卷的其中一个副本落在坏盘的机器上,卡了这么久为什么没有副本降级。

发表于 | 分类于

分布式系统中经常需要考虑对象(或者记录、文件、数据块等)的读写的顺序以及并发访问问题。通常来说,如果两个对象没有共享的资源,就可以进行并发的访问,如果有共享的部分,就需要对这部分资源进行加锁;而对于同一个对象的并发读写(尤其是并发写更新时),就需要注意顺序性以及并发访问的控制,以免数据错乱。本文主要对ceph中对象读写的顺序及并发性保证机制进行介绍。

PG的概念

ceph中引入PG(Placement Group)的概念,这是一个逻辑上的组,通过crush算法映射到一组OSD上,每个PG里包含完整的副本关系,比如3个数据副本分布到一个PG里的3个不同的OSD上。下面引用ceph论文中的一张图就可以比较直观的理解了。将文件按照指定的对象大小分割成多个对象,每个对象根据hash映射到某个pg,然后再根据crush算法映射到后端的某几个osd上。
ceph-io-sequence

不同对象的并发控制

不同的对象有可能落到同一个pg里,ceph实现里,在OSD的处理线程中就会给PG加锁,一直到queue_transactions里把事务放到journal的队列里(以filestore为例)才释放PG的锁。从这里可以看出,对于同一个PG里的不同对象,是通过PG锁来进行并发的控制,好在这个过程中没有涉及到对象的I/O,不会太影响效率;对于不同PG的对象,就可以直接进行并发访问。

发表于 | 分类于

前一篇《ceph存储引擎bluestore解析》已经对bluestore整体架构和I/O映射逻辑进行了阐述,本文主要从bluestore的工作处理流程进行解析。

1.整体流程

bluestore-work-flow
图中展示了流程中的关键路径及涉及到的线程与队列。下面详细阐述工作流程。

发表于 | 分类于

ceph后端支持多种存储引擎,以插件式的方式来进行管理使用,目前支持filestore,kvstore,memstore以及最新的bluestore,目前默认使用的filestore,但是因为filestore在写数据前需要先写journal,会有一倍的写放大,并且filestore一开始只是对于机械盘进行设计的,没有专门针对ssd做优化考虑,因此诞生的bluestore初衷就是为了减少写放大,并针对ssd做优化,而且直接管理裸盘,从理论上进一步减少文件系统如ext4/xfs等部分的开销,目前bluestore还处于开发优化阶段,在jewel版本还是试用版本,并且最新的master相比jewel已经做了大的重构,预期会在后续的大版本中稳定下来成为默认的存储引擎。本文基于master分支对bluestore存储引擎进行分析。

bluestore整体架构

ceph-bluestore

bluestore直接管理裸设备,抛弃了ext4/xfs等本地文件系统,BlockDevice实现在用户态下使用linux aio直接对裸设备进行I/O操作。既然是直接管理裸设备就必然需要进行裸设备的空间管理,对应的就是Allocator,目前支持StupidAllocator和BitmapAllocator两种分配器。相关的元数据以kv的形式保存到kv数据库里,默认使用的是rocksdb,由于rocksdb本身是基于文件系统的,不是直接操作裸设备,但是rocksdb也比较灵活,将系统相关的处理抽象成Env,用户可用实现相应的接口,rocksdb默认的Env是PosixEnv,直接对接本地文件系统,为此,bluestore实现了一个BlueRocksEnv,继承自EnvWrapper,来为rocksdb提供底层系统的封装,为了对接BlueRocksEnv,实现了一个小的文件系统BlueFS,只实现rocksdb Env需要的接口,在系统启动mount这个文件系统的时候将所有的元数据都加载到内存中,BluesFS的数据和日志文件都通过BlockDevice保存到裸设备上,BlueFS和BlueStore可以共享裸设备,也可以分别指定不同的设备。

发表于 | 分类于

1.背景

在对ceph块存储进行性能测试时发现,当有osd重启或者存储机重启时,I/O性能会急剧下降,尤其在随机写的负载下,下降幅度达到90%,并且会持续一段时候才慢慢恢复到正常水平。一开始我们也尝试将恢复相关的参数调低(osd_recovery_max_active=1、osd_recovery_max_chunk=131072、osd_max_backfills=1),以及调整正常I/O和恢复I/O的优先级(osd_recovery_op_priority=10、osd_client_op_priority=63),但是测试结果来看,这些参数的调整没有多大的效果,随机写负载下性能下降仍然很大。

2.原因分析

在《ceph基于pglog的一致性协议》一文中分析了ceph的一致性协议,从中我们得知在osd重启、存储机重启等场景下基于pglog的恢复的时候,在peering的时候会根据pglog来构建出missing列表,然后在恢复时根据missing列表逐个进行恢复,恢复的粒度是整个对象大小(默认4MB,有可能有的对象不足4MB,就按对象大小),即使只修改了一个4KB,也需要将4MB的对象拷贝过来,这样100个io就会达到400MB的带宽,对网络及磁盘产生较大的影响。当写io命中正在修复的对象时,也是先修复原来4MB的对象,即需要将4MB的数据通过网络拷贝过来,延迟就会增加很多,然后再写入数据,对于随机写的场景尤其严重,基本都是命中的情况,带来相当大的延迟,从而iops下降(下降幅度80%~90%)。这个影响时间取决于上层的业务量和osd停服的时间。
ceph-recover-optimize

发表于 | 分类于

前段时间一直在研究ceph的快照以及其所涉及的故障恢复逻辑,ceph在这部分的实现挺精妙的,虽然也是基于COW实现的,但是细细品来,其在快照的元数据管理以及父子关系的维护上有独到之处,本文将详细阐述快照的实现原理以及快照对象在恢复时如何至关重要的作用。

1.rbd快照的直观理解

ceph的rbd卷可用做多次快照,每次做完快照后再对卷进行写入时就会触发COW操作,即先拷贝出原数据对象的数据出来生成快照对象,然后对原数据对象进行写入。直观图解如下:
rbd-flow
做快照的操作是很快的,因为只更新了卷的元数据,添加了一些快照信息(包括该卷有哪些快照,快照id,如果这个卷是克隆出来的,那么还包括parent信息)。每个rbd卷在rados里都有一个rbd_header对象,这个rbd_header对象里面没数据,卷的元数据都是作为这个对象的属性以omap方式记录到leveldb里(可以使用rados -p listomapvals rbd_header.来获得快照相关的元数据信息)。

当做完快照后,如果对原卷进行写入的时候,就会先拷贝数据出来生成快照对象(在代码实现里就是一个clone操作),拷贝的时候是整个对象的数据(有可能这个数据对象不是默认的4MB大小,那么有多大拷多大),然后再写入新数据。在ceph里,卷的原对象叫做head对象,而卷作为快照后通过cow拷贝出来的快照对象称为snap对象
因为ceph里对象是写时分配的,一个新创建的卷如果每一写过数据,在rados里是没有生成数据对象的,只有当数据写入时,才分配出数据对象来。

发表于 | 分类于

分布式存储系统通常采用多副本的方式来保证系统的可靠性,而多副本之间如何保证数据的一致性就是系统的核心。ceph号称统一存储,其核心RADOS既支持多副本,也支持纠删码。本文主要分析ceph的多副本一致性协议。

1.pglog及读写流程

ceph使用pglog来保证多副本之间的一致性,pglog的示意图如下:pglog主要是用来记录做了什么操作,比如修改,删除等,而每一条记录里包含了对象信息,还有版本。
ceph使用版本控制的方式来标记一个PG内的每一次更新,每个版本包括一个(epoch,version)来组成:其中epoch是osdmap的版本,每当有OSD状态变化如增加删除等时,epoch就递增;version是PG内每次更新操作的版本号,递增的,由PG内的Primary OSD进行分配的。
pglog-entry
每个副本上都维护了pglog,pglog里最重要的两个指针就是last_complete和last_update,正常情况下,每个副本上这两个指针都指向同一个位置,当出现机器重启、网络中断等故障时,故障副本的这两个指针就会有所区别,以便于来记录副本间的差异。

为了便于说明ceph的一致性协议,先简要描述一下ceph的读写处理流程。
写处理流程:
1)client把写请求发到Primary OSD上,Primary OSD上将写请求序列化到一个事务中(在内存里),然后构造一条pglog记录,也序列化到这个事务中,然后将这个事务以directIO的方式异步写入journal,同时Primary OSD把写请求和pglog(pglog_entry是由primary生成)发送到Replicas上;
2)在Primary OSD将事务写到journal上后,会通过一系列的线程和回调处理,然后将这个事务里的数据写入filesystem(只是写到文件系统的缓存里,会有线程定期刷数据),这个事务里的pglog记录(也包括pginfo的last_complete和last_update)会写到leveldb,还有一些扩展属性相关的也在这个事务里,在遍历这个事务时也会写到leveldb;
3)在Replicas上,也是进行类似于Primary的动作,先写journal,写成功会给Primary发送一个committed ack,然后将这个事务里的数据写到filesystem,pglog与pginfo写到leveldb里,写完后会给Primary发送另外一个applied ack;
4)Primary在自己完成journal的写入时,以及在收到Replica的committed ack时都会检查是否多个副本都写入journal成功了,如果是则向client端发送ack通知写完成;Primary在自己完成事务写到文件系统和leveldb后,以及在收到replica的applied ack时都会检查是否多个副本都写文件系统成功,如果是则向client端发送ack通知数据可读;
对读流程来说,就比较简单,都是由Primary来处理,这里就不多说了。


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多