Facebook团队关于Hadoop/HBase在SSD上的实验和讨论

硬件技术的发展给存储和数据库软件技术提供了新的机会。近年来SSD开始流行,那么SSD能否给Hadoop/HBase带来性能的提升呢?来自Facebook数据团队的工程师们做了相关的研究和实验工作。

本文是http://hadoopblog.blogspot.com/2012/05/hadoop-and-solid-state-drives.html (需自备梯子)的翻译并加上了一些自己的思考,版权归原博客作者所有。

先说下SSD吧,SSD没有传统机械硬盘的机械寻道时间而带来的延迟,所以IOPS性能可以达到100-200K(而15K的SAS一般在100左 右),所以能提供相对于机械硬盘100+倍的的小文件读取性能,而且在连续读取方面也能带来3倍左右的性能提升。但是在写入性能方面,SSD由于擦除等因 素的存在,导致写入性能提升并不是很大,而且顺序写性能明显由于随机写,所以实践中要尽量避免随机写SSD。SSD的读写性能差距较大,而机械硬盘读写性 能差距不大。

Use case:

Hadoop在Facebook主要有两种使用场景:基于MapReduce的OLAP类分析型应用和简单的key-value存储。

前者的场景下 数据基本上是顺序读取的,不会所以用SSD可能获益不多。多数的MapReduce任务是CPU密集型的 (decompression,deserialization等),瓶颈在于data shuffle过程中的map-output-fetch,加快从HDFS中读取文件的IO速度并不能显著提高MapReduce任务的执行速度。可以把 Map任务的输出写到SSD上,这样能够加快data shuffle中map-output-fetch的速度 ,所以说这是一个优化思路。

对于后者的场景,SSD能够提升online-transaction-process-workload的速度是有理论依据的,所以说也是一个优化思路。

Background:

SSD的读写延迟和机械硬盘相比是有数量级的差距的,特别是在随机读写的时候。例如在随机读上,SSD的随机读取延迟大概是30 micro-seconds(微秒),而机械硬盘随机读取延迟大概在5-10 milli-secondes(毫秒)。SSD能支撑100k-200k的OPS,而机械硬盘仅能支撑200-300的OPS。这意味着随机读写在SSD 上不是瓶颈。(这是Dhruba Borthakur的博客中的说法,但是我认为随机写在SSD上还是要尽量避免的。)另外一方面,现有的数据库架构都是基于机械硬盘设计的,没有考虑到 SSD的IO代价模型。那么SSD能否给现有基于机械硬盘设计的数据库带来性能的提升呢?Facebook的数据团队做了实验。他们分别做了基于HDFS 和HBase的随机读取实验,看到底SSD能够跑出什么样的性能。

HDFS random-read on cached data:

