相比较于MogileFS,FastDFS同样有tracker和storage这样的功能分类。但是FastDFS的tracker节点的元数据信息是由各个storage节点通过tcp协议上报得到的,因此在一定程度上减轻了tracker的负载压力。storage节点以group为单位进行组织。任何一个storage server都应该属于某个group,一个group应该包含多个storage server;在同一个group内部,各storage server的数据互相冗余。 
本文通过构建FastDFS集群,结合安装了fastdfs插件的Nginx,实现图片文件的上传和访问功能


关于FastDFS
  • 体系结构图 
    由下图可以看出,Tracker群中,各个tracker server相互独立,不进行相互的通信。Storage群中各个group组相互独立,不进行相互通信。也就是说,各个组之间保存的数据是不相同的。针对于每一个group组,其中的节点是属于相互备份的关系。图中的每一个Storage server会启动一个单独的线程主动向Tracker中的每一个tracker server报告其状态信息,包括磁盘使用情况,文件同步情况以及上传下载的次数统计等信息。

FastDFS + Nginx实现基于CentOS7平台的分布式文件存储与访问_第1张图片

  • 文件上传流程图 
    由下图可以看到,文件上传的流程可以分为4个步骤: 

  1. 客户端通过API接口,与Tracker发生通信

  2. Tracker返回给客户端一台可用的Storage server和端口号

  3. 客户端依据返回值,自动向对应的Storage server发送请求,并开始上传

  4. 上传完毕之后,Storage server返回给客户端所上传的文件的ID,并结束。

FastDFS + Nginx实现基于CentOS7平台的分布式文件存储与访问_第2张图片

  • 文件下载流程图 
    文件下载的流程可以分为3个步骤: 

  1. 客户端通过Tracker server下载指定Storage组中的某个文件

  2. Tracker server向客户端返回一个可用的Storage server的IP地址和端口号

  3. Client直接通过Tracker server返回的IP地址和端口与其中一台Storage server建立连接并进行文件下载

FastDFS + Nginx实现基于CentOS7平台的分布式文件存储与访问_第3张图片

  • FastDFS拓扑设计 
    设计结构和IP规划如下所示。这里采用一个客户端,两个tracker,4个storage,2个storage组的结构。

节点IP 节点功能
192.7.20.121 FastDFS客户端,ansible主控端
192.7.20.60 FastDFS tracker 节点1, FastDFS storage 节点1
192.7.20.61 FastDFS tracker 节点2,FastDFS storage 节点2
192.7.20.62 FastDFS storage 节点3
192.7.20.63 FastDFS storage 节点4

FastDFS + Nginx实现基于CentOS7平台的分布式文件存储与访问_第4张图片


ansible初始配置

为了方便安装配置,这里采用自动化运维工具ansible用来提高效率。首先在客户端上面配置3个host组,附加到/etc/ansible/hosts文件后面,分别命名为slave, ftracker, fstorage,内容如下所示。其中slave组和fstorage组的节点相同,只是出于配置的内容而分成了这两组:

[slave]
192.7.20.60
192.7.20.61
192.7.20.62
192.7.20.63

[ftracker]
192.7.20.60
192.7.20.61

[fstorage]
192.7.20.60
192.7.20.61
192.7.20.62
192.7.20.63

之后需要建立ssh免密码登录,在客户端主机上面使用ssh-copy-id命令将客户端公钥导入到4台slave节点上面。

ssh-copy-id ~/.ssh/id_rsa.pub [email protected]
ssh-copy-id ~/.ssh/id_rsa.pub [email protected]
ssh-copy-id ~/.ssh/id_rsa.pub [email protected]
ssh-copy-id ~/.ssh/id_rsa.pub [email protected]

之后,通过ping模块测试一下4台slave节点的连通状态,正常情况下,4台slave可通:

$ ansible slave -m ping
192.7.20.60 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
192.7.20.62 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
192.7.20.61 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
192.7.20.63 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}

编译FastDFS源码,并进行部署

从GitHub面可以下载到最新版本的fastdfs源码文件,以及fastdfs依赖库源码文件。将二者都下载到客户端主机,并用unzip命令解压,其目录中的内容如下所示:

fastdfs源码目录:
client  common  conf  COPYING-3_0.txt  fastdfs.spec  HISTORY  init.d  INSTALL  make.sh  php_client  README.md  restart.sh  stop.sh  storage  test  tracker

libfastcommon源码目录:
doc  HISTORY  INSTALL  libfastcommon.spec  make.sh  php-fastcommon  README  src

