docker搭建fastdfs

2,531 阅读11分钟

简介

FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务

优点

优点:

i. 降低了系统的复杂度,处理效率更高

ii. 支持在线扩容机制,增强系统的可扩展性

iii. 实现了软RAID,增强系统的并发处理能力及数据容错恢复能力

iv. 支持主从文件,支持自定义扩展名

主备Tracker服务,增强系统的可用性

缺点

i. 不支持断点续传,不适合大文件存储

ii. 不支持POSIX(可移植操作系统接口)通用接口访问,通用性较低

iii. 对跨公网的文件同步,存在较大延迟,需要应用做相应的容错策略

iv. 通过API下载,存在单点的性能瓶颈

不直接提供权限控制,需自己实现

各大文件系统对比

ftp:可设密码文件安全性高(若设密码程序访问麻烦)、可断点续传、不可扩容、无容灾

HDFS:适用于大文件存储、云计算、不太适合单纯小文件存储(效率不高)

TFS:出自于淘宝,适用于海量小于1M文件、使用相对麻烦、文档相对少

GlusterFS:功能强大灵活、适合大文件、支持多种数据类型、使用麻烦、对硬件要求高、至少两个节点、中文资料少

mogileFS:与FastDFS架构比较像、FastDFS参考mogileFS,FastDFS比mogileFS效率高

FastDFS:出自于淘宝,适用于海量小文件(建议范围:4KB < file_size <500MB)、使用相对简单、小文件存储网上大多推荐使用FastDFS

fastdfs角色

FastDFS服务端有三个角色:跟踪服务器(tracker server)、存储服务器(storage server)和客户端(client)

a) tracker server: 跟踪服务器,主要做调度工作,起负载均衡的作用。在内存中记录集群中所有存储组和存储服务器的状态信息,是客户端和数据服务器交互的枢纽。相比GFS中的master更为精简,不记录文件索引信息,占用的内存量很少

b) storage server: 存储服务器(又称:存储节点或数据服务器),文件和文件属性(meta data)都保存到存储服务器上。Storage server直接利用OS的文件系统调用管理文件

client: 客户端,作为业务请求的发起方,通过专有接口,使用TCP/IP协议与跟踪器服务器或存储节点进行数据交互。FastDFS向使用者提供基本文件访问接口,比如upload、download、append、delete等,以客户端库的方式提供给用户使用

docker+nginx+fastdfs单机模式

环境以及软件版本

系统:centos7.7

nginx:nginx/1.12.2

主机:192.168.0.191

docker安装配置

docker安装配置这里就不重复了,记得配置阿里云加速器就ok

nginx安装配置

  • 拉取nginx镜像

    docker pull nginx
    

    image.png

  • 查看镜像

    docker images
    

    image.png

  • 运行nginx

    docker run --name nginx -d -p 80:80 nginx
    

    注意:这个时候并没有挂载nginx容器的相关目录,以后的所有配置都得进入容器更改,极不方便,上面启动之后

    使用命令

    docker exec -it nginx nginx –t
    

    查看nginx配置文件所在的目录,如下图所示配置文件在/etc/nginx/nginx.conf

    image.png

  • 复制容器内的配置文件到宿主机,然后在运行容器挂载该目录(docker好像不允许直接挂载nginx的配置文件,需要先复制容器内的配置文件到宿主机,否则会失败)

    docker cp -a nginx:/etc/nginx/ /home/nginx/conf/
    

    image.png

    复制之后,停止并删除已经运行的nginx容器,然后再以下面的方式启动

    docker stop nginx
    
    docker rm nginx
    

    image.png

  • 再次运行nginx

     docker run --name nginx -d -p 80:80 --restart always -v /home/nginx/conf/nginx/:/etc/nginx/ -v /home/nginx/log/:/var/log/nginx/ nginx
    

    image.png

    1. –v:将宿主机挂载到容器方便进行数据同步,也方便修改配置,如下图所示便是nginx的挂载目录,修改相关配置都会同步到容器

      image.png

    2. –p:指定端口

    3. --restart always:表示开机启动,也可不加

  • 访问

    http://192.168.0.191/
    

    因为指定的端口是80,这里可以不写端口访问

    image.png

    以上Nginx的安装就完成了,之后结合fastdfs的时候再说具体配置