这个实验中,他们创建了一个两节点的集群,一个NameNode,一个DataNode。创建一个2G的HDFS文件,文件的block大小是 256M,拷贝数是1,所以一共有8个block。DataNode配置了CPU超线程,同时本地文件系统用的是XFS存放block文件。 Benchmark程序也在DataNode上跑,同时打开了hdfs-read-shortcircuit功能(https://issues.apache.org/jira/browse/HDFS-2246), 这样DFSClient就会绕过与DataNode的网络通信而直接从本地磁盘文件系统读取数据。把这2G文件都放在操作系统的缓存中,所以整个测试过程 没有磁盘IO。用这样的测试场景来模拟SSD的。然后分别用不同的client线程数测试,每个client线程从HDFS中读取16K的数据。由于一共 就8个block,所以DFSclient缓存了所有的BlockLocations,所以这个过程没有与NameNode的通信。

在开始的几轮实验中,最大的随机读吞吐量大概在50K OPS,但是CPU还很空闲。他们发现在hdfs-read-shortcircuit过程中花费了非常可观的时间在DNS查询和metric- counters的更新上。在修改了这部分的代码之后,他们的最大随机读吞吐量已经达到了 92K OPS,同时CPU接近95%的利用率。下图是实验结果,从中可以看出基于HDFS的数据库并不能完全利用SSD提供的IOPS。

然后他们用profile的形式来跑HDFS的代码显示,DFSClient端的代码路径太长了,对缓存的数据随机读取产生了相当大的影响。使用机 械硬盘的数据读取磁盘IO时间在毫秒的数量级,那么DFSClient读取流程的开销可能不是那么明显。但是当数据被存储在操作系统的cache或者在 SSD中时,DFSClient的读取流程的开销就比较大了,这部分需要重新设计读取流程。另外一个思路是用C/C++实现一个read-only的 client,避免现在基于Java实现的DFSClient的开销。

HBase random-get on cached data:

然后他们做了类似的实验在HBase上。创建了一个表,只有一个region,并且这个region所有的数据都是缓存在一台 RegionServer操作系统的cache中。同样用这样的方法模拟SSD。用4个客户机做random-get操作。RegionServer配置 最大2000个线程,HBase的table使用lzo压缩算法并开启delta-encoding-fast-diff功能。同样整个过程没有磁盘 IO。HBase的吞吐量在35K OPS左右,并且同时CPU使用没有超过45%。在RegionServer上严重的锁竞争和上下文切换使得CPU的利用率比较低。实验结果如下图所示。

What dose this mean

两个实验表明,HDFS和HBase都不能完全利用SSD提供的高效IOPS。所以要想把HDFS/HBase用在SSD上跑出很好的性能,需要对 代码进行重构,但是这需要花大量的时间。而且这个结论同样适用于其他数据库应用,因为现有数据库都是基于机械磁盘的IO代价模型设计的,不适用与SSD硬 盘。能够完全利用SSD磁盘的数据库架构需要从头开始设计。

也有些人对这个实验提出了一些讨论和意见:

Q:35K OPS的HBase操作代表着多少次磁盘IO的OPS,一般情况下一次HBase操作并不是一次磁盘IO操作,也就是所谓的IO放大(IO amplification)。大多数数据库IO amplification大概是1:5到1:10,主要包括record相应的索引的查询和更新,transaction commit,index rebuild, multi-versioning checkpoints, archiving, WAN replication等。同时IO amplification也是负载相关的。

A:35K HBase OPS就代表着35K的磁盘IOPS,因为workload是纯随机读取,没有其他操作。

Q:可以考虑在HDFS中增加存储设备信息作为API开发给上层。例如对于HBase应用来说,把数据文件存放在机械硬盘上,把WAL和flush file等生命周期短、经常访问的数据存放在SSD上。有点类似于EMC他们经常说的分层存储的概念。

A:这种思路很好,可行性也很高。但是这样的话,瓶颈就会落到CPU核心数目越来越多,这些核心不能被高效利用。(http://pdos.csail.mit.edu/multicore/ 这个项目研究了操作系统和软件在多核处理器上的可扩展性问题)

Q:有人提出混合存储,可以把HDFS的block的第一个备份存在SSD上,另外两个备份存在机械硬盘上。甚至可以指定哪个表,哪个 Region,哪个HFile block存放在SSD上。然后读取的时候首先尝试在SSD上的数据,这样就可以显著提升总的读取性能,而且不降低reliability。

A:混合存储的思路也很好,但是一个程序只能从HDFS DataNode上获得16K左右的随机读取OPS,并不能完全利用SSD提供的IOPS潜能。

Q:对于SSD来说,单一进程不能跑满SSD的IOPS,是否可以考虑多个进程实例来共享SSD所能提供的所有的200K+的IOPS呢?

A:是的,事实上在Facebook就是在每个SSD的机器上跑多个进程实例,为的就是提高IOPS的利用率。

总结与感想:SSD改变了传统的基于机械硬盘的IO代价模型,那么对于基于机械硬盘设计的数据库应用来说需要重构了。对于HDFS和HBase应用来说,需要优化的空间也很大,特别是LSM(http://www.springerlink.com/content/rfkpd5yej9v5chrp/?MUD=MP)模型的提出和使用,给HBase设计和优化提供了很大的空间。同时SSD带来了磁盘IOPS的 数量级的提升,但是软件逻辑中锁操作使得CPU并不能跑满,所以就提出了单台server跑多个实例的概念,即多进程的概念。Redis也是单线程逻辑, 单机多进程部署,从中可以看出软件特别是数据库软件开发的一种回归。多线程程序锁的争用导致CPU利用率低下,使得很多程序开始考虑单线程、多进程的概 念,而且降低debug的成本,提高了鲁棒性。听说Amazon推出的DynamoDB也是基于SSD的,后面看看它是怎么用的。

对debian下的exim4添加DKIM支持

exim是一款常见的邮件发送软件, 用自带的apt-get安装的exim4版本是不支持DKIM,我们必须要对其源码进行改造才行。
这里介绍一下在debian环境下对exim进行DKIM(DomainKeys Identified Mail)邮件签名的支持。

# apt-get install libdkim-dev dpatch

在/etc/apt/sources.list中添加:

deb-src http://ftp.us.debian.org/debian lenny main contrib non-free

添加必要的软件:

# cd /usr/src
# apt-get –force-yes -y install build-essential devscripts docbook-xsl xsltproc docbook-xml libpcre3-dev libldap2-dev libpam0g-dev libident-dev libdb4.6-dev libxmu-dev libxt-dev libxext-dev libx11-dev libxaw7-dev libpq-dev libmysqlclient15-dev libsqlite3-dev libperl-dev libgnutls-dev libsasl2-dev
# apt-get source exim4
# cd exim4-4.69/
# fakeroot debian/rules unpack-configs

然后复制配置文件

# cp EDITME.exim4-heavy EDITME.exim4-custom
# echo “EXPERIMENTAL_DKIM=yes” >> EDITME.exim4-custom
# echo “LDFLAGS += -ldkim” >> EDITME.exim4-custom
# fakeroot debian/rules pack-configs

修改
vi debian/rules
找到extradaemonpackages这行,修改为:
extradaemonpackages=exim4-daemon-heavy exim4-daemon-custom
为了让exim找到libdkim,还需要

echo “80_libdkim_patch” >> debian/patches/00list
cd debian/patches
wget ‘http://bugs.debian.org/cgi-bin/bugreport.cgi?msg=5;filename=99_libdkim_1.0.19.dpatch;att=1;bug=486437′ -O – | \
cat >> 80_libdkim_patch.dpatch

完成以上步骤,并无提示出错i后就可以编译了

debuild -uc -us

之后就可以得到安装包了:

# ls ../*.deb | grep exim4
../exim4_4.69-7_all.deb
../exim4-base_4.69-7_amd64.deb
../exim4-config_4.69-7_all.deb
../exim4-daemon-custom_4.69-7_amd64.deb
../exim4-daemon-custom-dbg_4.69-7_amd64.deb
../exim4-daemon-heavy_4.69-7_amd64.deb
../exim4-daemon-heavy-dbg_4.69-7_amd64.deb
../exim4-daemon-light_4.69-7_amd64.deb
../exim4-daemon-light-dbg_4.69-7_amd64.deb
../exim4-dbg_4.69-7_amd64.deb
../exim4-dev_4.69-7_amd64.deb
../eximon4_4.69-7_amd64.deb

安装包的顺序
dpkg -i exim4-config*.deb
dpkg -i exim4-base*.deb
dpkg -i exim4-daemon-light*.deb
dpkg -i daemon-custom*.deb
最后,修改配置文件/etc/exim4/exim4.conf.template在remote_smtp 处
添加:

dkim_selector = dkim
dkim_domain =  ${lc:${domain:$h_from:}}
dkim_private_key = /root/dkim.private.key
dkim_strict = 0
dkim_canon = relaxed

公司内网的QQ屏蔽

近日,我尝试了下在公司内网屏蔽QQ,相信很多网管也会有碰到这方面的需求,以下过程可以参考下

我们公司内部是用了LINUX服务器作为网关路由,因此我主要是采用iptables来做屏蔽。
QQ的登陆主要是解析登陆服务器域名,得到IP列表,然后再根据这些列表逐一尝试登陆的,由于现在QQ不单用到UDP协议,还可以通过TCP协议,不单有8000端口,还有80,443端口,所以我们需要把所有的能解析到的IP都过滤屏蔽掉,我的思路是解析,然后根据解析出来的IP列表屏蔽到IPTABLE里去,最后再屏蔽一些额外的不是通过域名解析得到的登陆服务器IP。

首先是把QQ禁用列表加到FORWARD里
iptables -N QQ-FILTER
iptables -I FORWARD -j QQ-FILTER
以下为自动加IP程序:

#! /bin/bash
iptables -F QQ-FILTER
host=(“tcpconn.tencent.com” “tcpconn2.tencent.com” “tcpconn3.tencent.com” \
tcpconn4.tencent.com tcpconn5.tencent.com sz.tencent.com sz2.tencent.com \
sz3.tencent.com sz4.tencent.com sz5.tencent.com sz6.tencent.com \
sz7.tencent.com sz8.tencent.com sz9.tencent.com tm.tencent.com \
tm2.tencent.com tm3.tencent.com tcptm.tencent.com tcptm2.tencent.com tcptm3.tencent.com )
now=`date`
for outer in ${host[*]}
do
nslookup $outer|grep Address|grep -v 127.0.0.1|awk -F ‘ ‘ ‘{ print $2 }’>temphost
i=0
cat temphost| while read line
do
cmd=”iptables -I QQ-FILTER -p all -d $line -i eth1 -j DROP”
$cmd
i=`echo $i+1 | bc`
done
echo “$now host:$outer ” >>banip.txt
done

allip=(“121.14.101.169″ “119.147.14.145″ “121.14.98.0/24″ “219.133.49.0/24″);
for outip in ${allip[*]}
do
cmd=”iptables -I QQ-FILTER -p all -d $outip -i eth1 -j DROP”
$cmd
done
iptables -I QQ-FILTER -p udp -d 0/0 –dport 4000:9000 -i eth1 -j DROP
iptables -I QQ-FILTER -p all -d 0/0 –dport 2680 -i eth1 -j DROP

ext4 workshop总结

2012年3月30日,我和刘峥一起去Mountain View参加了ext4 workshop,和google的ext4开发人员以及ext4 Maintainer Ted T’so一起进行了交流,会议进行的非常顺利,我们一起就淘宝感兴趣的ext4新特性以及ext4下一步的开发计划进行了讨论。

首先Ted T’so介绍了ext4以及bigalloc在google的应用,目前google所有的机器都已经运行在ext4上了,并且no-journal也已经占大多数。关于bigalloc,目前google主要使用模式是fallocate+direct sequential write。我们提到了在某些集群遇到使用fallocate后写放大的问题(一次写入由于元数据的修改可能会导致多次的写io),并提到了我们可能的解决方案(一种很hack的方法,让应用来保证总是先写入后读取),然而出乎我们意料的是google内部竟然也是这么干的,于是我们双方决定提出一个新的fallocate参数(FALLOC_FL_NO_HIDE_STALE),让这种hack的方案光明正大起来(不过可惜在后来的Linux Storage and file system summit上其他的文件系统开发人员对这个并不认可,所以可能最终我们只能在ext4内部来实现了)。

第二个议题是关于fsync的,当前extN系列的文件系统的fsync虽然用户可以选择相应的fd来做,但是到文件系统内部还是通过commit整个文件系统的journal来实现的,再加上data=ordered模式下需要写入对应的文件数据,导致fsync的代价很大,overhead也很多。因此我们就能否优化fsync使它仅作用于某一个文件,从而加速fsync进行了讨论。可惜的是,由于jbd/2本身机制的问题,这个的实现可能需要修改或者替换整个jbd2,所以暂时被搁置了。

第三个议题是关于extent tree cache的。xfs的开发人员Dave Chinner曾经指出由于extN文件系统是基于bitmap模式的,这样导致数据分配比较慢,另外每个extent的大小是有限制的(目前是128MB),文件太大以后extent数目会增多,用户如果随机读取文件会由于元数据的读入而变慢,董昊曾经提出修改extent的格式来突破extent大小的限制,可惜的是由于存在多余页面的写入问题,Ted T’so并不是很喜欢这个方案。google目前的做法是在内存中保存一个big extent tree,这样多个在硬盘中的extents可以在内存中被合并为一个big extent,这样读数据时必需的元数据读取就不需要走复杂的get_block逻辑,从而起到加速的效果(一个可行的实现是在打开文件的时候读入整个元数据并创建一棵big extent tree,从而使后续的文件元数据读取可以直接访问tree,避免多余的i/o操作)。我们对这个特性也很感兴趣,Ted表示可以让google的开发人员把代码整理一下,发到社区,我们后期可以在超大文件中使用。

第四个议题是extent io tree,这个实际上是第三个议题的衍生。big extent虽然解决了多余的元数据读取,但是对于delayed allocation,punch hole以及多线程同时读写操作仍然无能为力。我们需要一种in memory的io extent cache tree,它主要可以有一下几个作用:
1)更好的支持delayed allocation环境下的fiemap和punch hole。由于delayed allocation的一些特点,导致fiemap和punch hole都必须通过遍历page所含有的buffer_head来确认,非常低效,通过in memory cache可以解决这个问题。
2)更好的支持数据库等direct io型业务,数据库业务会同时发起多个dio read/write,目前direct read可以通过dioread_nolock来完成并发,但是对于direct write还需要串行。如果可以实现range lock,那么多个针对不同区域的direct write就可以同时进行。range lock可以作为in memory extent cache tree的一个附加产物被很容易的实现,从而最终提升数据库的并行读写能力。
3)优化现有的代码,使extent操作更加高效。
目前社区有几套方案,但是都有各自的优缺点和局限性,需要统一,我们也表达了淘宝对这个特性的关注。