分别查看fastdfs.spec文件以及libfastcommon.spec文件,可以发现,spec文件中所要求的Source的格式为%{name}-%{version}.tar.gz的形式,因此,我们需要将fastdfs和libfastcommon的源码重新打包为带有version格式的tar.gz的压缩包。而版本号可以从spec文件的开头部分找到:

$ tar -zcvf libfastcommon-1.0.36.tar.gz libfastcommon-master/*
$ tar -zcvf fastdfs-5.0.11.tar.gz fastdfs-master/*

在root家目录下面创建rpmbuild文件夹,并在该文件夹里面再创建SOURCES和SPECS两个文件夹,将fastdfs.spec和libfastcommon.spec文件复制到/root/rpmbuild/SPECS目录下面,将libfastcommon-1.0.36.tar.gz和fastdfs-5.0.11.tar.gz复制到/root/rpmbuild/SOURCES目录下面:

$ mkdir -pv /root/rpmbuild/{SOURCES,SPECS}
$ cp fastdfs-master/fastdfs.spec /root/rpmbuild/SPECS
$ cp libfastcommon-master/libfastcommon.spec /root/rpmbuild/SPECS
$ cp fastdfs-5.0.11.tar.gz /root/rpmbuild/SOURCES
$ cp libfastcommon-1.0.36.tar.gz /root/rpmbuild/SOURCES

进入SPECS目录,运行rpmbuild命令,分别将fastdfs和libfastcommon编译为rpm包。正常情况下,所编译完成的rpm包会在/root/rpmbuild/RPMS/i386或者/root/rpmbuild/RPMS/x86_64目录下面:

$ cd /root/rpmbuild/SPECS
$ rpmbuild -bb libfastcommon.spec
$ rpmbuild -bb fastdfs.spec

$ ls /root/rpmbuild/RPMS/x86_64 | grep -iE 'fast*|lib*'
fastdfs-5.0.11-1.el7.centos.x86_64.rpm
fastdfs-debuginfo-5.0.11-1.el7.centos.x86_64.rpm
fastdfs-server-5.0.11-1.el7.centos.x86_64.rpm
fastdfs-tool-5.0.11-1.el7.centos.x86_64.rpm
libfastcommon-1.0.36-1.el7.centos.x86_64.rpm
libfastcommon-debuginfo-1.0.36-1.el7.centos.x86_64.rpm
libfastcommon-devel-1.0.36-1.el7.centos.x86_64.rpm
libfdfsclient-5.0.11-1.el7.centos.x86_64.rpm
libfdfsclient-devel-5.0.11-1.el7.centos.x86_64.rpm

下面就用上述生成的rpm包进行安装。首先需要在客户端上面进行安装。因为客户端也需要用fastdfs的命令进行文件的上传,查看,修改,删除……

yum localinstall /root/rpmbuild/RPMS/x86_64/*.rpm

之后将上述的rpm包放到一个名字为fastdfs_rpm的文件夹里面,并将该文件夹打包为tar.gz格式的压缩包,然后将其分发到4台slave上面,并解压。

$ cd
$ mkdir fastdfs_rpm
$ cp /root/rpmbuild/RPMS/x86_64/*.rpm fastdfs_rpm/
$ tar zcvf fastdfs_rpm.tar.gz fastdfs_rpm/
$ ansible slave -m copy -a 'src=/root/fastdfs_rpm.tar.gz dest=/root/'
$ ansible slave -m shell -a 'chdir=/root tar -zxvf fastdfs_rpm.tar.gz'


$ ansible slave -m shell -a 'ls /root/fastdfs_rpm/'
192.7.20.61 | SUCCESS | rc=0 >>
fastdfs-5.0.11-1.el7.centos.x86_64.rpm
fastdfs-debuginfo-5.0.11-1.el7.centos.x86_64.rpm
fastdfs-server-5.0.11-1.el7.centos.x86_64.rpm
fastdfs-tool-5.0.11-1.el7.centos.x86_64.rpm
libfastcommon-1.0.36-1.el7.centos.x86_64.rpm
libfastcommon-debuginfo-1.0.36-1.el7.centos.x86_64.rpm
libfastcommon-devel-1.0.36-1.el7.centos.x86_64.rpm
libfdfsclient-5.0.11-1.el7.centos.x86_64.rpm
libfdfsclient-devel-5.0.11-1.el7.centos.x86_64.rpm

192.7.20.60 | SUCCESS | rc=0 >>
fastdfs-5.0.11-1.el7.centos.x86_64.rpm
fastdfs-debuginfo-5.0.11-1.el7.centos.x86_64.rpm
fastdfs-server-5.0.11-1.el7.centos.x86_64.rpm
fastdfs-tool-5.0.11-1.el7.centos.x86_64.rpm
libfastcommon-1.0.36-1.el7.centos.x86_64.rpm
libfastcommon-debuginfo-1.0.36-1.el7.centos.x86_64.rpm
libfastcommon-devel-1.0.36-1.el7.centos.x86_64.rpm
libfdfsclient-5.0.11-1.el7.centos.x86_64.rpm
libfdfsclient-devel-5.0.11-1.el7.centos.x86_64.rpm

192.7.20.63 | SUCCESS | rc=0 >>
fastdfs-5.0.11-1.el7.centos.x86_64.rpm
fastdfs-debuginfo-5.0.11-1.el7.centos.x86_64.rpm
fastdfs-server-5.0.11-1.el7.centos.x86_64.rpm
fastdfs-tool-5.0.11-1.el7.centos.x86_64.rpm
libfastcommon-1.0.36-1.el7.centos.x86_64.rpm
libfastcommon-debuginfo-1.0.36-1.el7.centos.x86_64.rpm
libfastcommon-devel-1.0.36-1.el7.centos.x86_64.rpm
libfdfsclient-5.0.11-1.el7.centos.x86_64.rpm
libfdfsclient-devel-5.0.11-1.el7.centos.x86_64.rpm

192.7.20.62 | SUCCESS | rc=0 >>
fastdfs-5.0.11-1.el7.centos.x86_64.rpm
fastdfs-debuginfo-5.0.11-1.el7.centos.x86_64.rpm
fastdfs-server-5.0.11-1.el7.centos.x86_64.rpm
fastdfs-tool-5.0.11-1.el7.centos.x86_64.rpm
libfastcommon-1.0.36-1.el7.centos.x86_64.rpm
libfastcommon-debuginfo-1.0.36-1.el7.centos.x86_64.rpm
libfastcommon-devel-1.0.36-1.el7.centos.x86_64.rpm
libfdfsclient-5.0.11-1.el7.centos.x86_64.rpm
libfdfsclient-devel-5.0.11-1.el7.centos.x86_64.rpm

在4台节点上面安装上述rpm文件,至此,完成fastdfs在4台slave上面的部署工作。

$ ansible slave -m shell -a 'yum localinstall /root/fastdfs_rpm/*.rpm -y'

Tracker节点的配置

下一步我们需要配置两台Tracker节点。首先需要准备tracker进程的目录。其次,需要准备tracker.conf,http.conf,mime.types这三个文件,之后便是修改配置文件并启动tracker进程。

创建目录:
$ ansible ftracker -m shell -a 'mkdir -p /data/fastdfs'

准备tracker配置文件:
$ ansible ftracker -m shell -a 'chdir=/etc/fdfs cp tracker.conf.sample tracker.conf'

将客户端机器的源码目录中的http.conf和mime.types拷贝到tracker的/etc/fdfs目录下面:
$ ansible ftracker -m copy -a 'src=/root/fastdfs-master/conf/{http.conf,mime.types} dest=/etc/fdfs'

修改base_path选项为/data/fastdfs目录
$ ansible ftracker -m shell -a 'chdir=/etc/fdfs/ sed -i 's:base_path=.*:base_path=/data/fastdfs:g' tracker.conf'

修改http.server_port为80端口
$ ansible ftracker -m shell -a 'chdir=/etc/fdfs/ sed -i 's:http.server_port=.*:http.server_port=80:g' tracker.conf'

修改store_lookup的值为0,即对于不同的group使用round-robin方式:
$ ansible ftracker -m shell -a 'chdir=/etc/fdfs sed -i 's:store_lookup=.*:store_lookup=0:g' tracker.conf'

启动tracker进程
$ ansible ftracker -m shell -a 'service fdfs_trackerd start'

查看tracker进程监听端口
$ ansible ftracker -m shell -a 'ss -tnlp | grep -i trackerd'
192.7.20.61 | SUCCESS | rc=0 >>
LISTEN     0      128          *:22122                    *:*                   users:(("fdfs_trackerd",pid=2629,fd=5))

192.7.20.60 | SUCCESS | rc=0 >>
LISTEN     0      128          *:22122                    *:*                   users:(("fdfs_trackerd",pid=2828,fd=5))

Storage节点的配置

配置storage节点,规划如下:192.7.20.60和192.7.20.62这两台机器作为group1,192.7.20.61和192.7.20.63这两台节点作为group2。创建/data/fastdfs目录(虽然是storage节点,但是仍然需要这么一个目录),创建/data/storage目录作为存储节点的目录。准备storage.conf,http.conf,mime.types这三个文件。并对storage的配置文件进行修改,之后启动服务。

创建/data/fastdfs目录:
$ansible fstorage -m shell -a 'mkdir -p /data/fastdfs'

创建/data/storage目录:
$ ansible fstorage -m shell -a 'mkdir -p /data/storage'

准备storage.conf文件:
$ ansible fstorage -m shell -a 'chdir=/etc/fdfs cp storage.conf.sample storage.conf'

准备http.conf文件和mime.types文件:
$ ansible fstorage -m copy -a 'src=/root/fastdfs-master/conf/{http.conf,mime.types} dest=/etc/fdfs'

设定base_path为/data/fastdfs:
$ ansible fstorage -m shell -a 'sed -i 's:base_path=.*:base_path=/data/fastdfs:g' /etc/fdfs/storage.conf'

设定存储目录路径为/data/storage:
$ansible fstorage -m shell -a 'sed -i 's:store_path0=.*:store_path0=/data/storage:g' /etc/fdfs/storage.conf'

设定两台tracker_server,分别为192.7.20.60:22122和192.7.20.61:22122
ansible fstorage -m shell -a 'sed -i '/tracker_server=.*/a\tracker_server==/' /etc/fdfs/storage.conf'

