Mogilefs2.34默认复制策略及其缺陷研究

Mogilefs的默认复制策略的类名叫做“MultipleHosts”,文件位置在lib/MogileFS/ReplicationPolicy/MultipleHosts.pm。

本次分析的Mogilefs版本是2.34。

MultipleHosts.pm中最重要的函数是replicate_to,主要功能是根据文件所属类的devcount,即副本目标数量,文件副本当前所在设备列表、所有设备列表、失效的设备列表等参数来决定当前文件是否需要复制,如果需要复制,返回应该复制待选的设备列表。

MultipleHosts主要的复制策略如下:

  1. 如果文件所在设备的独立主机数量已经大于最少复制份数,返回TOO_GOOD(过复制,多复制了,因此不用再复制了);
  2. 如果文件所在设备的个数大于最少复制份数,返回TOO_GOOD;
  3. 如果文件所在设备的独立主机数量等于最小复制份数,返回ALL_GOOD(正好,不多也不少,不用再复制)
  4. 如果文件所在设备的独立主机数量大于目前所有设备的独立主机数量,同时文件所在设备的个数大于等于最少复制份数,返回ALL_GOOD
  5. 如果没有满足上述条件,Mogilefs在所有可用设备中选择合适的设备来复制文件副本,选择的方式如下:
    • 首先选择和目前所有副本所在主机不同的主机上的设备,这些设备的列表放置到ideal列表中;
    • 如果找不到A中所述的设备,就找和目前所有副本所在设备不同的设备,这些设备的列表放置到desp列表中。

最后replicate_to返回一个ReplicationRequest对象,并将ideal列表和desp列表分别复制给ReplicationRequest对象的ideal成员属性和desperate成员属性。接下来Mogilefs会根据replicate_to返回的信息采取相应的文件复制动作。

上述策略中,第2条是存在缺陷的,看看如下场景:

  1. 假设文件目标副本数量为2,有两台存储主机A和B,每台主机都有两个设备devA1,devA2,devB1,devB2,如果其中主机B出现故障,devB1和devB2被标记为down,这时候,有一个新的文件上传,MogileFS首先找到A主机上的某个设备存下来,假设这个设备是devA1,然后MogileFS运行MultipleHosts,replicate_to将按照上述第5条,得到一个空的ideal列表和一个有devB2的desp列表,MogileFS因此会将文件复制到devB2,这是文件副本数量为2。但是因为MogileFS使用的是desp列表复制文件,不够可靠,因此复制任务不会删除,MogileFS将在以后为该文件找到更可靠的复制方案。
  2. 主机B的故障解除,这时devB1和devB2被重新标记为alive,MogileFS再次运行复制任务,这一次,因为副本数量已经是2了,MultipleHosts的replicate_to会根据第2条规则返回TOO_GOOD,这就出现了问题,显然不是期待的结果。

预期的结果应该是MogileFS找到主机B上的devB1和devB2作为ideal列表,然后在两个设备之间挑选一个来复制文件,以达到完美可靠性,然后删除复制任务。

下列是MultipleHosts.pm中的replicate_to函数:


# MutipleHosts类的核心函数,主要功能是根据当前文件所在设备列表、所有设备列表、失效的设备列表等参数来决定当前文件是否需要复制,如果需要
# 复制,应该复制到那个设备上。
# 参数:
# fid: 当前文件的fid
# on_devs: 当前文件所在的设备列表
# all_devs: 所有设备哈希列表,key是设备编号devid,value是设备对象
# failed:失效设备的哈希列表,key是设备编号devid,value是1
# min: 指定的文件复制份数,如果该参数为空或0,则使用文件所属类的默认复制份数,否者就是用该复制份数
sub replicate_to {
    my ($self, %args) = @_;

    # 将参数复制到函数的本地变量
    # copy all paramters to local variables
    my $fid      = delete $args{fid};      # fid scalar to copy
    my $on_devs  = delete $args{on_devs};  # arrayref of device objects
    my $all_devs = delete $args{all_devs}; # hashref of { devid => MogileFS::Device }
    my $failed   = delete $args{failed};   # hashref of { devid => 1 } of failed attempts this round

    # 如果该参数为空或0,则使用文件所属类的默认复制份数,否者就是用该复制份数
    # this is the per-class mindevcount (the old way), which is passed in automatically
    # from the replication worker.  but if we have our own configured mindevcount
    # in class.replpolicy, like "MultipleHosts(3)", then we use the explicit one. otherwise,
    # if blank, or zero, like "MultipleHosts()", then we use the builtin on
    my $min      = delete $args{min};
    $min         = $self->{mindevcount} || $min;

    # 参数不能过多也不能过少
    # parameters should not too many or be lack
    warn "Unknown parameters: " . join(", ", sort keys %args) if %args;
    die "Missing parameters" unless $on_devs && $all_devs && $failed && $fid;

    # 文件所在设备的个数
    # number of devices we currently live on
    my $already_on = @$on_devs;

    # 如果最少复制份数为1,并且文件所在设备的个数已经不为0,则返回ALL_GOOD(无需复制)
    # a silly special case, bail out early.
    return ALL_GOOD if $min == 1 && $already_on;

    # 所有设备中,可能被选择来复制文件的设备数量
    # total disks available which are candidates for having files on them
    my $total_disks = scalar grep { $_->dstate->should_have_files } values %$all_devs;

    # 分析文件所在设备分布在哪些独立主机上
    # see which and how many unique hosts we're already on.
    my %on_dev;
    my %on_host;
    foreach my $dev (@$on_devs) {
        $on_host{$dev->hostid} = 1;
        $on_dev{$dev->id} = 1;
    }
    # 文件所在设备的独立主机数量
    my $uniq_hosts_on    = scalar keys %on_host;
    # 目前所有设备的独立主机数量
    my $total_uniq_hosts = unique_hosts($all_devs);

    # if we are on two hosts but 10 devices, you want to weaken the number of
    # devices you're on until you're on the right number of hosts with the
    # right number of devices.
    return TOO_GOOD if $uniq_hosts_on >  $min; # 如果文件所在设备的独立主机数量已经大于最少复制份数,返回TOO_GOOD(过复制)
    return TOO_GOOD if $already_on > $min;     # 如果文件所在设备的个数大于最少复制份数,返回TOO_GOOD(过复制)(好像有问题)
    return ALL_GOOD if $uniq_hosts_on == $min; # 如果文件所在设备的独立主机数量等于最小复制份数,返回ALL_GOOD(无需复制)
    # 如果文件所在设备的独立主机数量大于目前所有设备的独立主机数量,同时文件所在设备的个数大于等于最少复制份数,返回ALL_GOOD(无需复制)
    return ALL_GOOD if $uniq_hosts_on >= $total_uniq_hosts && $already_on >= $min;

    # if we have two copies and that's all the disks there are
    # anywhere, be happy enough, even if mindevcount is higher.  in
    # that case, when they add more disks later, they'll need to fsck
    # to make files replicate more.
    # this is here instead of above in case an over replication error causes
    # the file to be on all disks (where more than necessary)
    return ALL_GOOD if $already_on >= 2 && $already_on == $total_disks;

    # if there are more hosts we're not on yet, we want to exclude devices we're already
    # on from our applicable host search.
    my %skip_host; # hostid => 1
    if ($uniq_hosts_on < $total_uniq_hosts) {
        %skip_host = %on_host;
    }

    my @all_dests = weighted_list map {
        [$_, 100 * $_->percent_free]
     } grep {
         ! $on_dev{$_->devid} &&
         ! $failed->{$_->devid} &&
         $_->should_get_replicated_files
     } values %$all_devs;

    return TEMP_NO_ANSWER unless @all_dests;

    my @ideal = grep { ! $skip_host{$_->hostid} } @all_dests;
    my @desp  = grep {   $skip_host{$_->hostid} } @all_dests;

    return TEMP_NO_ANSWER if $already_on >= $min && @ideal == 0;

    return MogileFS::ReplicationRequest->new(
                                             ideal => \@ideal,
                                             desperate => \@desp,
                                             );
}

清除lighttpd mod_cache 目录文件的脚本

This shell script will clean lighttpd web server cache. You need to run this script via a cron job. Download install script at /etc/cron.daily directory.


#!/bin/bash
# Shell script to clean web server cache stored at /var/www/cache/ directory.
# -------------------------------------------------------------------------
# Copyright (c) 2007 nixCraft project <http://cyberciti.biz/fb/>
# This script is licensed under GNU GPL version 2.0 or above
# -------------------------------------------------------------------------
# This script is part of nixCraft shell script collection (NSSC)
# Visit http://bash.cyberciti.biz/ for more information.
# -------------------------------------------------------------------------

# Cache dir path
CROOT="/var/www/cachelighttpd/"

#Deleting files older than 10 days
DAYS=10

# Lighttpd user and group
LUSER="lighttpd"
LGROUP="lighttpd"