第五个议题是关于文件系统分配策略的。我们在文件系统的调优中遇到一个问题,对不同特点的文件使用不同的分配策略,比如索引文件尽量往磁盘的开始处分配(那里的数据读写速度相对较快),数据文件可以往磁盘尾部分配。通过大家的讨论,一个可行的方案是添加O_HOT, O_COLD标志(比如上面的例子中索引文件使用O_HOT,数据文件使用O_COLD),这样文件创建和打开的时候可以显式的通知文件系统,从而在未来的磁盘分配中采取不同的策略。

第六个议题是关于data=journal模式的,redhat的开发人员提到data=journal模式有一些问题,并在实际的用户中很少使用,但是却增加了开发和测试人员的负担,Ted对此表示同意,目前大家的看法是以后可能会逐渐去除data=journal这种模式,所以我们以后也不会推荐这种模式了。

第七个议题是关于inline data。Ted对inline data的开发工作进行了询问,并表示下一步将开始review,并争取早日合并。

第八个议题是关于resize。目前ext4 resize在64 bits支持,bigalloc支持,flex_bg支持方面都还有很大缺陷,下一步需要进一步改进。

第九个议题是关于ext4 patch的review问题。目前Ted一个人在做review,导致速度会比较慢,以后可能会要求所有的开发人员都需要review他人的patch(这个是向xfs学习),另外在任何新特性开发的过程中需要使用xfstests做回归测试,从而保证开发质量。

最后,Ted对淘宝对ext4社区的贡献表示感谢,并邀请我们参加每周的例行会议,以便大家更好的沟通。

Linux的IO调度器-CFQ

最近由于一些控制IO带宽的需求,开始研究CFQ以及对应的IO cgroup,今天baidu了一下,竟然发现没有多少中文的介绍,所以准备写一个系列,介绍一下这个调度器,粗粗想了一下,大概可以灌四篇水,包括CFQ的基本介绍,CFQ各个配置参数的含义和调优,CFQ的基本架构以及CFQ+cgroup。各位看官要是觉得还有什么值得写的,请留言给我或者直接新浪微博 @淘伯瑜。闲话少说,言归正传。

CFQ是Completely Fair Queuing的缩写,顾名思义,他存在的主要目的就是为了保证公平性, 并为此做了大量的工作。为了说明他的公平性,让我们先来简单看看另外目前kernel另外两个IO调度器,noop和deadline。那么怎么看自己目前硬盘的调度器呢?sysfs里面就有:

cat /sys/block/sda/queue/scheduler 目前这个是设备相关的,不同的盘可以选择不同的IO调度器。

noop

这个调度器基本上什么也不做,没有自己的请求队列,不做排队,基本上是上层发什么请求就直接扔给驱动的队列(其实他也可以做简单的尾部合并,不过这个合并实际上是通用的block层代码实现,可能也不应该算在他的头上)。代码么,也只有100行左右,非常简单。对于一些非常快速的设备,如fusion IO的pci-e卡,noop的性能还是相当不错的。

deadline

这个调度器功能上有以下特点,有自己的请求队列,可以做前端合并,读写分离,并用红黑树来管理请求,红黑树的key是请求的读写位置,另外他还设置了fifo队列来管理请求的超时,这样对于一般的请求通过排序来照顾到顺序IO,并通过fifo来保证请求的响应时间。400多行的代码做到如此的简单高效,非常赞。另外一般的SSD读写混合时候效率很差,deadline使用的batch方式,读写请求的发送在某种程度上是分离的。从我们的测试来看,一般的SSD(如Intel的x25-m系列)使用deadline效果还是非常棒的。

CFQ

CFQ相对于上面两个那可真是巨无霸了,目前的block/cfq-iosched.c就有4000多行,这个还不包括blk-cgroup的1700行代码,这许多代码加起来的特性那就相当丰富了。下面咱们简单来理理。

  • CFQ把进程当成了基本的调度单位,也就是说各个请求现在都是属于进程的,CFQ调度的是进程,当选择了某个进程的时候,他的请求才能够被发送到设备,否则只能在自己的队列里面待着(说进程可能不太准确,这个实际上是一个task_struct里面有一个,所以可能说内核线程更准确一些,各位看官有印象就好了,下面我就不区分了)。
  • 优先级:进程被分成不同的类别,而且又有不同的优先级,具体的分类有点复杂,建议有兴趣的看看man ionice(不知道Jens Axboe同学当时为啥要分的如此细)。
  • 时间片:时间片是CFQ分给每个进程的基本单位,当CFQ选择了一个进程开始服务的时候,一般情况下他会给这个进程足够长的时间(slice_sync)发送请求,当该进程暂时没有请求的时候,会等待一段时间(slice_idle),这样如果他又发送新的顺序请求,就避免了不必要的磁盘seek(其实这个对SSD恰恰是有一定的副作用的,在后面的博文中会详细分析),然后再选择另外一个进程服务。当然如果有优先级高的进程,可以中断当前的进程,选择那个进程开始服务。
  • 带宽控制:可能这里的带宽的定义比较含糊,其实准确的来说,目前CFQ是通过时间片来控制的,所以通过给各个进程分配不同的时间片,CFQ期待能够尽量保持各个进程的带宽比例,并假设IOPS或者带宽能够和时间片线性相关。
  • 暂时就想到这么多了,各位如果还有啥好的,欢迎补充呀!

下一篇将介绍CFQ的各个参数的含义,各位感兴趣的可以先看看,主要的参数在这里:

@tma-laptop1:~/kernel/linux-2.6$ ls /sys/block/sda/queue/iosched/
back_seek_max      fifo_expire_async  group_idle   quantum      slice_async_rq  slice_sync
back_seek_penalty  fifo_expire_sync   low_latency  slice_async  slice_idle

free命令中的buffers和cached

最近有好几位同事问我关于free(1)结果中buffers和cached的区别,一直很佩服@淘宝褚霸 对知识的无私分享,所以今天就以这个话题开始我的blog吧,但愿有机会时常更新它,争取2012年能够做到至少一个月一篇吧,呵呵!闲话少说,进入正题。

free命令大家都经常使用,

taotaoma@tma-laptop1:~/kernel/linux-2.6$ free
total       used       free     shared    buffers     cached
Mem:       5918476    1675260    4243216          0     157664     819004
-/+ buffers/cache:     698592    5219884
Swap:            0          0          0