fastdfs安装配置

  • 拉取fastdfs镜像

    docker pull delron/fastdfs
    

    拉取最新版本

    image.png

  • 查看镜像

    docker images
    

    image.png

  • 使用docker镜像构建tracker容器(跟踪服务器,起到调度的作用)

    docker run -dti --network=host --name tracker -v /var/fdfs/tracker:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs tracker
    

    -v:表示将宿主机目录挂载到容器

    image.png

  • 使用docker镜像构建storage容器(存储服务器,体统容量和备份服务)

    docker run -dti  --network=host --name storage -e TRACKER_SERVER=192.168.0.191:22122 -v /var/fdfs/storage:/var/fdfs  -v /etc/localtime:/etc/localtime  delron/fastdfs storage
    

    -v: 表示将宿主机目录挂载到容器

    TRACKER_SERVER=本机ip:22122,本机IP地址不要使用127.0.0.1

    image.png

  • 进入storage容器,到storage的配置文件中配置http访问的端口,配置文件在/etc/fdfs/目录下的storage.conf

    docker exec -it storage bash
    

    image.png

    默认端口是8888,也可以不进行修改,我这里就没有修改了,使用默认的

    image.png

  • 配置nginx,进入storage容器配置nginx,在/usr/local/nginx/conf/目录下,修改nginx.conf文件

    docker exec -it storage bash
    
    cd /usr/local/nginx/conf/
    
    vi nginx.conf
    

    image.png

    默认端口是8888,默认配置不修改也可以,这里我使用的是默认的

    注意:如果上面步骤的storage.conf 端口改了,那么这里nginx的配置也要改,需要保持一致

    image.png

  • 测试文件上传

    使用web模块进行文件上传,将文件上传至fastdfs文件系统

    1. 首先上传一张照片到/var/fdfs/storage/目录下

      image.png

    2. 进入storage容器执行以下命令,如下图所示

      /usr/bin/fdfs_upload_file /etc/fdfs/client.conf /var/fdfs/1.jpg
      

      image.png

      此时该图片已经上传至文件系统,并返回该图片存储的url: group1/M00/00/00/wKgAv13qDs-AfJN6ABCG3sAMTlE315.jpg

    3. 浏览器访问: http://192.168.0.191:8888/group1/M00/00/00/wKgAv13qDs-AfJN6ABCG3sAMTlE315.jpg 便会获取到此图片

      image.png

      根据文件返回的group1/M00/00/00/wKgAv13qDs-AfJN6ABCG3sAMTlE315.jpg可以得知:图片储存在

      服务器/var/fdfs/storage/data/00/00/目录下,如下图所示

      image.png

  • 开机启动容器

    docker update --restart=always tracker
    
    docker update --restart=always storage
    

    image.png

  • 常见问题

    1. ​ storage无法启动

    ​ 可以删除/var/fdfs/storage/data/目录下的fdfs_storage.pid文件,然后重新运行storage