# start cleaning
find ${CROOT} -type f -mtime +${DAYS} | xargs -r /bin/rm

# if directory missing just recreate it
if [ ! -d $CROOT ]
then
	mkdir -p $CROOT
	chown ${LUSER}:${LGROUP} ${CROOT}
fi

将MogileFS设备从dead状态改为alive状态

一般不建议将已标记为“dead”的设备再次变成“alive”,mogadm也不允许直接将dead的设备直接设置为alive,但是也可以通过 将dead设备设置为down然后再设置为alive来强制dead设备变成alive:


$ mogadm device list --all
mog_storage_122 [1]: alive
 used(G) free(G) total(G)
dev12201: alive      650.340 200.037 850.377
dev12202: alive      316.328 539.433 855.761

mog_storage_130 [2]: alive
 used(G) free(G) total(G)
dev13001: alive      649.490 200.887 850.377
dev13002: dead       403.684 452.077 855.761

$ mogadm device mark mog_storage_130 13002 alive
Error updating device: state_too_high Status cannot go from dead to alive; must use down
$ mogadm device mark mog_storage_130 13002 down
$ mogadm device mark mog_storage_130 13002 alive
$ mogadm device list --all
mog_storage_122 [1]: alive
 used(G) free(G) total(G)
dev12201: alive      650.346 200.031 850.377
dev12202: alive      316.331 539.430 855.761

mog_storage_130 [2]: alive
 used(G) free(G) total(G)
dev13001: alive      649.498 200.879 850.377
dev13002: alive      403.691 452.069 855.761

MogileFS可靠性测试

1 测试目标

测试MogileFS在Storage节点和失效失效前后对客户端读写请求的处理差异。

2 测试环境

两台服务器10.0.0.122和10.0.0.130,分别安装Tracker, Storage。服务器10.0.0.122安装MySQL主数据库,服务器10.0.0.130安装MySQL从数据库。两台服务器上的Tracker 都使用10.0.0.122的MySQL主数据库进行数据插入、更新和查询,同时可以使用10.0.0.130的MySQL从数据库进行数据的查询。详见安装与配置的过程

MogileFS环境如下图。

3 测试过程

3.1 读取测试

3.1.1 测试脚本

测试脚本如下:


<?php
$domain       = 'image';
$tracker_host = '10.0.0.122';
$tracker_port = 7001;
$client       = new MogileFs();
$client->connect($tracker_host, $tracker_port, $domain);
$key=0;
$metadata     = $client->get($key);
foreach($metadata as $key => $metadata_e)
{
    echo $key . ": " . $metadata_e . "<br>";
}
?>

上述脚本是从MogileFS文件系统中请求一个$key=0,并且已经存在于MogileFS文件系统中的文件的Meta Data,然后打印出所有的响应数据来。

3.1.2 正常情况的响应结果

当系统正常的时候,上述在浏览器中运行上述脚本,得到如下响应结果:


path2: http://10.0.0.122:7500/dev12202/0/000/000/0000000007.fid
path1: http://10.0.0.130:7500/dev13002/0/000/000/0000000007.fid
paths: 2

上面的结果告诉客户端可以从两个Storage节点获取文件。

3.1.3 模拟存储节点宕机或设备失效

现在,我们将10.0.0.130上的mogstored停掉或者将10.0.0.130上的dev13002设置为“down”:


service mogstored stop

或者


mogadm device mark mog_storage_130 13002 down

再在浏览器中运行上述脚本,得到如下响应结果:


path2: http://10.0.0.122:7500/dev12202/0/000/000/0000000007.fid
paths: 1

上面的结果告诉客户端只能在10.0.0.122上面获取文件了。

如果我们再启动10.0.0.130上的mogstored或将10.0.0.130上的dev13002设置为“alive”,则浏览器中又能够得到正常情况下的两个路径了。

3.1.4 模拟设备数据永久丢失

我们用标记设备为“dead”的办法来模拟设备数据永久丢失的情况,因为当我们发现设备的数据无法恢复的时候,就必须标记设备为“dead”了。

如果我们再将10.0.0.130上的dev13002设置为“dead”,这时候在浏览器中运行上述脚本,得到如下响应结果:


path2: http://10.0.0.130:7500/dev13001/0/000/000/0000000007.fid
path1: http://10.0.0.122:7500/dev12202/0/000/000/0000000007.fid
paths: 2

现在还是两个路径,但是10.0.0.130的路径中包含了dev13001,而dev13002设备已经标注为“dead”,这说明MogileFS在设备“dead”之后自动完成的文件的再次复制。