各个内容的显示应该还算是简单明了的,唯一比较让人困惑的两项就是buffers和cached。一般的认识都是说buffers指的是元数据的数量,而cached是指文件的page cache的数量。那么真的是这样么?

其 实现在的linux内核中对于缓存的管理都是以page的形式进行的,也就是说在系统底层只存在各种page,这些page保存在不同的tree 中,buffer这个概念实际上已经过时了,但是为了保持对过往系统的兼容性,linux内核中还保留了这个概念,并仍然用它来代表文件系统中的一些所谓的元数据,但是由于已经没有buffer了,那么free该怎么显示buffers呢?内核巧妙的利用了一个特性,那就是文件系统在读取元数据的时候一般都是通过它所对应的块设备来进行,也就是说元数据存储的page一般都是保存在块设备对应的tree中,而一般文件的page  cache则是保存在它的宿主文件的tree中。有了这个假设,我们就可以通过统计所有在块设备的tree上的page来得出系统的buffers数量。

这一块对应的代码是这样的(以2.6.32的代码为例):

free是通过读取/proc/meminfo里面的数据来显示的,而/proc/meminfo的具体实现代码在Linux内核中的fs/proc/meminfo.c里面。

sysinfo.bufferram是通过函数si_meminfo得到的,查看si_memeinfo可以看到这样一行:

val->bufferram = nr_blockdev_pages();

而对应的函数就很简单了

long nr_blockdev_pages(void)
{
struct block_device *bdev;
long ret = 0;
spin_lock(&bdev_lock);
list_for_each_entry(bdev, &all_bdevs, bd_list) {
ret += bdev->bd_inode->i_mapping->nrpages;
}
spin_unlock(&bdev_lock);
return ret;
}

遍历所有的块设备,然后把所有的块设备tree上的pages数目加起来,然后返回,是不是一目了然呢!

问题到这里真的就结束了么?非也非也,这样的算法明显很不精确,因为如果我直接操作裸设备,那内核如何区分呢?大家可以做一个简单的测试。

echo 3 > /proc/sys/vm/drop_caches

free

dd if=/dev/sda of=/dev/null bs=1M count=1000

free

相信大家一定都看到了buffers的剧烈增长了吧!由于你像文件系统一样直接通过块设备来读取设备的内容,所以内核把你这次操作读取的page都算作了buffers,也就从侧面验证了free命令中计算buffers所用的算法。

2012 linux storage, filesystem and mm summit总结(文件系统和i/o部分)

2012年的文件系统和存储峰会在4月1日和4月2日在旧金山举行,会议介绍在这里,主要分为文件系统,IO系统和内存管理系统三块,由于是邀请制,会议的含金量还是很高的,全球在这三个方面的大约80个人在一起总结目前的工作并讨论接下来一年在这三个方向下一步的开发计划,由于文件系统和IO系统的结合越来越紧密,所以很多session都是在一起讨论的,本文也不区分两者,而统一放在一起阐述。

file system consistency
这个议题实际上是今年FAST的一篇论文,来自University of Tornoto的Ashvin Goel和Daniel Fryer介绍了他们的工作,主要思想是在运行态通过各种预先设定好的invariant来动态检查各种文件系统error,从而尽早的发现问题,具体的论文可以参见这里。 想法不错,但是内核开发者对额外的内存以及cpu消耗表示担忧,所以具体的能否在工业界实现还有待进一步的研究。

write back status update
吴峰光同学介绍writeback最近的进展,io-less dirty throttling已经有了明显的进展(70%),目前的主要工作集中在balance_dirty_pages中,通过计算dirty_limit以及这个程序的dirty_ratio,可以设置程序合适的dirty速度(通过让程序睡眠来实现),从而让多个io heavy的程序能够同时开展工作并且没有太大的波动,延时也会比较稳定。而且目前Mel Gorman已经将Pageout从direct reclaim里面拿掉,而由flusher来完成写入,所以writeback就显得更为重要。接下来的工作可能有以下几个方向:
1. dirty page虽然不会被direct reclaim写入,但是仍然会存在于lru中,这样多次的scan会浪费多余的cpu,添加一个新的page flag并将它们挪到新的队列似乎是一个选择。
2. blkcg IO controller for buffer write:由于目前的blkcg只能对read/sync write起作用(blkcg需要使用进程上下文,而对于buffer write IO,上下文已经丢失),而write back的上下文则刚好是在buffer write,这也就使blkcg对buffer write的控制在这里实现变得顺理成章。峰光已经有一个patch set,但是Tejun似乎对这个提案不太赞成,他认为blkcg的工作应该交给block层来做,但是从我和峰光的讨论来看,这里涉及到大量的修改以及不必要的overhead,我个人还是比较看好峰光的实现,当然他们方案的pk还在继续,有兴趣的同学可以参见这里。
3. per-memcg的dirty limit:由于目前只有一个全局的dirty limit,导致对memcg的控制较弱,这个可能是google比较感兴趣的一个方案,下一步他们应该会投入一定的人力来做这个事情。

Writeback and Stable Pages
这个还是stable pages的老问题,他的来龙去脉可以看这里, 解决方案有三种:
1.上层做rewrite,这个比较复杂,所以一直不被看好
2.上层等待底层的写结束以后再做新的写入
3.上层发起新写请求的时候做cow拷贝出新的数据出来,这样新的上层写入和底层的i/o操作互不干扰
去年LSF的一致结论是使用wait_on_page_writeback,也就是方法2。可是今年Ted T’so在google集群中发现了问题,就是写会被阻塞很久(具体的thread可以参见这里),于是方案3又被重新提起。但是这里有一个问题就是随着底层i/o设备速度越来越快,cow的方法真的能比等待写入的速度更快么? 目前的结论是由底层的i/o设备向上层也就是文件系统报告它是否需要stable page的支持,文件系统则会告诉i/o层是否会给予一个stable pages。这样对于一般不支持stable page的设备,盲目的wait_on_page_writeback就不再需要了,这样可以部分避免google遇到的问题,而wait_on_page_writeback则继续是那些需要stable page的设备的方案。目前的redhat内核对stable page的支持还没有加入,所以google的问题我们应该暂时还不会遇到。

copy offload
copy offload这个概念实际上很早就在SCSI-1中已经有描述,但是一直没有普及。可能是由于最近big data的兴起,copy offload才重新火了起来。其实这个概念很简单,就是用户的copy被offload到了存储端直接完成,从而节约了大量的用户和存储间的带宽以及内存,cpu等的消耗。如果extended copy能够被copy源以及copy目标的存储系统支持,那么大数据的备份将非常方便。
这个的实现目前还在探索阶段,SUSE的Hannes Reinecke介绍了他的一些尝试,一种是通过扩展FIEMAP来实现,但是FIEMAP目前的一些问题似乎还没有解决。另外一个方案是添加一个xcopy的system call,但是可能会有一些长时间的阻塞,另外这个和sys_sendfile基本很像,还有就是这个方案就完全是linux自己的方案了,posix的兼容完全无法保证。

In Kernel AIO/DIO
目前内核的AIO实现是使用的user pages,所以这套路径在内核里面就没有办法使用了。Oracle的Dave Kleikamp提到修改iov_iter,让它可以同时容纳iovec(用户传过来的)和bio_vec(内核传过来的),并修改direct_IO的路径,这样在内核态就可以使用了。
他同时提到了自己对loop back设备的修改,让loop back设备能够直接发送AIO,从而避免Loop back设备所在文件系统的cache。这套方案对于基于文件image的虚拟机有很大的帮助,我个人认为Oracle VM就是使用的这种方式。KVM应该也可以从中收益。

RAID engine Unification
由于pNFS开始有RAID方面的需求,所以现在内核里面的实现已经有MD,DM,btrfs了,所以Boaz Harrosh提议能否创建一个新的raid实现,其他的都可以调用这个实现。当然这个首要前提就是不能影响现有的工作,并能够通过lvm raid的一套工具来管理,又一项重大的工程。