ansible fstorage -m shell -a 'sed -i '/tracker_server=$/tracker_server=192.7.20.60:22122' /etc/fdfs/storage.conf'

ansible fstorage -m shell -a 'sed -i '/tracker_server==$/tracker_server=192.7.20.61:22122' /etc/fdfs/storage.conf'



定义192.7.20.61和192.7.20.63为group2组:
ansible 192.7.20.61 -m shell -a 'sed -i s/group_name=group1/group_name=group2/g /etc/fdfs/storage.conf'

ansible 192.7.20.63 -m shell -a 'sed -i s/group_name=group1/group_name=group2/g /etc/fdfs/storage.conf'



启动storaged进程:
$ ansible fstorage -m shell -a 'service fdfs_storaged start'

查看storaged进程监听端口:
ansible fstorage -m shell -a 'ss -tnlp | grep storaged'       
192.7.20.60 | SUCCESS | rc=0 >>
LISTEN     0      128          *:23000                    *:*                   users:(("fdfs_storaged",pid=1009,fd=5))

192.7.20.61 | SUCCESS | rc=0 >>
LISTEN     0      128          *:23000                    *:*                   users:(("fdfs_storaged",pid=834,fd=5))

192.7.20.62 | SUCCESS | rc=0 >>
LISTEN     0      128          *:23000                    *:*                   users:(("fdfs_storaged",pid=975,fd=5))

