最近在学习FastDFS集群时引发了许多思考.本篇主要介绍集群搭建的步骤和我个人一些理解.本文引入了当前网上很多博文的图片和步骤,主要以某平台分布式系统系列教程FastDFS部分为基础,阐述我FastDFS集群搭建方法及我个人对集群理解.
FastDFS是一个开源的分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。
FastDFS服务端有两个角色:跟踪器(tracker)和存储节点(storage)。跟踪器主要做调度工作,在访问上起负载均衡的作用。存储节点存储文件,完成文件管理的所有功能:存储、同步和提供存取接口。
FastDFS同时对文件的meta data进行管理。所谓文件的meta data就是文件的相关属性,以键值对(key value pair)方式表示,如:width=1024,其中的key为width,value为1024。文件meta data是文件属性列表,可以包含多个键值对。FastDFS系统结构如下图所示:
客户端 client 发起对 FastDFS 的文件传输动作,是通过连接到某一台 Tracker Server 的指定端口来实现的,Tracker Server 根据目前已掌握的信息,来决定选择哪一台 Storage Server ,然后将这个 Storage Server 的地址等信息返回给 client,然后 client 再通过这些信息连接到这台 Storage Server,将要上传的文件传送到给 Storage Server上。
FastDFS 集群规划:
跟踪服务器1:192.168.1.131 edu-dfs-tracker-1
跟踪服务器2:192.168.1.132 edu-dfs-tracker-2
存储服务器1:192.168.1.135 edu-dfs-storage-group1-1
存储服务器2:192.168.1.136 edu-dfs-storage-group1-2
存储服务器3:192.168.1.137 edu-dfs-storage-group2-1
存储服务器4:192.168.1.138 edu-dfs-storage-group2-2
环境:CentOS 6.6
用户:root
FastDFS v5.05
libfastcommon-master.zip(是从FastDFS和FastDHT中提取出来的公共C函数库)
fastdfs-nginx-module_v1.16.tar.gz
nginx-1.6.2.tar.gz
fastdfs_client_java._v1.25.tar.gz
源码地址:https://github.com/happyfish100/
下载地址:http://sourceforge.net/projects/fastdfs/files/
官方论坛:http://bbs.chinaunix.net/forum-240-1.html
(所有跟踪服务器和存储服务器均执行如下操作)
编译和安装所需的依赖包: # yum install make cmake gcc gcc-c++
下载libfastcommon-master.zip
到/usr/local/src
目录
下载地址:https://github.com/happyfish100/libfastcommon
# cd /usr/local/src/
# unzip libfastcommon-master.zip
# cd libfastcommon-master
# ./make.sh
# ./make.sh install
要注意libfastcommon的安装位置.后边会用到.可能是:/usr/lib64/libfastcommon.so
也可能是:
/usr/lib64/libfdfsclient.so/usr/local/lib/libfastcommon.so
,这里大家安装完libfastcommon留意一下安装到什么位置
/usr/local/lib/libfdfsclient.so
下载地址:https://github.com/happyfish100/fastdfs/releases
# cd /usr/local/src/
# tar -zxvf FastDFS_v5.05.tar.gz
# cd FastDFS
(编译前要确保已经成功安装了libfastcommon)
# ./make.sh
# ./make.sh install
采用默认安装的方式安装,安装后的相应文件与目录:
因为FastDFS服务脚本设置的bin目录是/usr/local/bin
,但实际命令安装在/usr/bin
,可以进入 /user/bin
目录使用以下命令查看fdfs的相关命令:
# cd /usr/bin/
# ls | grep fdfs
因此需要修改FastDFS服务脚本中相应的命令路径,也就是把/etc/init.d/fdfs_storaged
和/etc/init.d/fdfs_tracker
两个脚本中的/usr/local/bin
修改成/usr/bin
,更改方法:使用查找替换命令进统一修改:%s+/usr/local/bin+/usr/bin
注意:以上操作无论是配置tracker还是配置storage都是必须的,而tracker和storage的区别主要是在安装完fastdfs之后的配置过程中。
# cd /etc/fdfs/
# cp tracker.conf.sample tracker.conf
# vi /etc/fdfs/tracker.conf
修改的内容如下:
disabled=false #启用
port=22122 #tracker的端口号,一般采用22122这个默认端口
base_path=/fastdfs/tracker #tracker的数据文件和日志目录
其它参数保留默认配置,具体配置解释请参考官方文档说明:http://bbs.chinaunix.net/thread-1941456-1-1.html
mkdir -p /fastdfs/tracker
(参考基础目录base_path配置)
vi /etc/sysconfig/iptables
添加如下行:-A INPUT -m state --state NEW -m tcp -p tcp --dport 22122 -j ACCEPT
重启防火墙: service iptables restart
# /etc/init.d/fdfs_trackerd start
初次成功启动,会在/fastdfs/tracker目录下创建data、logs两个目录,可以通过以下两个方法验证是否启动成功:
netstat -unltp | grep fdfs
tailf /fastdfs/tracker/logs/trackerd.log
# /etc/init.d/fdfs_trackerd stop
# vi /etc/rc.d/rc.local
添加以下内容: /etc/init.d/fdfs_trackerd start
# cd /etc/fdfs/
# cp storage.conf.sample storage.conf
# vi /etc/fdfs/storage.conf
修改的内容如下:
disabled=false #启用配置文件
group_name=group1 #组名(第一组为group1,第二组为group2)
port=23000 #storage的端口号,同一个组的storage端口号必须相同
base_path=/fastdfs/storage #设置storage的日志目录
store_path0=/fastdfs/storage #存储路径
store_path_count=1 #存储路径个数,需要和store_path个数匹配
tracker_server=192.168.1.131:22122 #tracker服务器的IP地址和端口
tracker_server=192.168.1.132:22122 #多个tracker直接添加多条配置
#设置http端口号 (其它参数保留默认配置,具体配置解释请参考官方文档说明: http://bbs.chinaunix.net/thread-1941456-1-1.html )
http.server_port=8888
这里着重介绍一下tracker_server参数,这个参数如果设置了tracker外网ip,那么storage也会以外网ip形式存在于我们整个集群中(这句有点难理解,我们可以通过fdfs_moniter命令查看storage的ip,如果storage配置的tracker是tracker内网ip,那么这个storage的ip也为内网,反之如果我们在storage配置tracker时使用的外网ip,那么这个storage的ip为外网ip),我最近在看官网论坛很多人问到外网部署的问题。大部分情况storage是不大可能部署在外网的。因为storage之间要做文件同步,外网的话网络io是个不小的问题。
当然有些同学会有疑问如果不部署在外网,外网如何访问到storage,这里我们完全可以配置tracker是使用内网ip,这样storage之间同步都会走内网。但是访问storage我们用storage外网ip。或者使用后文提到的storage的nginx负载均衡策略。
同时我还做了一个无聊的测试,storage内外网混部。这种情况如果上传文件到了一个配置外网tracker地址的storage(A),那么A向B(配置tracker内网地址)同步文件是同步不过去的。因为对于A来讲B是一个内网storage(AB不同子网),但是如果文件上传到了B上,B同步给A却可以。
参考基础目录base_path配置,mkdir -p /fastdfs/storage
(略)
# /etc/init.d/fdfs_storaged start
初次成功启动,会在/fastdfs/storage目录下创建数据目录data 和日志目录logs,各节点启动,使用 tailf /fastdfs/storage/logs/storaged.log
命令监听存储节点日志,可以看到存储节点链接到跟踪器,并提示哪一个为leader跟踪器。同时也会看到同一组中的其他节点加入进来的日志信息。
查看23000端口监听情况:netstat -unltp|grep fdfs
所有Storage节点都启动之后,可以在任一Storage节点上使用如下命令查看集群信息:/usr/bin/fdfs_monitor /etc/fdfs/storage.conf
# /etc/init.d/fdfs_storaged stop
(略)
修改Tracker服务器中的客户端配置文件:
# cp /etc/fdfs/client.conf.sample /etc/fdfs/client.conf
# vi /etc/fdfs/client.conf
配置如下:
base_path=/fastdfs/tracker
tracker_server=192.168.1.131:22122
tracker_server=192.168.1.132:22122
执行文件上传命令:
# /usr/bin/fdfs_upload_file /etc/fdfs/client.conf /usr/local/src/FastDFS_v5.05.tar.gz
返回ID号:(能返回以下文件ID,说明文件上传成功)
group1/M00/00/00/wKgBh1Xtr9-AeTfWAAVFOL7FJU4.tar.gz
group2/M00/00/00/wKgBiVXtsDmAe3kjAAVFOL7FJU4.tar.gz
FastDFS 通过Tracker 服务器,将文件放在 Storage 服务器存储,但是同组存储服务器之间需要进入文件复制,有同步延迟的问题。假设Tracker服务器将文件上传到了192.168.1.135,上传成功后文件ID 已经返回给客户端。此时 FastDFS 存储集群机制会将这个文件同步到同组存储 192.168.1.136,在文件还没有复制完成的情况下,客户端如果用这个文件ID在192.168.1.136上取文件,就会出现文件无法访问的错误。而fastdfs-nginx-module可以重定向文件连接到源服务器取文件,避免客户端由于复制延迟导致的文件无法访问错误。
还有一个原因我们必须要安装fastdfs-nginx-module,fastdfs本身提供了http支持,但是原有的http支持不很好。所以我们这里安装fastdfs-nginx-module对外提供http服务
上传fastdfs-nginx-module_v1.16.tar.gz
到/usr/local/src
,执行以下命令解压:
# cd /usr/local/src/
# tar -zxvf fastdfs-nginx-module_v1.16.tar.gz
修改fastdfs-nginx-module的config配置文件vi /usr/local/src/fastdfs-nginx-module/src/config
CORE_INCS="$CORE_INCS /usr/local/include/fastdfs /usr/local/include/fastcommon/"
#修改为:CORE_INCS="$CORE_INCS /usr/include/fastdfs /usr/include/fastcommon/" 这里就是上边提到需要我们留意的安装路径
上传当前的稳定版本Nginx(nginx-1.6.2.tar.gz)到/usr/local/src目录
安装编译Nginx所需的依赖包:yum install gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zlib zlib-devel openssl openssl-devel
# cd /usr/local/src/
# tar -zxvf nginx-1.6.2.tar.gz
# cd nginx-1.6.2
# ./configure --prefix=/usr/local/nginx --add-module=/usr/local/src/fastdfs-nginx-module/src
# make && make install
复制fastdfs-nginx-module源码中的配置文件到/etc/fdfs目录,并修改
# cp /usr/local/src/fastdfs-nginx-module/src/mod_fastdfs.conf /etc/fdfs/
# vi /etc/fdfs/mod_fastdfs.conf
第一组Storage的mod_fastdfs.conf配置与第二组配置只有group_name不同: group_name=group2
connect_timeout=10
base_path=/tmp
tracker_server=192.168.1.131:22122
tracker_server=192.168.1.132:22122
storage_server_port=23000
group_name=group1 #第一组group_name
url_have_group_name = true
store_path0=/fastdfs/storage
这里很多博客更改了[group1],[group2]这一段,实际测试结果是这一段是没有必要设置的。这个共从1.x版本开始提供,到现在已经5.x版本。不设置这些参数情况下如果group1上的文件依然可以通过group2上的任意storage的nginx访问。
复制 FastDFS 的部分配置文件到/etc/fdfs 目录
# cd /usr/local/src/FastDFS/conf
# cp http.conf mime.types /etc/fdfs/
在/fastdfs/storage 文件存储目录下创建软连接,将其链接到实际存放数据的目录
# ln -s /fastdfs/storage/data/ /fastdfs/storage/data/M00
# vi /usr/local/nginx/conf/nginx.conf user root;
配置如下:
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8888;
server_name localhost;
location ~/group([0-9])/M00 {
#alias /fastdfs/storage/data;
ngx_fastdfs_module;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html; }
}
}
说明:
A、8888 端口值是要与/etc/fdfs/storage.conf
中的 http.server_port=8888
相对应,因为 http.server_port 默认为 8888,如果想改成 80,则要对应修改过来。
B、Storage 对应有多个 group 的情况下,访问路径带 group 名,如/group1/M00/00/00/xxx, 对应的 Nginx 配置为:
location ~/group([0-9])/M00 {
ngx_fastdfs_module;
}
C、如查下载时如发现老报 404,将 nginx.conf 第一行 user nobody 修改为 user root 后重新启动。
(略)
/usr/local/nginx/sbin/nginx
重启命令:/usr/local/nginx/sbin/nginx -s reload
设置 Nginx 开机启动vi /etc/rc.local
加入/usr/local/nginx/sbin/nginx
通过浏览器访问测试下载刚刚上传的文件
http://192.168.1.135:8888/group1/M00/00/00/wKgBh1Xtr9-AeTfWAAVFOL7FJU4.tar.gz
http://192.168.1.137:8888/group2/M00/00/00/wKgBiVXtsDmAe3kjAAVFOL7FJU4.tar.gz
这里我们通过一个例子讲解nginx模块的原理:
现在讲group1配置三台storage,分别为ABC,当我们上传一个文件到A(A是tracker给我指定的)。A接收文件后会启动两个线程来将文件同步给B和C。文件同步完成后我们将A和C上的文件更改一个名字(随便一个什么),出现如下现象:
出现这个情况的原因是,nginx当接到一个文件下载请求时会先到自己本地寻找这个文件,如果找不到会根据文件url找到这个文件最初被上传的storage(本例中是A)。将请求转发给源storage的nginx,如果还没请求到就返回错误。
如上例子如果我们同group内只有两个storage,那组内的storage安装nginx后自动实现了高可用。但是如果组内是三个storage,实际是一个伪高可用状态。
经过上边配置我们知道我们的集群现在还存在两个问题
为了解决以上问题,我们为所有storage的nginx再安装nginx,这个nginx不一定、不一定、不一定在tracker上安装。重要的事情说三遍。tracker的负载均衡,高可用是在我们客户端调用tracker时候实现的。比如如下配置(java客户端配置文件):
connect_timeout = 2
network_timeout = 30
charset = ISO8859-1
http.tracker_http_port = 8888
http.anti_steal_token = no
tracker_server=192.100.139.121:22122
tracker_server=192.100.139.122:22122
包括我们后续用到的keepalived也不是为了tracker做的双机热备,多个tracker是对等结构。
那么为什么在tracker上安装storage的nginx的nginx(这句话很绕,大家努力理解下吧),我猜想的原因应该是为了保证如果宕机,那么上传和下载一起宕机。再者就是为了资源的有效利用,因为tracker的机器压力不会很大。
# yum install gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zlib zlib-devel
openssl openssl-devel
上传 ngx_cache_purge-2.3.tar.gz 到/usr/local/src,解压
# cd /usr/local/src/
# tar -zxvf ngx_cache_purge-2.3.tar.gz
上传当前的稳定版本 Nginx(nginx-1.6.2.tar.gz)到/usr/local/src
目录
编译(添加 ngx_cache_purge 模块)
# cd /usr/local/src/
# tar -zxvf nginx-1.6.2.tar.gz
# cd nginx-1.6.2
# ./configure --prefix=/usr/local/nginx --add-module=/usr/local/src/ngx_cache_purge-2.3 # make && make install
设置负载均衡以及缓存vi /usr/local/nginx/conf/nginx.conf
配置如下
user root;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
use epoll;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server_names_hash_bucket_size 128;
client_header_buffer_size 32k;
large_client_header_buffers 4 32k;
client_max_body_size 300m;
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 16k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 128k;
proxy_temp_file_write_size 128k;
proxy_cache_path /fastdfs/cache/nginx/proxy_cache levels=1:2
keys_zone=http-cache:200m max_size=1g inactive=30d;
proxy_temp_path /fastdfs/cache/nginx/proxy_cache/tmp;
upstream fdfs_group1 {
server 192.168.1.135:8888 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.1.136:8888 weight=1 max_fails=2 fail_timeout=30s;
}
upstream fdfs_group2 {
server 192.168.1.137:8888 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.1.138:8888 weight=1 max_fails=2 fail_timeout=30s;
}
server {
listen 8000;
server_name localhost;
location /group1/M00 {
proxy_next_upstream http_502 http_504 error timeout invalid_header;
proxy_cache http-cache;
proxy_cache_valid 200 304 12h;
proxy_cache_key $uri$is_args$args;
proxy_pass http://fdfs_group1;
expires 30d;
}
location /group2/M00 {
proxy_next_upstream http_502 http_504 error timeout invalid_header;
proxy_cache http-cache;
proxy_cache_valid 200 304 12h;
proxy_cache_key $uri$is_args$args;
proxy_pass http://fdfs_group2;
expires 30d;
}
location ~/purge(/.*) {
allow 127.0.0.1;
allow 192.168.1.0/24;
deny all;
proxy_cache_purge http-cache $1$is_args$args;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
按以上 nginx 配置文件的要求,创建对应的缓存目录:
# mkdir -p /fastdfs/cache/nginx/proxy_cache
# mkdir -p /fastdfs/cache/nginx/proxy_cache/tmp
(略)
启动 Nginx : /usr/local/nginx/sbin/nginx
重启 Nginx : /usr/local/nginx/sbin/nginx -s reload
设置 Nginx 开机启动 : vi /etc/rc.local 加入:/usr/local/nginx/sbin/nginx
前面直接通过访问 Storage 节点中的 Nginx 的文件
http://192.168.1.135:8888/group1/M00/00/00/wKgBh1Xtr9-AeTfWAAVFOL7FJU4.tar.gz
http://192.168.1.137:8888/group2/M00/00/00/wKgBiVXtsDmAe3kjAAVFOL7FJU4.tar.gz
现在可以通过 Tracker 中的 Nginx 来进行访问
通过 Tracker1 中的 Nginx 来访问
http://192.168.1.131:8000/group1/M00/00/00/wKgBh1Xtr9-AeTfWAAVFOL7FJU4.tar.gz
http://192.168.1.131:8000/group2/M00/00/00/wKgBiVXtsDmAe3kjAAVFOL7FJU4.tar.gz
通过 Tracker2 中的 Nginx 来访问
http://192.168.1.132:8000/group1/M00/00/00/wKgBh1Xtr9-AeTfWAAVFOL7FJU4.tar.gz
http://192.168.1.132:8000/group2/M00/00/00/wKgBiVXtsDmAe3kjAAVFOL7FJU4.tar.gz
很重要:在tracker上为storage的nginx做负载均衡和高可用解决了上述三个问题,但是也带来了一个新问题。大家知道fastdfs在已有group上添加storage或者创建一个新的group(这个需求很实际,比如业务量增加,需要更多的空间存储文件)时,是不需要重启fastdfs的。但是一旦在tracker上安装了nginx,nginx配置的upstream包含了所有group以及group中的storage。会直接导致当我们添加新的storage或者group的时候必须重启tracker上的nginx。
添加新group或者storage顺序如下:
1. 要先更改tracker1上的nginx
2. 重启tracker1的nginx(此时不会断业务,后文keepalived会提到)
3. 更改tracker2上的nginx
4. 重启tracker2的nginx
5. 启动新加入的group的storage的nginx
6. 启动新加入的group的storage
如果是新添加group,那么第2步重启更改后的nginx不会出现问题,因为此时新的group还没有启动,所以不会有新group的文件下载请求
如果是新添加storage(向已有group添加),在第2步是有可能出现下载重试的。原因是group原有的下载请求可能被负载均衡到的新添加的storage上,但是此时新storage还没有启动,不过没关系我们刚刚配置的tracker上的max_fails=2
可以帮我做容错。建议配置成max_fails=[storage数]
ps:新添加tracker我没太细想,正常生产环境添加tracker的情况还是比较少的。除非有单实例转向集群,这个肯定是要停机更新的。
现在我们解决最后两个问题
1. tracker上的nginx宕机如何解决
2. 如何给客户端提供一个唯一的下载路径
分别在192.168.1.131 192.168.1.132:8000 服务器安装Keepalived实现双机热备,具体配置大家自己查一查吧。这篇不讲了,写的好累 - -
最终通过
http://vip:88/dfs/group1/M00/00/00/wKgBh1Xtr9-AeTfWAAVFOL7FJU4.tar.gz
访问图片就可以了。