springboot集成fastdfs

  • pom.xml

    <!-- https://mvnrepository.com/artifact/com.github.tobato/fastdfs-client -->
    <dependency>
        <groupId>com.github.tobato</groupId>
        <artifactId>fastdfs-client</artifactId>
        <version>1.26.7</version>
    </dependency>
    
  • yml配置

    #fastdfs服务配置
    fdfs:
      so-timeout: 1500
      connect-timeout: 600
      #  tracker-list: 192.168.0.191:22122  #单机连接服务配置
      tracker-list: 192.168.0.192:22122,192.168.0.193:22122 #集群连接服务配置
      #  visit-host: 192.168.0.191:8888 #访问服务配置
      visit-host: 192.168.0.191
    
  • java代码

    package com.xy.controller.fastdfs;
    
    import com.xy.entity.FastdfsFile;
    import com.xy.service.IFastdfsFileService;
    import com.github.tobato.fastdfs.domain.fdfs.StorePath;
    import com.github.tobato.fastdfs.exception.FdfsUnsupportStorePathException;
    import com.github.tobato.fastdfs.service.FastFileStorageClient;
    import io.swagger.annotations.ApiOperation;
    import io.swagger.annotations.ApiParam;
    import org.apache.commons.io.FilenameUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.IOException;
    
    import static com.xy.utils.common.GenerateUniqueCode.getTimeAddRandom7;
    import static com.xy.utils.date.DateUtils.getCurDateTimeFull;
    
    /**
     * @Description fastdfs文件操作
     * @Author xy
     * @Date 2019/12/6 17:25
     */
    @RestController
    @RequestMapping(value = "/fastdfs")
    public class FastdfsFileTestController {
    
        @Autowired
        private FastFileStorageClient storageClient;
    
        @Autowired
        IFastdfsFileService fastdfsFileService;
    
        @Value("${fdfs.visit-host}")
        private String hostIP;
    
    
        /**
         * @param multipartFile 文件
         * @return java.lang.String
         * @Description fastdfs文件上传
         * @Author xy
         * @Date 2019/12/6 17:49
         **/
        @ApiOperation(value = "文件上传")
        @PostMapping(path = "/fileUpload", name = "文件上传")
        public String uploadFile(
                @ApiParam(value = "文件")
                @RequestParam(name = "file", required = true) MultipartFile multipartFile
        ) throws IOException {
            String fullPath = "";
    
            StringBuffer stringBuffer = new StringBuffer();
            try {
                /***
                 * 文件上传
                 */
                StorePath storePath = storageClient.uploadFile(multipartFile.getInputStream(), multipartFile.getSize(),
                        FilenameUtils.getExtension(multipartFile.getOriginalFilename()), null);
                String filePath = storePath.getFullPath();
    
                /***
                 * 插库
                 */
                stringBuffer.append(hostIP).append("/").append(filePath);
                fullPath = new String(stringBuffer);
    
                FastdfsFile fastdfsFile = new FastdfsFile()
                        .setFdfsFileName(multipartFile.getOriginalFilename())
                        .setFdfsFileUrl(filePath)
                        .setFdfsFileFullUrl(fullPath)
                        .setFdfsCode(getTimeAddRandom7())
                        .setCreeTime(getCurDateTimeFull())
                        .setCreeUser("xy");
                boolean bool = fastdfsFileService.save(fastdfsFile);
                System.out.println(bool);
                System.out.println(filePath);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return fullPath;
        }
    
    
        /**
         * @param fileUrl 文件地址
         * @return void
         * @Description 删除文件
         * @Author xy
         * @Date 2019/12/9 9:08
         **/
        @ApiOperation(value = "删除文件")
        @PostMapping(path = "/deleteFile", name = "删除文件")
        public String deleteFile(
                @ApiParam(value = "文件地址")
                @RequestParam(name = "fileUrl", required = true) String fileUrl
        ) {
            if (StringUtils.isEmpty(fileUrl)) {
                return "参数不能为空";
            }
            try {
                StorePath storePath = StorePath.parseFromUrl(fileUrl);
                storageClient.deleteFile(storePath.getGroup(), storePath.getPath());
            } catch (FdfsUnsupportStorePathException e) {
                e.printStackTrace();
            }
            return "操作成功";
        }
    }
    
  • 特别注意

    代码中的配置文件关于fastdfs服务的配置,如下图所示端口是不一样的

    image.png

    服务连接端口是22122,是我们启动storage时指定的TRACKER_SERVER=192.168.0.191:22122

    image.png

    而访问端口是8888,是上面我们配置nginx默认端口

docker+nginx+fastdfs集群

环境

系统:同上

nginx:同上

fastdfs:同上

主机:

a) 192.168.0.191:nginx

b) 192.168.0.192:tracker1,storage1

c) 192.168.0.193:tracker2,storage2

fastdfs集群搭建

搭建过程中参考了

[docker下搭建fastfds集群版_苝花向暖丨楠枝向寒-CSDN博客_docker fastdfs 集群](blog.csdn.net/weixin_4024…)