xfstests
Dave Chinner提到了xfstests的一些进展,现在xfstests正在逐渐成为一套标准的文件系统测试工具,里面大概有100个测试是和底层文件系统无关的,当前还有一些不足需要进一步去完善。我在参加ext4 workshop的时候也和Ted聊过,Ted也建议采用xfstests作为基本的regression test工具,而如果有新的测试用例也需要逐渐往xfstests里面添加。

Flush media
SanDisk的Steven Sprouse介绍了flash设备的特点,以及接下来的可能工作方向。
目前的flush设备生命周期的计算都是以TB或者PB为单位,也就是说有一个简单的公式
lefetime terabyte writes = physical capacity * write endurance / write amplification
容量physical capacity在不断增加,但是成本也在增长,写的次数write endurance却随着密度的增大在下降,写放大write amplification取决于很多因素比如usage,block size, over provision, trim等。目前nand的发展趋势是block size越来越大(2004年的时候是64KB,现在则到了1MB),每个cell的bits越来越多(密度越来越大),单位bit的写次数在降低,所以一些厂商的ssd内部也已经是混合存储了(一些写次数多的SLC被用于journal等,其他的数据则放在成本较低的MLC里)。
关于写放大,一般的认为是随机的写会导致一定的写放大,但是这里有一个问题是多大的写才算呢?理论上来说只有当写的大小比ssd的block size大的时候才可以,但是block size信息一般ssd是不会暴露出来的,而且上文也提到过block size现在的趋势是越来越大,所以也许今天的顺序写到明天就变成随机写。Christoph Helwig甚至提出小于64M的写都是随机写,那么到底该如何呢?最优的方案是ssd能够把block size暴露出来,让上层文件系统了解到这些信息。
所以最终的话题自然转到文件系统和block设备层之间如何更好的合作呢?
1. 文件系统能够告诉block层那些数据会在同时消失,这样ssd可以把它们放在一个block上,删除的时候整个block就都能重新使用,Ted提到一个很好的例子,rpm,一般一个rpm包安装的文件的生命周期都是一样的,同时被安装,同时被删除。
2. block层能够上报文件系统自己的一些信息,比如上文提到的block size, page size, stripe size等,让文件系统更好的利用这些信息
3. block层提供机制能够让文件系统提供数据的tag(比如那些数据会经常被更新和访问),这样底层设备可以做相应的处理
4. 文件系统能够做一些flash设备的清理工作(如trim),帮助设备更好的工作
关于合作,Steven还举出了一个很好的例子,有A,B,C,D四个文件,对于底层设备有两种存储模式
interleave mode
die1 die2 die3 die4
a1 a2 a3 a4
b1 b2 b3 b4
c1 c2 c3 c4
d1 d2 d3 d4
这种模式下,文件的读取由于可以在并行的在4个die上执行,会非常快,但是一旦涉及到数据更新,写放大就会比较严重。
另一种模式是non-interleave mode
die1 die2 die3 die4
a1 b1 c1 d1
a2 b2 c2 d2
a3 b3 c3 d3
a4 b4 c4 d4
这样的话更新的时候写放大很小,但是读取就会比较慢了,因为只能在一个die上串行。这时候如果文件系统(或者应用)能够提供必要的信息,底层设备就能够更好的布局。

Device mapper and Bcache
还是老议题,facebook的flashcache以及google的bcache都是以快速ssd设备来做慢速设备的cache的解决方案,flashcache基于device mapper,bcache则需要修改内核,但是效率更好一些。内核到底应该怎么弄,大家只是觉得需要一个,但是需要那一种还没有达成一致。

high iops and scsi/block
Roland Dreier首先描述了目前block层设备驱动的两种模式:make_request类型的(设备直接注册make_request,从而可以忽略大部分block层的函数)和基于request类型的(由于queue_lock的存在有大量的contention)。前者工作在底层并且无法使用很多block层的功能(最明显的一点是blktrace利用的很多trace point已经无法使用了),而后者则效率比较差。Jens提到他正在进行的multi-queue工作,并承诺将尽早发出,这个对于一些通用的快速ssd设备(而不是像fusion io那样使用专属驱动)应该会有很大的帮助。

LBA hinting and new storage commands
Frederick Knight首先介绍了SMR技术(shingled magnetic recording),通过SMR可以进一步提高磁盘的密度(具体原理请google之),但是带来的问题就是在写数据的时候需要一次写一个band,如何处理这个问题呢?
1. transparent: 完全对用户透明
2. band把信息向上汇报然后让上层来处理
3. 使用hints让上层和设备层互相传递部分信息,具体的策略则由各自自行决定
社区目前是趋向第一种方案(当然,这样内核完全不需要修改),或者方案三。

Tumblr架构 – 页面浏览量150亿/月并且比Twitter更难拓展

     注:一些内容不熟悉,所以没有翻译。原文地址在这里
    Tumblr每个月增长30% , 一天5亿网页浏览,40K/sec , 每天3TB的数据存储在1000+的服务器上。最开始只有4名工程师来处理所有事情,当有20多个工程师的时候,才有实力出一些有趣的解决方案。
    Tumblr最开始是典型的大型LAMP应用,现在的分布式服务模型使用了Scala, HBase, Redis, Kafka , Finagle等,现在在处理PHP应用的问题,开始走向面向服务的设计。
    分布式系统工程师 Blank Matheny讲述Tumblr的架构

现状

  • 每天5亿PV
  • ~20 工程师
  • 平均每秒4W请求
  • 每天1+ TB 数据写入到Hadoop集群
  • 每天更多TB的数据写入到 MySQL/HBase/Redis/Memcache
  • 每月增长超过30%
  • ~1000 硬件服务器
  • 每个工程师每天分摊的PV将近10亿
  • 每天提交 50GB . Follower列表更新每天产生2.7TB 的数据。
  • Dashboard 每秒百万的读取和每秒5万的写入, 并且还在增长.

软件

  • 开发使用OS X , 服务器使用Linux (CentOS, Scientific)
  • Apache
  • PHP, Scala, Ruby
  • Redis, HBase, MySQL
  • Varnish, HA-Proxy, nginx,
  • Memcache, Gearman, Kafka, Kestrel, Finagle
  • Thrift, HTTP
  • Func – a secure, scriptable remote control framework and API
  • Git, Capistrano, Puppet, Jenkins

硬件

  • 500 web servers
  • 200 database servers (许多服务器是容灾的需要)
    • 47 pools
    • 30 shards
  • 30 memcache servers
  • 22 redis servers
  • 15 varnish servers
  • 25 haproxy nodes
  • 8 nginx
  • 14 job queue servers (kestrel + gearman)

架构设计

  • Tumblr 相对其他社会网络有不同的使用模式
    • 网状的分发结构,每天超过5000万的提交需要分发到用户的Follower , 很多用户都有数百个Follower ,百万Follower的用户也不是一两个。所以Tumblr的规模极具挑战性。
    • 由于存在图片和视频,让用户停留时间比较长。
    • 返回给用户的内容和用户的联系人有关,不是简单的数据流。
    • 需要做大量的数据分析(用户数量,用户平均活动范围,用户提交的高质量的内容等)
  • Tumblr 运行在托管服务器上 . 设计上需要考虑未来的地理分布。
  • Tumblr作为一个平台有2部分组成: 公共Tumblr日志 和面板
     

    • 公共Tumblr日志是一个博客平台 . 容易缓存
    • 控制面板类似 Twitter 时间表 . 需要实时更新用户follower的内容.
      • cache不管用,尤其对活跃的用户
      • 需要实时和一致,不应该显示陈旧数据,所以会有每天提交50GB,Follower列表更新需要2.7TB。(多媒体存储在S3上)
    • 多数用户把Tumblr作为内容消费工具, 70%浏览来自面板.
    • 面板的可用性非常好了,Tumblr日志因为遗留的架构问题导致可用性不好。