3.2 写入测试

3.2.1 测试脚本

写入的测试脚本如下:


<?php
$domain       = 'image';
$tracker_host = '10.0.0.122';
$tracker_port = 7001;
$client       = new MogileFs();
$client->connect($tracker_host, $tracker_port, $domain);
$localfile = '/var/www/html/test.jpg';
$key       = 10000;
$class     = 'large_image';
$ret       = $client->put($localfile, $key, $class);
echo $ret;

读取的测试脚本如下:


<?php
$domain       = 'image';
$tracker_host = '10.0.0.122';
$tracker_port = 7001;
$client       = new MogileFs();
$client->connect($tracker_host, $tracker_port, $domain);
$key=10000;
$metadata     = $client->get($key);
foreach($metadata as $key => $metadata_e)
{
    echo $key . ": " . $metadata_e . "<br>";
}
?>

3.2.1 正常情况的结果

正常情况下,写入脚本应该在浏览器上显示1,读取脚本在浏览器上显示如下:


path2: http://10.0.0.130:7500/dev13001/0/000/000/0000000032.fid
path1: http://10.0.0.122:7500/dev12202/0/000/000/0000000032.fid
paths: 2

两个副本分别在10.0.0.122和10.0.0.130两个存储节点上。

3.2.2 模拟存储节点宕机

现在,我们将10.0.0.130上的mogstored停掉,以模拟单个存储节点宕机。

然后将写入脚本的$key从10000改为10001,执行写入脚本,浏览器上仍然显示1,表示写入成功。

再执行读取脚本,浏览器上显示如下:


path2: http://10.0.0.122:7500/dev12201/0/000/000/0000000037.fid
path1: http://10.0.0.122:7500/dev12202/0/000/000/0000000037.fid
paths: 2

说明这时MogileFS将写入的文件副本分别存在10.0.0.122的两个设备上了。

3.2.3 存储节点恢复

我们再将10.0.0.130上的mogstored启动,来模拟该节点恢复。

等几秒钟,再执行读取脚本,浏览器上显示如下:


path2: http://10.0.0.130:7500/dev13001/0/000/000/0000000037.fid
path1: http://10.0.0.122:7500/dev12202/0/000/000/0000000037.fid
paths: 2

注意上面的两个副本已经不再位于同一个存储节点10.0.0.122了,而是分属10.0.0.122和10.0.0.130两个存储节点。

这说明MogileFS在存储节点恢复之后,对位于同一个存储节点的多份副本进行了自动分散,以提高可靠性。

4 结论

MogileFS可在单个存储节点或存储节点上的设备失效的情况下,不间断向外提供文件读取服务。

当存储节点上的设备被标记为“dead”之后,MogileFS系统立即自动复制该设备上的文件,以填补副本的缺失。

单个存储节点失效的情况下,MogileFS仍然能够提供文件写入服务。

存储节点恢复之后,MogileFS会对位于同一个存储节点的多份副本进行了自动分散,以提高可靠性。

MogileFS Storage 启动脚本


#!/bin/bash
#
# mogstored  - Startup script for the MogileFS storage node
#
# chkconfig: - 85 15
# description: MogileFS storage node
# processname: mogstored
# config: /etc/mogilefs/mogstored.conf
# pidfile: /var/run/mogstored.pid

# Source function library.
. /etc/rc.d/init.d/functions

# Path to the apachectl script, server binary, and short-form for messages.
lockfile=${LOCKFILE-/var/lock/mogstored}
RETVAL=0

start() {
         echo -n $"Starting mogstored: "
         /usr/bin/mogstored --config /etc/mogilefs/mogstored.conf --daemonize > /dev/null
         RETVAL=$?
         echo
         [ $RETVAL = 0 ] && touch ${lockfile}
         return $RETVAL
}
stop() {
         echo -n $"Stopping mogstored: "
         killproc mogstored
         RETVAL=$?
         echo
         [ $RETVAL = 0 ] && rm -f ${lockfile}
}
reload() {
     echo -n $"Reloading mogstored: "
     killproc mogstored -HUP
     RETVAL=$?
     echo
}

# See how we were called.
case "$1" in
   start)
         start
         ;;
   stop)
         stop
         ;;
   status)
         status mogstored
         RETVAL=$?
         ;;
   restart)
         stop
         start
         ;;
   reload)
         reload
         ;;
   *)
         echo $"Usage: mogstored {start|stop|restart|reload|status}"
         exit 1
esac

exit $RETVAL