[使用docker部署fastdfs集群版_冷雨夜@leon-CSDN博客_docker fastdfs集群](blog.csdn.net/zhanngle/ar…)

  • ​ 192.168.0.192/192.168.0.193,两台主机,拉取fastdfs镜像

    docker pull delron/fastdfs
    

    image.png

    image.png

    image.png

  • 启动两台主机的tracker

    1. 192.168.0.192:

      docker run -dti --network=host --restart always --name tracker -v /var/fdfs/tracker:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs tracker
      

      -v:表示将宿主机目录和容器内目录进行挂载

      --restart always:表示开机启动

    2. 192.168.0.193:

      docker run -dti --network=host --restart always --name tracker -v /var/fdfs/tracker:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs tracker
      
  • 启动两台机器的storage

    1. 192.168.0.192:

      docker run -dti --network=host --restart always --name storage -e TRACKER_SERVER=192.168.0.192:22122 -v /var/fdfs/storage:/var/fdfs  -v /etc/localtime:/etc/localtime  delron/fastdfs storage
      
    2. 192.168.0.193:

      docker run -dti --network=host --restart always --name storage -e TRACKER_SERVER=192.168.0.193:22122 -v /var/fdfs/storage:/var/fdfs  -v /etc/localtime:/etc/localtime  delron/fastdfs storage
      

      image.png

      image.png

    3. 分别进入两台机器的storage容器

      docker exec -it storage bash
      

      进入/etc/fdfs/目录,修改以下文件

      image.png

      三个文件都找到如下图所示,添加两台机器的的tracker_server(注意,是两台都要配置)

      tracker_server=192.168.0.192:22122

      tracker_server=192.168.0.193:22122

      image.png

  • 最后修改两台机器storage容器的nginx.conf配置文件

    1. 进入/usr/local/nginx/conf目录,都改成group1(本教程搭建貌似也可以不用改),如果端口改了这里也要更改,上面我使用的是默认的,所以这里不用改

      image.png

  • 配置好了之后可以验证一下

    1. 重启

      fdfs_storaged /etc/fdfs/storage.conf restart
      
    2. 查看储存节点信息

      fdfs_monitor /etc/fdfs/storage.conf
      

      会出现下图信息

      Group count:group总数

      Storage server count:存储服务总数

      Active server count:正在使用的存储服务总数

      ​ ...

      image.png

  • 进入其中一个storage服务,上传一张图片,进行测试,如下图所示

    1. 命令

      /usr/bin/fdfs_upload_file /etc/fdfs/client.conf /var/fdfs/3.jpg
      

      图片的由来,是因为启动的时候使用了-v /var/fdfs/storage:/var/fdfs 对宿主机和容器目录进行挂载,然后只要在宿主机上传一张图片,容器就会同步

      image.png

    2. 访问

      http://192.168.0.192:8888/group1/M00/00/00/wKgAwV3wkkuAfs7yABWOZCYlEqY065.jpg

      http://192.168.0.193:8888/group1/M00/00/00/wKgAwV3wkkuAfs7yABWOZCYlEqY065.jpg

      image.png

      image.png

  • 现在192机器的storage服务停掉,193也是可以访问的

    如下图所示192已经访问不了,193还可以访问

    image.png

    image.png

    重启之后,192又可以访问

    image.png

    image.png

nginx反向代理

上面的fastdfs集群搭建好之后,测试还要对ip切来切去,很麻烦,所以还记得最上面在191的机器上安装了一个nginx吗,下面用191服务器来做反向代理

上面已经将宿主机的目录和容器目录进行挂载,所以进入cd /home/nginx/conf/nginx/目录修改nginx的配置文件即可

image.png

新增以下配置:

#fastdfs的服务器以及访问端口,上面配置的时候我采用了默认端口所以是8888,如果有修改,这里需要保持一致
upstream fdfs {
        server 192.168.0.192:8888;
        server 192.168.0.193:8888;
}
server {
        listen 80;
        server_name localhost;
        location / {
            root html;
            index index.html index.htm;
            proxy_pass http://fdfs;#这里要和上面保持一致,注意后面不要带/斜杆,如果带了斜杆,访问的时候就要加上/fdfs/了
        }
}

image.png

保存退出然后docker restart nginx 重启nginx

  • 访问nginx主机ip+图片的路径,因为启动nginx指定的端口是80,这里可以不写端口访问

    http://192.168.0.191/group1/M00/00/00/wKgAwF3wUBWAFW58ABWOZCYlEqY420.jpg

    image.png

  • nginx默认采用轮询的方式进行代理,那么怎么知道是否代理192和193两台服务呢?

    我关闭192的storage服务,看nginx是否还能代理193,如果能访问成功,说明代理了两台服务

    image.png

    看下面还是可以访问的

    image.png

    如果我两台storage服务都关闭,看下图所示,访问失败,证明nginx代理有效

    image.png

  • 问题:上面说到nginx默认是轮询代理,那么我关闭了一台storage之后,理应刷新一下可以访问,再刷新一下是访问不到的,可我尝试多次就算关闭一台也一直可以访问

    提出这个问题之后,我立马觉得自己脑子生锈了,因为问题根本不存在,如果存在的话,假设某个网站配置了轮询代理,假如有一台服务蹦了,难道网站会像我提出的问题那样

    刷新一下可以显示,再刷新就显示不了嘛,显然是不存在的。

    正确的结论应该是:假设有一台服务崩了,nginx会立马剔除那台服务器,避免访问失败的情况

  • 扩展:nginx负载均衡的六种方式

    image.png

    image.png

    image.png

    image.png

    image.png

    image.png

    image.png

springboot连接配置

​ 连接配置只需要在配置文件多加一个服务,如下图所示

访问服务配置因为使用nginx做了代理,这里写nginx的主机就好,因为端口是80这里可以不带端口进行访问

​ 代码同上

image.png

image.png

image.png