老的Tumblr

  • Tumblr公司开始托管在Rackspace ,他们给每个自定义域blog一个A记录(域名管理方面的内容),当迁出Rackspace的时候需要迁移大量用户,2007年前,使用 HAProxy and Varnish来负责均衡。许多像这样的遗留问题
  • 一个传统的 LAMP 演进.
    • 历史原因,每个工程师都用PHP。
    • 最开始阶段一台web服务器,一台数据库服务器,一台PHP应用服务器。
    • To scale they started using memcache, then put in front-end caching, then HAProxy in front of the caches, then MySQL sharding. MySQL sharding has been hugely helpful.
    • 最开始使用memcache ,然后转到前端缓存,使用HAProxy 在缓存前,然后使用Mysql水平分区,Mysql水平分区非常有用。
    • 最开始中心化,过去的几年中采用了2台用C语言写的后端服务器来做2件事情:生成ID 和 Staircar ,使用Redis来处理面板通知
  • 面板采用了 scatter-gather 的方式,当用户访问他们面板的时候采用事件驱动的方式,由于采用事件顺序,分片方案不是工作的很好。

新的Tumblr

  • 因招聘和开发速度的原因,转向JVM。
  • 目标是移除PHP的所有应用,采用服务的方式。所有应用是很薄的一层,通过访问服务来鉴权和呈现。
  • Scala and Finagle 的选择
    • 内部人员很多回Ruby和PHP,所以Scala语言比较容易接受
    • Finagle 是Scala写的,解决了大部分的分布式问题,同时它是免费的。
    •  Finagle 提供了初始阶段所有想要的东西 (Thrift, ZooKeeper, etc).
    • Finagle 被 Foursquare和Twitter使用 . Meetup在使用Scala
    • 喜欢Thrift的应用接口 , 主要是高性能方面。
    • 喜欢Netty, 但是不喜欢Java , Scala是个不错选择
    • 选择 Finagle 可以通过更少的代码解决分布问题。
    • Node.js不被选择是因为JVM平台更有优势,Node.js没有开发标准和好的开发策略,没有通过大型系统的代码验证。Scala 可以使用java代码,可拓展性好,5毫秒的响应速度,有许多大型java应用可以参考。
  • 内部服务从基于C/libevent转向基于 Scala/Finagle
  • 使用非关系型数据库HBase和Redis ,  但是大半数据存储在Mysql集群,没有使用HBase替代MySQL。
  • HBase 被用来备份数十亿的短网址和历史数据分析,被用来解决高并发写的问题,例如:每秒百万写级别的面板重置。HBase 没有替代Mysql是因为项目经验少,不能打赌。
  • MySQL和shard(分片数据库)在时序数据上的问题是,一个分片总是热点块,并且由于在slave端的平行插入而导致读端的复制总是落后。
  • Created a common services framework.
    • Spent a lot of time upfront solving operations problem of how to manage a distributed system.
    • Built a kind of Rails scaffolding, but for services. A template is used to bootstrap services internally.
    • All services look identical from an operations perspective. Checking statistics, monitoring, starting and stopping all work the same way for all services.
    • Tooling is put around the build process in SBT (a Scala build tool) using plugins and helpers to take care of common activities like tagging things in git, publishing to the repository, etc. Most developers don’t have to get in the guts of the build system.
  • 前置层使用 HAProxy. Varnish , 40台机器.
  • 500 台web服务器上跑着Apache和PHP应用.
  • 200台数据库服务器,部分服务器是容灾需要;成本考虑,硬件使用MTBF。
  • 6种后端服务支撑PHP应用。有一个小组专门来开发这种服务,每2,3周都会推出一种新的服务。包括: Includes dashboard notifications, dashboard secondary index, URL shortener, and a memcache proxy to handle transparent sharding.
  • MySQL足够了,所以没有用MongoDB.
  • Gearman , 一个工作队列系统。用来跑长时间运行的或者无需人工干预的工作。
  • Availability is measured in terms of reach. Can a user reach custom domains or the dashboard? Also in terms of error rate.
  • 历史最高需求已经被搞定。现在,如果一部分需求不能满足要求,我们会从用户角度和应用角度对错误的模型进行分析和系统的处理来达到成功的目的。
  • 最初使用了Finagle的Actor模型,后来去掉了。使用了twitter的utility library的Futures接口来处理异步的需求。
  • Scala鼓励不适用共享状态。Finagle 在twitter的产品中得到了很好的测试验证,在机器上无状态运行,保证了开发人员不用去担心线程和锁。
  • 22 Redis servers. Each server has 8 – 32 instances so 100s of Redis instances are used in production.
    • Used for backend storage for dashboard notifications.
    • A notification is something  like a user liked your post. Notifications show up in a user’s dashboard to indicate actions other users have taken on their content.
    • High write ratio made MySQL a poor fit.
    • Notifications are ephemeral so it wouldn’t be horrible if they were dropped, so Redis was an acceptable choice for this function.
    • Gave them a chance to learn about Redis and get familiar with how it works.
    • Redis has been completely problem free and the community is great.
    • A Scala futures based interface for Redis was created. This functionality is now moving into their Cell Architecture.
    • URL shortener uses Redis as the first level cache and HBase as permanent storage.
    • Dashboard’s secondary index is built around Redis.
    • Redis is used as Gearman’s persistence layer using a memcache proxy built using Finagle.
    • Slowly moving from memcache to Redis. Would like to eventually settle on just one caching service. Performance is on par with memcache.

内部的Firehose(通信管道)

  • Internally applications need access to the activity stream. An activity steam is information about users creating/deleting posts, liking/unliking posts, etc.  A challenge is to distribute so much data in real-time. Wanted something that would scale internally and that an application ecosystem could reliably grow around. A central point of distribution was needed.
  • Previously this information was distributed using Scribe/Hadoop. Services would log into Scribe and begin tailing and then pipe that data into an app. This model stopped scaling almost immediately, especially at peak where people are creating 1000s of posts a second. Didn’t want people tailing files and piping to grep.
  • An internal firehose was created as a message bus. Services and applications talk to the firehose via Thrift.
  • LinkedIn’s Kafka is used to store messages. Internally consumers use an HTTP stream to read from the firehose. MySQL wasn’t used because the sharding implementation is changing frequently so hitting it with a huge data stream is not a good idea.
  • The firehose model is very flexible, not like Twitter’s firehose in which data is assumed to be lost.
    • The firehose stream can be rewound in time. It retains a week of data. On connection it’s possible to specify the point in time to start reading.
    • Multiple clients can connect and each client won’t see duplicate data. Each client has a client ID. Kafka supports a consumer group idea. Each consumer in a consumer group gets its own messages and won’t see duplicates. Multiple clients can be created using the same consumer ID and clients won’t see duplicate data. This allows data to be processed independently and in parallel. Kafka uses ZooKeeper to periodically checkpoint how far a consumer has read.