MogileFS Tracker 启动脚本


#!/bin/bash
#
# mogilefsd    Startup script for the MogileFS tracker
#
# chkconfig: - 85 15
# description: MogileFS tracker
# processname: mogilefsd
# config: /etc/mogilefs/mogilefsd.conf
# pidfile: /var/run/mogilefsd.pid

# Source function library.
. /etc/rc.d/init.d/functions

# Path to the apachectl script, server binary, and short-form for messages.
lockfile=${LOCKFILE-/var/lock/mogilefsd}
RETVAL=0

start() {
         echo -n $"Starting mogilefsd: "
         sudo -u mogile /usr/bin/mogilefsd -c /etc/mogilefs/mogilefsd.conf --daemon
         RETVAL=$?
         echo
         [ $RETVAL = 0 ] && touch ${lockfile}
         return $RETVAL
}
stop() {
         echo -n $"Stopping mogilefsd: "
         killproc mogilefsd
         RETVAL=$?
         echo
         [ $RETVAL = 0 ] && rm -f ${lockfile}
}
reload() {
     echo -n $"Reloading mogilefsd: "
     killproc mogilefsd -HUP
     RETVAL=$?
     echo
}

# See how we were called.
case "$1" in
   start)
         start
         ;;
   stop)
         stop
         ;;
   status)
         status mogilefsd
         RETVAL=$?
         ;;
   restart)
         stop
         start
         ;;
   reload)
         reload
         ;;
   *)
         echo $"Usage: mogilefsd {start|stop|restart|reload|status}"
         exit 1
esac

exit $RETVAL

MogileFS安装与配置

MogileFS介绍

MogileFS是一种分布式文件存储系统。可支持文件自动备份的功能,提供高可用性和高可扩展性。MogileFS是用Perl语言写的,由于有依赖模块的问题,安装过程中需要其他库和模块的支持,安装不算容易。Mogilefs不支持对一个文件内部的随机或顺序读写,因此只适合做一部分应用,如图片服务,静态HTML服务等,这些应用在文件写入后基本上不需要对文件进行修改,但是可以生成一个新的文件覆盖原有文件。

Mogilefs分为如下几部分:

1、数据库部分

数据库保存了Mogilefs的所有元数据,一般使用MySQL,可以单独拿数据库服务器来做,也可以跟其他程序跑在一起。可以用mogdbsetup程序来初始化数据库。数据库部分非常重要,因为一个MogileFS系统的所有Tracker都必须指向同一个数据库,因此如果数据库出现问题,那么整个MogileFS系统将处于不可用状态,因此数据库最好采用HA结构,如MySQL主从复制架构,这样可以降低数据库出现问题对MogileFS系统的影响。

2、跟踪器(Tracker)

mogilefsd即trackers程序,类似mogilefs的wiki上介绍 的,trackers做了很多工作,主要有以下几个:

  • Replication: 机器间复制文件
  • Deletion:从命名空间删除是立即的,从文件系统删除是异步的
  • QueryWorker: 响应客户端的请求
  • Reaper:在磁盘失败后将文件复制请求重新放到队列中
  • Monitor:监测主机和设配的健康度和状态

Tracker是访问MogileFS的入口,所有系统管理相关的操作都要通过其中的某个trackers完成,应用系统的一些操作也要通过trackers,因此一个高业务量的MogileFS系统最好同时运行多个trackers来做负载均衡。

3、存储节点(Storage)

mogstored程序的启动将使本机成为一个存储节点。mogstored启动后,便可以通过mogadm增加这台机器到系统中。一台机器可以只运行一个 mogstored作为存储节点即可,也可以同时运行其他程序。存储节点下面还需要定义设备(device),每个设备都有一个唯一的ID编号,设备也是用mogadm添加到MogileFS系统中的。

4、工具

主要就是mogadm,mogtool这两个工具了,用来在命令行下控制整个mogilefs系统以及查看状态等等。

5、客户端(Client)

客户端通过接口访问mogilefs系统,对整个系统进行文件读写操作。

安装目标

两台服务器10.0.0.122和10.0.0.130,分别安装Tracker, Storage。服务器10.0.0.122安装MySQL主数据库,服务器10.0.0.130安装MySQL从数据库。两台服务器上的Tracker 都使用10.0.0.122的MySQL主数据库进行数据插入、更新和查询,同时可以使用10.0.0.130的MySQL从数据库进行数据的查询。

如下图所示:

This website uses a Hackadelic PlugIn, Hackadelic SEO Table Of Contents 1.7.3.