192.7.20.63 | SUCCESS | rc=0 >>
LISTEN     0      128          *:23000                    *:*                   users:(("fdfs_storaged",pid=922,fd=5))

Client的配置以及测试

使用192.7.20.121作为FastDFS的客户端。需要在客户端也建立/data/fastdfs目录,复制client.conf.sample文件为client.conf,并使用client.conf作为配置文件。在该配置文件里面,设定base_path的路径为/data/fastdfs,以及两台tracker_server的地址和端口。配置完毕之后如下所示:

$ grep base_path /etc/fdfs/client.conf
base_path=/data/fastdfs

$ grep tracker_server /etc/fdfs/client.conf | grep -vE "^#|^[^t]"
tracker_server=192.7.20.60:22122
tracker_server=192.7.20.61:22122

将client配置完毕之后,便可以进行测试。在测试之前,先列出fastdfs的工具程序。如下所示。其中fdfs_upload_file用于上传文件,fdfs_delete_file用于删除文件,fdfs_trackerd用于远程启动trackerd进程,fdfs_storaged用于远程启动storaged进程,fdfs_monitor用于查询storage节点的group信息。其余的选项,通过命令行帮助即可得到使用方式,在这里不一一赘述。

fdfs_appender_test    fdfs_append_file      fdfs_delete_file      fdfs_file_info        fdfs_storaged         fdfs_test1            fdfs_upload_appender
fdfs_appender_test1   fdfs_crc32            fdfs_download_file    fdfs_monitor          fdfs_test             fdfs_trackerd         fdfs_upload_file

进行文件的上传,由于使用了轮询storage的配置,因此在前后两次上传之后分别传送到了不同group组。通过fdfs_delete_file命令将其删除:

上传
$ fdfs_upload_file /etc/fdfs/client.conf kde.jpg
group1/M00/00/00/wAcUPFl4fp6AGwNPAA6q2wjnW8s033.jpg

$ fdfs_upload_file /etc/fdfs/client.conf resplendentSunset.jpg 
group2/M00/00/00/wAcUPVl4fsSAVEclAAUpOv3StqA293.jpg

查看各个storage节点上面的文件:
ansible fstorage -m shell -a 'ls /data/storage/data/00/00'                        
192.7.20.60 | SUCCESS | rc=0 >>
wAcUPFl4fp6AGwNPAA6q2wjnW8s033.jpg

192.7.20.62 | SUCCESS | rc=0 >>
wAcUPFl4fp6AGwNPAA6q2wjnW8s033.jpg

192.7.20.63 | SUCCESS | rc=0 >>
wAcUPVl4fsSAVEclAAUpOv3StqA293.jpg

192.7.20.61 | SUCCESS | rc=0 >>
wAcUPVl4fsSAVEclAAUpOv3StqA293.jpg


删除
$ fdfs_delete_file /etc/fdfs/client.conf group1/M00/00/00/wAcUPFl4fp6AGwNPAA6q2wjnW8s033.jpg

$ fdfs_delete_file /etc/fdfs/client.conf group2/M00/00/00/wAcUPVl4fsSAVEclAAUpOv3StqA293.jpg

查看各个storage节点上面的文件:
ansible fstorage -m shell -a 'ls /data/storage/data/00/00'                                
192.7.20.60 | SUCCESS | rc=0 >>


192.7.20.62 | SUCCESS | rc=0 >>


192.7.20.63 | SUCCESS | rc=0 >>


192.7.20.61 | SUCCESS | rc=0 >>

Nginx + fastdfs模块配置

通过Nginx的第三方模块fastdfs-nginx-module,安装部署在storage节点上面,可以实现将FastDFS存储的文件通过http协议呈现给浏览器客户端。该模块主要解决同步延迟的问题。同组之间的服务器需要复制文件,有延迟的问题。假设Tracker服务器将文件上传到了192.168.1.80,文件ID已经返回客户端,这时,后台会将这个文件复制到192.168.1.30,如果复制没有完成,客户端就用这个ID在192.168.1.30取文件,肯定会出现错误。这个fastdfs-nginx-module可以重定向连接到源服务器取文件,避免客户端由于复制延迟的问题,出现错误。

使用--add-module选项将上述模块包含进去,并将nginx重新编译(这里略去编译过程),并在配置文件中添加如下内容:

server {
        listen 8080;
        location /M00 {
                alias /data/storage/data;
                ngx_fastdfs_module;
        }
}

将编译好的nginx打包,利用ansible推送到所有storage上面去。

将FastDFS源码包中的mod_fastdfs.conf文件进行修改,如下所示,也将其推送到所有storage上面去,并解压缩。

base_path=/data/fastdfs
tracker_server=192.7.20.60:22122
tracker_server=192.7.20.60:22122
store_path0=/data/storage
log_filename=/data/fastdfs/logs/mod_fastdfs.log
$ ansible fstorage -m copy -a 'src=/root/nginx-1.12.0 dest=/root/'

创建nginx组和nginx用户,创建一个该nginx模块的log文件,用于记录log

$ ansible fstorage -m shell -a 'groupadd -r nginx'

$ ansible fstorage -m shell -a 'useradd -r -g nginx -s /sbin/nologin -m nginx'

$ ansible fstorage -m shell -a 'touch /data/fastdfs/logs/mod_fastdfs.log'

$ ansible fstorage -m shell -a 'chown nginx.nginx /data/fastdfs/logs/mod_fastdfs.log'

创建/opt/nginx目录,并将nginx包解压到该目录下面,并启动:

$ ansible fstorage -m shell -a 'mkdir /opt/nginx'
$ ansible fstorage -m shell -a 'tar -zxf /root/nginx-1.12.0.tar.gz -C /opt/nginx'
$ ansible fstorage -m shell -a 'chown -R nginx.nginx /opt/nginx'
$ ansible fstorage -m shell -a '/opt/nginx/sbin/nginx'

启动完毕之后,正常情况下,便可以从任何storage节点上面,访问任意group的资源了,如下所示:

FastDFS + Nginx实现基于CentOS7平台的分布式文件存储与访问_第5张图片

FastDFS + Nginx实现基于CentOS7平台的分布式文件存储与访问_第6张图片