Cell Design for Dashboard Inbox(业务方面的实现,未翻译)

  • The current scatter-gather model for providing Dashboard functionality has very limited runway. It won’t last much longer.
    • The solution is to move to an inbox model implemented using a Cell Based Architecture that is similar to Facebook Messages.
    • An inbox is the opposite of scatter-gather. A user’s dashboard, which is made up posts from followed users and actions taken by other users,  is logically stored together in time order.
    • Solves the scatter gather problem because it’s an inbox. You just ask what is in the inbox so it’s less expensive then going to each user a user follows. This will scale for a very long time.
  • Rewriting the Dashboard is difficult. The data has a distributed nature, but it has a transactional quality, it’s not OK for users to get partial updates.
    • The amount of data is incredible. Messages must be delivered to hundreds of different users on average which is a very different problem than Facebook faces. Large date + high distribution rate + multiple datacenters.
    • Spec’ed at a million writes a second and 50K reads a second. The data set size is 2.7TB of data growth with no replication or compression turned on. The million writes a second is from the 24 byte row key that indicates what content is in the inbox.
    • Doing this on an already popular application that has to be kept running.
  • Cells
    • A cell is a self-contained installation that has all the data for a range of users. All the data necessary to render a user’s Dashboard is in the cell.
    • Users are mapped into cells. Many cells exist per data center.
    • Each cell has an HBase cluster, service cluster, and Redis caching cluster.
    • Users are homed to a cell and all cells consume all posts via firehose updates.
    • Each cell is Finagle based and populates HBase via the firehose and service requests over Thrift.
    • A user comes into the Dashboard, users home to a particular cell, a service node reads their dashboard via HBase, and passes the data back.
    • Background tasks consume from the firehose to populate tables and process requests.
    • A Redis caching layer is used for posts inside a cell.
  • Request flow: a user publishes a post, the post is written to the firehose, all of the cells consume the posts and write that post content to post database, the cells lookup to see if any of the followers of the post creator are in the cell, if so the follower inboxes are updated with the post ID.
  • Advantages of cell design:
    • Massive scale requires parallelization and parallelization requires components be isolated from each other so there is no interaction. Cells provide a unit of parallelization that can be adjusted to any size as the user base grows.
    • Cells isolate failures. One cell failure does not impact other cells.
    • Cells enable nice things like the ability to test upgrades, implement rolling upgrades, and test different versions of software.
  • The key idea that is easy to miss is:  all posts are replicated to all cells.
    • Each cell stores a single copy of all posts. Each cell can completely satisfy a Dashboard rendering request. Applications don’t ask for all the post IDs and then ask for the posts for those IDs. It can return the dashboard content for the user. Every cell has all the data needed to fulfill a Dashboard request without doing any cross cell communication.
    • Two HBase tables are used: one that stores a copy of each post. That data is small compared to the other table which stores every post ID for every user within that cell. The second table tells what the user’s dashboard looks like which means they don’t have to go fetch all the users a user is following. It also means across clients they’ll know if you read a post and viewing a post on a different device won’t mean you read the same content twice. With the inbox model state can be kept on what you’ve read.
    • Posts are not put directly in the inbox because the size is too great. So the ID is put in the inbox and the post content is put in the cell just once. This model greatly reduces the storage needed while making it simple to return a time ordered view of an users inbox. The downside is each cell contains a complete copy of call posts. Surprisingly posts are smaller than the inbox mappings. Post growth per day is 50GB per cell, inbox grows at 2.7TB a day. Users consume more than they produce.
    • A user’s dashboard doesn’t contain the text of a post, just post IDs, and the majority of the growth is in the IDs.
    • As followers change the design is safe because all posts are already in the cell. If only follower posts were stored in a cell then cell would be out of date as the followers changed and some sort of back fill process would be needed.
    • An alternative design is to use a separate post cluster to store post text. The downside of this design is that if the cluster goes down it impacts the entire site.  Using the cell design and post replication to all cells creates a very robust architecture.
  • A user having millions of followers who are really active is handled by selectively materializing user feeds by their access model (see Feeding Frenzy).
    • Different users have different access models and distribution models that are appropriate. Two different distribution modes: one for popular users and one for everyone else.
    • Data is handled differently depending on the user type. Posts from active users wouldn’t actually be published, posts would selectively materialized.
    • Users who follow millions of users are treated similarly to users who have millions of followers.
  • Cell size is hard to determine. The size of cell is the impact site of a failure. The number of users homed to a cell is the impact. There’s a tradeoff to make in what they are willing to accept for the user experience and how much it will cost.
  • Reading from the firehose is the biggest network issue. Within a cell the network traffic is manageable.
  • As more cells are added cells can be placed into a cell group that reads from the firehose and then replicates to all cells within the group. A hierarchical replication scheme. This will also aid in moving to multiple datacenters.

团队结构

  • Teams: 技术设施, 平台开发,架构, SRE (侧重可靠性和可拓展性方面的问题解决), 产品, 测试, services(趋向于战略研究).

软件部署

  • 开发一套rsync脚本来部署PHP,当机器超过200台的时候出现各种状态问题。
  • 接下来,使用Capistrano(一个开源工具,可以在多台服务器上运行脚本)在服务堆栈中构建部署进程(开发、分期、生产)。在几十台机器上部署可以正常工作,但当通过SSH部署到数百台服务器时,再次失败。
  • 现在,所有的机器上运行一个协调软件。基于Redhat Func(一个安全的、脚本化的远程控制框架和接口)功能,一个轻量级的API用于向主机发送命令,以构建扩展性。
  • 建立部署是在Func的基础上向主机发送命令,避免了使用SSH。比如,想在组A上部署软件,控制主机就可以找出隶属于组A的节点,并运行部署命令。
  • 部署命令使用Capistrano来实现。使用http的方式从git从库上来检出和上传。
  • Func API用于返回状态报告,报告机器上的软件版本号
  • 安全重启任何服务,因为他们先关闭连接,然后重启。
  • 所有功能在激活前都运行dark mode下

开发

  • 从哲学上将,任何人都可以使用自己想要的任意工具。但随着团队的发展壮大,这些工具出现了问题。新员工想要更好地融入团队,快速地解决问题,必须以他们为中心,建立操作的标准化。
  • 过程类似于Scrum(一种敏捷管理框架),非常敏捷。
  • 每个开发人员都有一台预配置的开发机器,并按照控制更新。
  • 开发机会出现变化,测试,分期,乃至用于生产。
  • 开发者使用VIM和TextMate。
  • 测试是对PHP程序进行代码审核。
  • 在服务方面,他们已经实现了一个与提交相挂钩的测试基础架构,接下来将继承并内建通知机制。

招聘流程

  • 面试通常避免数学、猜谜、脑筋急转弯等问题,看重实际工作技能。
  • 编程能力是重中之重。
  • 找到合适的人,不是比较人
  • 挑战在于找到具有可用性、扩展性经验的人才,以应对Tumblr面临的网络拥塞。
     

    • Example, for a new ID generator they needed A JVM process to generate service responses in less the 1ms at a rate at 10K requests per second with a 500 MB RAM limit with High Availability. They found the serial collector gave the lowest latency for this particular work load. Spent a lot of time on JVM tuning.

Lessons learned

  • 无处不在的自动化。
  • MySQL 添加水平分区, 应用则不需要。
  • Redis是惊人的(是不是提醒大家用一下)。
  • Scala应用程序执行效率和出色。
  • 取消不能工作的项目。
  • 雇佣的人不要因为他们有对团队无用的技能,雇佣的人应该适应团队和工作。
  • 找到帮你找到合适人的stack
  • 围绕团队技能开展工作
  • 阅读论文和博客
  • 多与同行交流
  • 技术追求需要循序渐进。在正式投入使用前,Tumblr用心研究HBase和Redis , 这样可以降低线上服务风险。

在 CentOS 6.2 上安装 Cobbler

每次給自己的电脑重装系统都是一件很无聊的事情,如果需要重装上百台虚拟机和服务器不但很无聊而且很耗时,面对现在云时代大量服务器和虚拟机的出现,运维必须要自动化。现在有很多开源工具可以帮助我们实现自动化安装系统,比如 FAI, Cobbler, Spacewalk, Ubuntu Orchestra 等,我们打算把 Cobbler 安装在某台虚拟机上,为我们新购的16台刀片服务器自动安装系统。

Cobbler 是一个系统启动服务(boot server),可以通过网络启动(PXE)的方式用来快速安装、重装物理服务器和虚拟机,支持安装不同的 Linux 发行版和 Windows. Cobbler 是个轻量级 Python 程序,总共大概1.5万行代码,还可以用来管理 DHCP, DNS, yum 源等。Cobbler 使用命令行方式管理,也提供了基于 Web 的界面管理工具(cobbler-web),不过命令行方式已经很方便,实在没有必要为了不实用的 Web 界面再添加一个 Web 服务器。

修改 DHCP 服务器配置

使用 Cobbler 最好配合现有局域网上的 DHCP 服务器一起使用,这样不会因为 Cobbler 干扰现有局域网。我们不打算用 Cobbler 来管理整个网络的 DHCP,因为我们已经有了 DHCP 服务器,所以只要在现有的 DHCP 服务器上做以下配置即可,下面记得调整 192.168.2.22 这个 IP 地址指向 Cobbler 服务器:

# for Cobbler setup
host cobbler {
    option host-name "cobbler";
    ddns-hostname "cobbler";
    hardware ethernet 00:0c:29:2d:2c:39; #MAC address of cobbler server
            fixed-address 192.168.2.22; #IP of Cobbler server
            allow booting;
    allow bootp;
    class "PXE" {
        match if substring(option vendor-class-identifier, 0, 9) = "PXEClient";
        next-server 192.168.2.22; #IP of Cobbler server
            filename "pxelinux.0";
    }
}

