MogileFS是一个开源的分布式文件系统,Nginx是开源的4-7层web应用服务端以及反向代理服务端。本文基于CentOS7平台,进行MogileFS + Nginx的部署
- MogileFS的一些注意事项
针对于MogileFS,有如下概念需要注意一下。
- MogileFS属于有中心节点形式的分布式文件系统,元数据默认存储在关系型数据库(MySQL)当中,在此处于单点,因此有必要对MySQL使用主从复制或者MHA。
- 按功能分为tracker,database,storage。其中tracker负责对于元数据信息进行管理,对整个MogileFS系统进行协调监控。其中database负责存储tracker所管理的元数据。其中storage负责存储数据文件。
- MogileFS依赖于perbal作为代理,一方面接收客户端的请求并将其发送至tracker进行检索,一方面接收tracker的检索结果并向storage发送获取请求,另一方面接收storage的响应并且传递给客户端所请求的内容。
- 亦可以使用Nginx作为代理,不过需要对原生的Nginx安装mogilefs的模块(或者称为打上mogilefs的补丁)
MogileFS管理文件的一些概念
-
Domain: 一个MogileFS可以有多个Domain,用来存放不同文件(大小,类型),同一个Domain内key必须为一,不同Domain内,key可以相同。
- Class:文件属性管理,定位文件存储在不同设备上面的份数。
- 每一个storage称为一个主机,一个主机上可以有多个存储设备(单独的硬盘),每个设备都有ID号,Domain+Fid用来定位文件。
MogileFS各个功能组件的交互图示
- MogileFS针对客户端请求的工作流程图
- MogileFS搭建环境拓扑结构
笔者的拓扑结构如下图所示。其中一台Nginx作为反向代理,用于代理用户的客户端请求。两个tracker,两个storage,一个database。这里,笔者将一组tracker + storage + database部署在一台机器上面,将另一组tracker + storage部署在另一台机器上面。
初始安装及初始配置
MogileFS是基于Perl语言构建的项目,因此需要从CPAN上面下载MogileFS的模块,因此需要保证被部署的机器能够直接或者通过代理连接至互联网,如果无法联网,则需要梳理模块的依赖关系,通过其他途径下载所有所需要的perl的模块,并且手工编译安装。
首先,在两台机器上面安装cpanm工具,用于安装模块:
wget http://xrl.us/cpanm -O /usr/bin/cpanm; sudo chmod +x /usr/bin/cpanm
cpanm安装完毕之后,在两台机器上面安装MogileFS所需要的模块:
cpanm App::cpanminus cpanm IO::AIO cpanm IO::WrapTie cpanm Danga::Socket cpanm DBD::mysql cpanm MogileFS::Server cpanm MogileFS::Client cpanm MogileFS::Utils
模块安装完毕之后,对Sys::Syscall模块进行降级操作。默认情况下通过cpan安装完MogileFS之后,其依赖模块Sys::Syscall的版本为0.25,如果不降级的话,会在运行时的log里面出现crash信息,并且无法完成devcount的冗余。笔者这里下载的旧版本是0.23版本的模块压缩包。由于该模块的特性,可以直接跳过编译步骤,将解压之后的模块文件夹里面的lib/Sys/Syscall.pm直接拷贝到/usr/local/share/perl5/Sys/目录下面,对原有0.25版本的模块进行替换。
tar -zxvf Sys-Syscall-0.23 cp -f Sys-Syscall-0.23/lib/Sys/Syscall.pm /usr/local/share/perl5/Sys/
通过如下命令查看模块版本是否已经替换为0.23版本:
grep -E "VERSION\s" /usr/local/share/perl5/Sys/Syscall.pm
安装完毕之后,进入配置阶段。首先在两台机器上面创建/etc/mogilefs这个目录,用于存放配置文件。其次在两台机器上面创建mogilefs组和mogilefs用户,用于运行mogileFS相关的进程(笔者将上述用户和组创建为系统用户和系统组)。之后再在两台机器上面创建/mog_data目录,用于模拟storage的设备目录的父目录,并赋予mogilefs用户和mogilefs用户组权限。
mkdir /etc/mogilefs groupadd -r mogilefs useradd -r -g mogilefs -d /home/mogilefs -s /bin/bash -m mogilefs mkdir /mog_data/ chown mogilefs.mogilefs /mog_data
由于利用CPAN安装的MogileFS并未涉及到配置文件,因此这里笔者使用了GitHub上面提供的mogilefsd.conf文件和mogstored.conf文件作为MogileFS系统的配置文件。mogilefsd.conf文件作为tracker的配置,而mogstored.conf为storage进行配置。将上述两个文件下载,并拷贝到两台机器的/etc/mogilefs目录下面
cp mogilefsd.conf /etc/mogilefs cp mogstored.conf /etc/mogilefs
mogilefsd.conf配置文件的内容如下所示,其中几个比较重要的参数例如db_dsn用于定义database的IP地址(这里定义为笔者环境里面的192.168.11.130这台机器),db_user定义mogilefs进程连接数据库时所使用的用户名,db_pass定义mogilefs进程连接数据库时所使用的密码,listen定义tracker的监听套接字地址和端口:
# Enable daemon mode to work in background and use syslog daemonize = 1 # Where to store the pid of the daemon (must be the same in the init script) pidfile = /home/mogilefs/mogilefsd.pid # Database connection information db_dsn = DBI:mysql:mogilefs:host=192.168.11.130 db_user = mogile db_pass = mogile # IP:PORT to listen on for mogilefs client requests listen = 0.0.0.0:7001 # Optional, if you don‘t define the port above. conf_port = 7001 # Number of query workers to start by default. query_jobs = 10 # Number of delete workers to start by default. delete_jobs = 1 # Number of replicate workers to start by default. replicate_jobs = 5 # Number of reaper workers to start by default. # (you don‘t usually need to increase this) reaper_jobs = 1 # Number of fsck workers to start by default. # (these can cause a lot of load when fsck‘ing) #fsck_jobs = 1 # Minimum amount of space to reserve in megabytes # default: 100 # Consider setting this to be larger than the largest file you # would normally be uploading. #min_free_space = 200 # Number of seconds to wait for a storage node to respond. # default: 2 # Keep this low, so busy storage nodes are quickly ignored. #node_timeout = 2 # Number of seconds to wait to connect to a storage node. # default: 2 # Keep this low so overloaded nodes get skipped. #conn_timeout = 2 # Allow replication to use the secondary node get port, # if you have apache or similar configured for GET‘s #repl_use_get_port = 1
mogstored.conf配置文件的内容如下所示,定义最大连接数为10000,定义http接口以及管理接口的套接字端口,定义根目录(所有设备目录的父目录)为/mog_data。
maxconns = 10000 httplisten = 0.0.0.0:7500 mgmtlisten = 0.0.0.0:7501 docroot = /mog_data/
之后需要在database里面创建mogilefs的连接用户。登录mysql节点,做如下配置。其中的pwd代表用户的登录密码。
GRANT ALL PRIVILEGES ON *.* TO [email protected]‘%‘ IDENTIFIED BY ‘pwd‘ WITH GRANT OPTION GRANT ALL PRIVILEGES ON mogilefs.* TO [email protected]‘%‘ IDENTIFIED BY ‘pwd‘ WITH GRANT OPTION
退出mysql命令行,在linux shell上面输入如下命令,用于初始化mogilefs数据库。
mogdbsetup --dbhost=192.168.11.130 --dbport=3306 --dbrootuser=root --dbrootpass=pwd --dbuser=mogile --dbpass=pwd
初始化完毕之后,登录mysql,可以查看一下新建的数据库:
MariaDB [(none)]> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | demo | | mogilefs | | mysql | | performance_schema | | solo | | test | | wp | +--------------------+ 8 rows in set (0.00 sec) MariaDB [(none)]> use mogilefs Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed MariaDB [mogilefs]> show tables; +----------------------+ | Tables_in_mogilefs | +----------------------+ | checksum | | class | | device | | domain | | file | | file_on | | file_on_corrupt | | file_to_delete | | file_to_delete2 | | file_to_delete_later | | file_to_queue | | file_to_replicate | | fsck_log | | host | | server_settings | | tempfile | | unreachable_fids | +----------------------+ 17 rows in set (0.00 sec)
初始化数据库完毕之后,我们需要提供让tracker和storage启动工作的启动脚本。其实,让tracker和storage工作的命令非常简单,如下所示:
启动tracker su - mogilefs -c "/usr/local/bin/mogilefsd -c /etc/mogilefs/mogilefsd.conf --daemon" 启动storage su - mogilefs -c "/usr/local/bin/mogstored -c $configfile --daemon"
将上述命令封装为mogilefsd和mogstored启动脚本,内容如下所示:
- mogilefsd启动脚本:
#!/bin/bash -x # # chkconfig: - 85 15 # description: MogileFS tracker # processname: mogilefsd # config: /etc/mogilefs/mogilefsd.conf # pidfile: /home/mogilefs/mogilefsd.pid . /etc/rc.d/init.d/functions lockfile=${LOCKFILE-/var/lock/subsys/mogilefsd} RETVAL=0 pidfile=‘/home/mogilefs/mogilefsd.pid‘ start() { ulimit -n 65535 echo -n $"Starting mogilefsd" su - mogilefs -c "/usr/local/bin/mogilefsd -c /etc/mogilefs/mogilefsd.conf --daemon" &> /dev/null RETVAL=$? [ $RETVAL = 0 ] && success && touch ${lockfile} && pidof /usr/local/bin/mogilefsd > $pidfile || failure echo return $RETVAL } stop() { echo -n $"Stopping mogilefsd" netstat -nlp|grep "mogilefsd"|grep -v grep|awk ‘{print $7}‘ |awk -F"/" ‘{print $1}‘|xargs kill -9 RETVAL=$? [ $RETVAL = 0 ] && success && rm -f ${lockfile} || failure echo } reload() { echo -n $"Reloading mogilefsd: " killall mogilefsd -HUP RETVAL=$? [ $RETVAL = 0 ] && success || failure echo } case "$1" in start) start ;; stop) stop ;; status) status mogilefsd RETVAL=$? ;; restart) stop sleep 1 start ;; reload) reload ;; *) echo $"Usage: mogilefsd {start|stop|restart|reload|status}" exit 1 esac exit $RETVAL
- mogstored启动脚本
#!/bin/bash -x # # chkconfig: - 86 14 # description: MogileFS storage # processname: mogstored # config: /etc/mogilefs/mogstored.conf # pidfile: /home/mogilefs/mogstored.pid . /etc/rc.d/init.d/functions lockfile=${LOCKFILE-/var/lock/subsys/mogstored} RETVAL=0 configfile=‘/etc/mogilefs/mogstored.conf‘ pidfile=‘/home/mogilefs/mogstored.pid‘ prog=$(which mogstored) start() { ulimit -n 65535 echo -n $"Starting mogstored" su - mogilefs -c "/usr/local/bin/mogstored -c $configfile --daemon" &> /dev/null RETVAL=$? [ $RETVAL = 0 ] && success && touch ${lockfile} && pidof /usr/local/bin/mogstored > $pidfile || failure echo return $RETVAL } stop() { echo -n $"Stopping mogstored" netstat -nlp|grep "mogstored"|grep -v grep|awk ‘{print $7}‘ |awk -F"/" ‘{print $1}‘|xargs kill -9 RETVAL=$? [ $RETVAL = 0 ] && success && rm -f ${lockfile} ${pidfile} || failure echo } reload() { echo -n $"Reloading mogstored: " killall mogstored -HUP RETVAL=$? [ $RETVAL = 0 ] && success || failure echo } case "$1" in start) start ;; stop) stop ;; status) status mogstored RETVAL=$? ;; restart) stop sleep 1 start ;; reload) reload ;; *) echo $"Usage: mogstored {start|stop|restart|reload|status}" exit 1 esac exit $RETVAL
在两台机器上面将mogilefsd和mogstored启动起来,从而完成初步的安装和配置工作。查看一下端口是否正常监听
systemctl start mogilefsd.service mogstored.service ss -tnl | grep -iE "7001|7500" LISTEN 0 128 *:7500 *:* LISTEN 0 128 *:7001 *:*
Tracker和Storage的配置
首先查看一下MogileFS所提供的命令行工具:
mogadm mogdbsetup mogfetch mogfileinfo moglistfids mogrename mogstored mogupload mogautomount mogdelete mogfiledebug mogilefsd moglistkeys mogstats mogtool
可以通过查阅man手册或者命令行的help工具来探索他们的用法。这里不再做详细的综述。
- 添加storage节点
MogileFS启动之后,利用mogadm命令做一下检测。指明tracker为192.168.11.130和192.168.11.132这两个节点。
$ mogadm --trackers=192.168.11.130:7001,192.168.11.132:7001 check Checking trackers... 192.168.11.130:7001 ... OK 192.168.11.132:7001 ... OK Checking hosts... No devices found on tracker(s).
需要添加hosts,即storage节点。同样通过mogadm命令进行添加。将添加的storage节点分别命名为store1和store2。添加完毕之后分别利用check和host list子命令来查看状态,正常情况下可以看到storage节点已经添加完毕,并且状态为alive。
$ mogadm --trackers=192.168.11.130:7001,192.168.11.132:7001 host add store1 --ip=192.168.11.130 --status=alive $ mogadm --trackers=192.168.11.130:7001,192.168.11.132:7001 host add store2 --ip=192.168.11.132 --status=alive $ mogadm --trackers=192.168.11.130:7001,192.168.11.132:7001 check Checking trackers... 192.168.11.130:7001 ... OK 192.168.11.132:7001 ... OK Checking hosts... [ 1] store1 ... OK [ 2] store2 ... OK Checking devices... host device size(G) used(G) free(G) use% ob state I/O% ---- ------------ ---------- ---------- ---------- ------ ---------- ----- ---- ------------ ---------- ---------- ---------- ------ total: 0.000 0.000 0.000 0.00% $ mogadm --trackers=192.168.11.130:7001,192.168.11.132:7001 host list store1 [1]: alive IP: 192.168.11.130:7500 store2 [2]: alive IP: 192.168.11.132:7500
- 给Storage节点添加设备device
需要给每一个storage节点添加device设备,用于存放文件。我们需要在mogstored.conf里面定义的docroot目录下面,创建多个名称为dev1, dev2, dev3, ……这样名称的目录。一般对于服务器而言,这些目录应该作为专门用于存储的磁盘的挂载点。假设dev1下面挂载了/dev/sde1分区,而该分区的大小为600GB,则MogileFS会认为,dev1可以使用的可用空间就为600GB。当然,这些目录下面也可以不用挂载磁盘设备。假设dev1下面并没有挂载磁盘设备,则MogileFS识别到的对应于dev1的存储可用空间大小,为dev目录所在磁盘的可用空间大小,即如果dev1目录位于/dev/sda1磁盘分区上面,而/dev/sda1分区的可用空间为100GB,则MogileFS会认为,dev1可以使用的可用空间就是100GB。
在笔者的环境下,docroot定义为/mog_data,而该目录位于根分区,因此dev所可以使用的空间便为根分区的可用空间。
在storage节点1上面创建/mog_data/dev1目录,在storage节点2上面创建/mog_data/dev2目录,并都赋予mogilefs用户权限和mogilefs组权限:
storage节点1: $ mkdir -pv /mog_data/dev1 $ chown mogilefs.mogilefs /mog_data/dev1 storage节点2: $ mkdir -pv /mog_data/dev2 $ chown mogilefs.mogilefs /mog_data/dev2
通过mogadm的device add子命令,在storage节点1上面添加dev1,在storage节点2上面添加dev2。添加完毕之后,用device list子命令进行查看。正常情况下,可以看到store1和store2下面分别多出了dev1和dev2设备,以及dev1和dev2设备的可用情况。
注:在添加的时候,dev1的节点编号为1,dev2的节点编号为2,以此类推,devn的节点编号为n
$ mogadm --trackers=192.168.11.130:7001,192.168.11.132:7001 device add store1 1 $ mogadm --trackers=192.168.11.130:7001,192.168.11.132:7001 device add store2 2 $ mogadm --trackers=192.168.11.130:7001,192.168.11.132:7001 device list store1 [1]: alive used(G) free(G) total(G) weight(%) dev1: alive 13.327 10.640 23.967 100 store2 [2]: alive used(G) free(G) total(G) weight(%) dev2: alive 13.327 10.640 23.967 100
于此同时,在两个节点的设备目录下面,也多出了一个名为usage的文件以及test-write文件夹。
- 创建Domain
如本文之前所提到,MogileFS使用Domain来存放文件。每一个Domain的实例,管理了一组key-value的”元数据-数据”对象,可以创建一个Domain的实例,名称为file,用于专门存储文本文件;可以创建另一个Domain的实例,名称为imgs,用于专门存储图片文件。每一Domain实例下面的key代表了一个被存储的文件的元数据索引,对应着被存储的文件本身,即value。
下面添加一个Domain实例,命名为imgs,用于存储图片。利用mogadm的domain add子命令添加,并利用domain list子命令进行查看。
$ mogadm --trackers=192.168.11.130:7001,192.168.11.132:7001 domain add imgs $ mogadm --trackers=192.168.11.130:7001,192.168.11.132:7001 domain list domain class mindevcount replpolicy hashtype -------------------- -------------------- ------------- ------------ ------- imgs default 2 MultipleHosts() NONE
添加Domain完毕之后,我们向上述imgs这个Domain里面上传一张图片。通过mogupload命令进行上传,并通过mogfileinfo命令进行查看。正常情况下,可以看到上传的1.jpg的devcount为2,即数据成功保存为了两份,并且访问地址分别为http://192.168.11.130:7500/dev1/0/000/000/0000000004.fid以及http://192.168.11.132:7500/dev2/0/000/000/0000000004.fid。
$ mogupload --trackers=192.168.11.130:7001,192.168.11.132:7001 --domain=imgs --key=‘1.jpg‘ --file=‘/root/konachan.png‘ $ mogfileinfo --trackers=192.168.11.130:7001,192.168.11.132:7001 --domain=imgs --key=‘1.jpg‘ - file: 1.jpg class: default devcount: 2 domain: imgs fid: 4 key: 1.jpg length: 2697564 - http://192.168.11.130:7500/dev1/0/000/000/0000000004.fid - http://192.168.11.132:7500/dev2/0/000/000/0000000004.fid
在storage节点上面,我们也可以看到相应的文件路径:
storage节点1上面: $ file /mog_data/dev1/0/000/000/0000000004.fid /mog_data/dev1/0/000/000/0000000004.fid: PNG image data, 1800 x 1111, 8-bit/color RGBA, non-interlaced storage节点2上面: $ file /mog_data/dev2/0/000/000/0000000004.fid /mog_data/dev2/0/000/000/0000000004.fid: PNG image data, 1800 x 1111, 8-bit/color RGBA, non-interlaced
通过Chrome浏览器,http访问上述两个图片资源:
至此,MogileFS的tracker,database,storage的配置初步结束。
Nginx添加mogilefs模块,作为MogileFS的前端反向代理
由上文所示,如果直接以fid形式对资源进行访问,则会带来一定的不便性。而Nginx的第三方模块mogilefs-module支持将Nginx作为tracker和storage的反向代理,使访问者只通过定义的key值便可以获取到指定的资源。
mogilefs的模块源代码在GitHub上面可以下载到。该源码的作者已经许久没有更新该模块了,笔者用Nginx1.12版本的Nginx通过静态编译的方式可以让其工作,但是还会遇到一些问题:
- 目前只能够通过静态编译的方式将该模块编译到nginx当中。从Nginx1.9版本开始,已经支持动态模块的装载与卸载。但是笔者将该模块的config文件修改为适合于动态编译的格式之后,加载模块启动Nginx的时候总会提示Segmentation fault,经过trace之后发现是该模块报了空指针的错误,解决方法目前尚未找到。
- 如果Nginx已经使用的其他的动态模块,再将mogilefs模块静态编译进入nginx之后,会造成部分动态模块也报Segmentation fault错误。目前已知的模块有http-echo-module, ajp-module。解决方法目前尚未找到。
- 针对该模块的mogilefs_methods指令,GET方法和DELETE方法经测试没有问题,但是PUT方法一直存在问题,开启nginx的debug模式,通过curl工具进行PUT操作时,在log里面存在upstream timed out (110: Connection timed out) while reading upstream错误,目前没有找到解决方法。
因此,笔者强烈建议,使用了mogilefs的nginx不要安装其他模块,而且该nginx只为了实现mogilefs的反向代理,不做其他功能。
将源码包下载之后,解压到任意路径(笔者这里将源码包解压到nginx1.12的源码目录里面)。在编译之前,先进入mogilefs的源码目录,修改ngx_http_mogilefs_modile.c源代码,找到ngx_http_mogilefs_create_spare_location函数,注释掉定义ngx_http_core_loc_conf_t的结构体指针*pclcf,注释掉pclcf指针的赋值语句。
修改完毕之后,可以进行编译了。在编译的时候,添加--add-module
选项,指向mogilefs的源码目录即可。
编译安装Nginx完毕之后,在配置文件里面添加如下内容,笔者使用7010端口作为Nginx反向代理mogilefs的端口,并且定义一个trackers的upstream服务器组,使用nginx默认的wrr轮询调度算法。定义mogilefs的domain为之前创建的imgs,
upstream trackers { server 192.168.11.130:7001; server 192.168.11.132:7001; } server { listen 7010; location / { mogilefs_tracker trackers; mogilefs_domain imgs; mogilefs_methods get delete; mogilefs_pass { proxy_pass $mogilefs_path; proxy_hide_header Content-Type; proxy_buffering off; } } }
编辑完毕之后,启动或者重启nginx进程,通过http://192.168.5.178:7010/1.jpg这个地址来访问之前上传的图片:
一些总结
MogileFS是基于Perl语言研发的一款优秀的分布式文件系统,对于海量小文件的存储有着很大的优势,但是由于在新的CentOS7上面的模块兼容性问题,以及Nginx插件的老化问题,使得mogilefs渐渐被新的分布式文件系统所代替,诸如FastDFS,Ceph,SeaweedFS等产品。当然,笔者能力有限,在部署时所遇到的问题并未能够解决,如果有所进展,则会及时更新博文。