安装和配置 Cobbler

Cobbler 不在 CentOS 6.2 的基本源中,需要导入 EPEL 源:

# rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-5.noarch.rpm
Retrieving http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-5.noarch.rpm
warning: /var/tmp/rpm-tmp.lo2Hd0: Header V3 RSA/SHA256 Signature, key ID 0608b895: NOKEY
Preparing...                ########################################### [100%]
   1:epel-release           ########################################### [100%]

# yum update
# yum upgrade

安装 cobbler:

# yum install cobbler

修改配置,主要修改 cobbler 服务器所在的 IP 地址:

# vi /etc/cobbler/settings
...
next_server: 192.168.2.22 #IP of Cobbler server
server: 192.168.2.22 #IP of Cobbler server
...

启用 httpd, xinetd 和 cobbler 服务并确认已经加到系统自动启动服务中:

# /etc/init.d/httpd start
# /etc/init.d/xinetd start
# /etc/init.d/cobblerd start

# chkconfig httpd on
# chkconfig xinetd on
# chkconfig cobblerd on

修改 rsync 和 tftp 这两个服务的 xinetd 配置:

# vi /etc/xinetd.d/rsync
service rsync
{
        disable = no
...
}

# vi /etc/xinetd.d/tftp
service tftp
{
        ...
        disable = no
        ...
}

关闭防火墙和 SELinux 后重启系统:

# /etc/init.d/iptables stop
# chkconfig iptables off

# vi /etc/sysconfig/selinux
...
SELINUX=disabled
...

# reboot

检查和修改 Cobbler 配置

系统重启后用 cobbler check 检查发现3个配置信息问题,第一个是如果要部署 debian/ubuntu 系统需要 debmirror 软件包;第二个是需要修改 cobbler 的默认密码;第三个是可选,想使用电源管理功能的话需要安装 cman 或 fence-agents:

# cobbler get-loaders

# cobbler check
The following are potential configuration items that you may want to fix:

1 : debmirror package is not installed, it will be required to manage debian deployments and repositories
2 : The default password used by the sample templates for newly installed machines (default_password_crypted in /etc/cobbler/settings) is still set to 'cobbler' and should be changed, try: "openssl passwd -1 -salt 'random-phrase-here' 'your-password-here'" to generate new one
3 : fencing tools were not found, and are required to use the (optional) power management features. install cman or fence-agents to use them

Restart cobblerd and then run 'cobbler sync' to apply changes.

现在来修复上面三个问题,我们希望能让这台 cobbler 服务器能同时部署 CentOS/Fedora 和 Debian/Ubuntu 系统,所以需要安装 debmirror,安装 debmirror-20090807-1.el5.noarch.rpm 前需要先安装依赖包:

# yum install wget
# yum install ed patch perl perl-Compress-Zlib perl-Cwd perl-Digest-MD5 \
perl-Digest-SHA1 perl-LockFile-Simple perl-libwww-perl

# wget ftp://fr2.rpmfind.net/linux/epel/5/ppc/debmirror-20090807-1.el5.noarch.rpm

# rpm -ivh debmirror-20090807-1.el5.noarch.rpm

修改 /etc/debmirror.conf 配置文件,注释掉 @dists 和 @arches 两行:

# vi /etc/debmirror.conf
...
#@dists="sid";
@sections="main,main/debian-installer,contrib,non-free";
#@arches="i386";
...

用 openssl 生成一串密码后加入到 cobbler 的配置文件(/etc/cobbler/settings)里,替换 default_password_crypted 字段:

# openssl passwd -1 -salt 'www.aikaiyuan.com' 'vpsee'
$1$www.aikaiyuan$T5FgCHY2P0NDr6JmbN0Bl0

# vi /etc/cobbler/settings
default_password_crypted: "$1$www.aikaiyuan$T5FgCHY2P0NDr6JmbN0Bl0"

安装 cman:

# yum install cman

修复完成,再用 cobbler check 检查一下,确认没问题后用 cobbler sync 做同步操作:

# cobbler check
No configuration problems found.  All systems go.

# cobbler sync

导入 ISO

挂载 CentOS-6.2-x86_64-bin-DVD1.iso 安装光盘然后导入到 cobbler(注意这个 iso 文件有 4GB 多,导入可能需要一段时间),导入成功后 cobbler list 查看一下:

# mount -o loop -t iso9660 CentOS-6.2-x86_64-bin-DVD1.iso /mnt

# cobbler import --path=/mnt --name=CentOS-6.2-x86_64-bin-DVD1 –arch=x86_64

# cobbler sync

# cobbler list
distros:
   CentOS-6.2-bin-DVD1-x86_64

profiles:
   CentOS-6.2-bin-DVD1-x86_64

systems:

repos:

images:

测试

最后创建一台虚拟机测试一下,把虚拟机设置成网络 PXE 启动(和 cobbler 在同一个网络),启动后就可以看到 Cobbler 引导界面,看到界面后选择 CentOS-6.2-bin-DVD1-x86_64 条目就可以顺利开始无人工干预安装系统,Cobbler 引导界面如下:

安装完系统后默认的密码是啥呢?根据 sample.ks 的配置提示,这个密码 $default_password_crypted 就是我们上面用 openssl passwd -1 -salt ‘www.aikaiyuan.com’ ‘aikaiyuan’ 生成的密码,所以这里的 root 密码是 aikaiyuan:

# cat /var/lib/cobbler/kickstarts/sample.ks
...
#Root password
rootpw --iscrypted $default_password_crypted

 

Windows PHP访问MySQL时出现httpd内存错误

我在用PHP连MySQL时,总是会弹出对话框提示httpd内存错误。使用VS调试输出的信息为:
httpd.exe 中的 0x0079ac5a 处未处理的异常: 0xC0000005: 读取位置 0×00000014 时发生访问冲突

 

在解决这个问题的过程中,我碰到了一个奇怪的现象,希望路过的高手能帮忙看一下,是什么原因。
问题描述如下:
  我是在Windows XP SP3下安装的Apache HTTP Server 2.2.11,PHP使用的版本是5.2.8,PHP和Apache结合使用的是LoadModule的方式,Apache的httpd.conf文件中的配置语句为
LoadModule php5_module D:/PHP/php5apache2_2.dll
并在后边加上了一句 PHPIniDir “D:/PHP”。

  按道理说,这么配置了以后,Apache启动加载的时候,就会到PHPIniDir指定的路径去寻找php.ini。Apache启动一切正常,我运行了一个php的探针页面,也都能正常显示。但phpinfo()显示的信息,却是这样的
Configuration File (php.ini) Path  C:\WINDOWS  
Loaded Configuration File  D:\PHP\php.ini  

  PHP配置文件的路径显示的竟然是C:\WINDOWS,但实际读取的配置文件却是对的,就是PHPIniDir设置的路径下的。我试着在注册表中添加HKEY_LOCAL_MACHINE\SOFTWARE\PHP\IniFilePath,指定路径为D:\PHP,但phpinfo()里面显示的路径还是C:\WINDOS。添加环境变量PHPRC,结果还是一样的。

  phpinfo()中显示的路径虽然不是我设定的那个,但一般的PHP页面还都是可以正常执行的,这说明php.ini是正确加载的。但在PHP中使用mysql_connect这一类方法访问MySQL数据库时,却总是弹出httpd的内错错误。我把PHP安装目录下的libmysql.dll和php5ts.dll两个dll文件拷贝到C:\WINDOS目录下后,内存错误的问题就没有了。看样子是Apache或PHP在Configuration File (php.ini) Path指定的路径中找不到那两个库文件,造成出现内存错误。

  我想请问各位高手,Configuration File (php.ini) Path的路径怎么设置?为什么我做了那些配置以后,这个值还是C:\WINDOWS,而且还能正确的从D:\PHP中加载php.ini。这个内存错误的问题虽然已经临时解决了,但我还是希望有高手能指导一下,为什么会出现这样的情况。。