拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

FastDFS在Ubuntu 24.04上的完整部署:从源码编译到Java客户端对接

FastDFS在Ubuntu 24.04上的完整部署:从源码编译到Java客户端对接

做中小规模文件服务选型的时候,我碰到最多的问题不是“该不该用分布式”,而是“到底用哪个”。如果目标是存图片、附件、音视频这类静态文件,吞吐要求没到海量级别,又不想背上 Hadoop 那套重型组件的运维负担,我的第一选择通常是 FastDFS。它是阿里巴巴开源的轻量级分布式文件系统,核心只有 Tracker 和 Storage 两个角色,部署链路极短,配合 Nginx 提供 HTTP 下载,再用 Java 客户端做上传管理,就能搭出一套最小化但完整的分布式文件服务。这篇文章基于 Ubuntu 24.04 完整走一遍:从源码编译、Tracker/Storage 配置、Nginx 接入到 Java 客户端对接,参数、命令、踩坑点一次讲清楚。适合手里有文件存储需求,但还没到必须上 HDFS、对象存储级别的团队参考。

1. 整体链路拆解:FastDFS 为什么适合“最小化”

1.1 Tracker 与 Storage 的分工关系

FastDFS 的架构在分布式文件系统里算非常清爽的。它没有独立元数据库,Tracker 只负责调度,把所有集群状态放在内存里;Storage 负责实际文件存储,按组划分,组内多台机器互为副本。客户端上传文件时,流程是:先连 Tracker 问“我该把文件传到哪台 Storage”,Tracker 根据剩余空间、组内负载返回一个可用的 Storage 地址,客户端再直连这台 Storage 完成数据写入。整个过程中 Tracker 不碰文件内容,所以并发压力很小,512MB 内存的机器就能跑得很舒服。

我用一个生活化类比帮助理解:Tracker 是前台调度员,只负责告诉你在哪个仓库有货架、走哪个门进;Storage 是真正的仓库管理员,文件由他亲手放到货架上。同一个车间的多台仓库(同一个 group)会互相复制,保证一台挂了另一台还有货。这种“调度与存储分离”的设计,让 FastDFS 的横向扩容非常直观:加 Storage 进组、加新组、调整 Tracker 就能完成。

1.2 为什么不是 HDFS、MinIO 或 Ceph

很多人一听到“分布式文件系统”就想到 HDFS,但我一般会劝他们先别急着上。HDFS 在设计上是为大数据离线批处理服务的,NameNode 内存吃紧、小文件存储效率低、在线随机读性能一般,而且依赖 ZooKeeper 和完整运维体系。Ceph 强大,但部署和运维复杂度对一个小团队来说完全是另一种量级。MinIO 适合需要 S3 接口的云原生场景,如果只是内网附件存储,它偏重了。

我整理了一个简单的选型对比:

方案核心定位优点明显的坑部署成本
HDFS大数据离线分析底座海量吞吐、生态完善小文件弱、运维重、内存占用高高
Ceph统一存储平台块/文件/对象全覆盖组件多、调优复杂高
MinIO对象存储S3 兼容、云原生友好功能多但相对重中
FastDFS轻量分布式文件系统部署简单、性能足够、成本低不支持随机修改、断点续传低

如果你的需求就是“存文件、取文件、删除文件”,对一致性要求没有强到金融级事务那么夸张,FastDFS 的性价比非常突出。这也是我坚持在中小项目里推荐它的原因:你不需要为了存几张图片就去维护一堆守护进程。

1.3 最小化部署拓扑:单机验证、生产两机器起步

本文搭建的环境是一台 Ubuntu 24.04 服务器,Tracker 和 Storage 部署在同一台机器上,只建一个 group。这样一个最小化系统,验证完所有链路后可以直接复制到生产环境做横向扩展。生产上我更推荐两台机器起步:Tracker 单独放在一台配置很低的机器上,Storage 和 Nginx 放在同一台机器。之所以让 Nginx 和 Storage 同机,是因为 fastdfs-nginx-module 在下载文件时会直接读 Storage 本地磁盘,避免跨机转发;如果 Nginx 在另一台机器,还需要额外做一层网络传输,性能会打折扣,还引入新的故障点。

单机部署的好处是排查问题省事,日志、端口、文件路径全在一台机器上,适合第一次接触这套技术栈的人建立完整认知。等你想清楚扩容方向后再拆开也不迟。

2. 安装前准备:编译 FastDFS 前需要处理的环境问题

2.1 安装基础依赖

FastDFS 和 fastdfs-nginx-module 都是 C 写的,Nginx 编译也需要一些基础库。先把依赖装齐,免得编译到一半才发现缺东西:

sudo apt update sudo apt install -y build-essential libtool libpcre3-dev libssl-dev zlib1g-dev

其中 build-essential 提供 gcc、make 等编译工具;libpcre3-dev 是 Nginx 解析正则需要的;libssl-dev 和 zlib1g-dev 留给 Nginx 的 SSL 和 gzip 模块。虽然最小化部署不一定马上用 HTTPS,但预留编译参数会方便很多。

提示:如果使用的是最小化安装的 Ubuntu,先确认 curl、wget、netstat 这些常用工具存在,后面排查问题都会用到。

2.2 编译 libfastcommon

libfastcommon 是 FastDFS 的基础依赖库,必须先装好。下载 release 包编译安装:

cd /usr/local/src wget https://github.com/happyfish100/libfastcommon/releases/download/V1.0.43/libfastcommon-1.0.43.tar.gz tar -zxvf libfastcommon-1.0.43.tar.gz cd libfastcommon-1.0.43 ./make.sh sudo ./make.sh install

编译安装结束后,确认共享库是否已经放在系统库路径下。Ubuntu 上一般会装到/usr/lib,个别版本可能装到/usr/local/lib。如果后面启动 FastDFS 报找不到libfastcommon.so,执行sudo ldconfig刷新缓存,或者临时加export LD_LIBRARY_PATH=/usr/lib:/usr/local/lib看看能不能解决。

2.3 编译安装 FastDFS 本体

FastDFS 源码包同样从 GitHub Releases 下载。我用的是 V6.06 版本,建议至少用 V6.x 之后的版本,旧版本在 Ubuntu 24.04 的高版本 GCC 下编译容易报类型错误:

cd /usr/local/src wget https://github.com/happyfish100/fastdfs/releases/download/V6.06/fastdfs-6.06.tar.gz tar -zxvf fastdfs-6.06.tar.gz cd fastdfs-6.06 ./make.sh sudo ./make.sh install

编译过程会输出不少 warning,只要没有 Error,一般没影响。安装完成后检查这些二进制是否生成:

  • /usr/bin/fdfs_trackerd:Tracker 启动程序
  • /usr/bin/fdfs_storaged:Storage 启动程序
  • /usr/bin/fdfs_upload_file:命令行上传工具
  • /usr/bin/fdfs_monitor:集群状态监控工具

同时conf/目录下有 tracker.conf、storage.conf、client.conf 三个模板文件,这是后面配置的基础。

2.4 Ubuntu 24.04 编译报错的避坑方向

Ubuntu 24.04 默认 GCC 编译器版本比较高,老的 FastDFS 源码在编译时经常出现 incompatible pointer type 这类类型不匹配错误。这本质上是老代码没有适配新编译器,不是你的操作失误。最省事的解决办法是换用较新的 release 版本源码,而不是去一点点改源码。如果实在只能用旧版本,再去搜索社区补丁。我的经验是:凡是编译环节出问题,先检查版本组合,不要急着怀疑环境。

3. 核心配置实战:Tracker 与 Storage 的参数解析与启动验证

3.1 把模板配置文件放到 /etc/fdfs

FastDFS 默认从/etc/fdfs读取配置,先把模板复制过去:

sudo mkdir -p /etc/fdfs sudo cp /usr/local/src/fastdfs-6.06/conf/tracker.conf /etc/fdfs/ sudo cp /usr/local/src/fastdfs-6.06/conf/storage.conf /etc/fdfs/ sudo cp /usr/local/src/fastdfs-6.06/conf/client.conf /etc/fdfs/

client.conf 在这条链路里不是给业务客户端用的,而是给 fdfs_upload_file、fdfs_monitor 这些命令工具用的。后面验证集群状态和上传文件都离不开它。

3.2 Tracker 配置:base_path 和 store_lookup 是重点

打开 tracker.conf,核心参数如下:

参数推荐值说明
port22122Tracker 监听端口,客户端和 Storage 都通过它寻址
base_path/data/fastdfs/trackerTracker 日志和数据存放目录,必须提前创建
connect_timeout30连接超时(秒)
network_timeout60网络超时(秒),传大文件建议调大
store_lookup2选择 group 的策略,0 轮询、1 按指定组、2 剩余空间优先
store_groupgroup1store_lookup 为 1 时可指定组名
reserve_storage_space10%Storage 预留空间比例,防止磁盘被写满
http.server_port8080Tracker 自带 HTTP 端口,本文用不到

这里最容易忽略的是base_path需要手动创建。FastDFS 启动时不会自动 mkdir,目录不存在时进程很容易启动失败,而且日志不够显眼。提前建好:

sudo mkdir -p /data/fastdfs/tracker

启动并检查:

sudo fdfs_trackerd /etc/fdfs/tracker.conf start netstat -tlnp | grep 22122

如果端口没起来,打开/data/fastdfs/tracker/logs/trackerd.log看报错。绝大多数启动失败都能在日志里找到直接原因。

3.3 Storage 配置:真正落盘的地方是 store_path0

打开 storage.conf,关键参数如下:

参数推荐值说明
group_namegroup1存储组名,同组内互为副本
port23000Storage 监听端口
base_path/data/fastdfs/storageStorage 的日志目录,需提前创建
store_path_count1存储路径数量
store_path0/data/fastdfs/storage_data文件实际存储根目录
tracker_server127.0.0.1:22122向哪个 Tracker 注册
http.server_port8888Storage 自带 HTTP 端口,本次用不到

有一个概念必须理解:store_path0下会自动生成data目录,真正的文件会落在data/00/00/这种两级哈希子目录下。所以上传返回的group1/M00/00/00/xxx.jpg中,M00对应的就是data目录。这个映射关系到后面配 Nginx 非常关键。

创建目录并启动:

sudo mkdir -p /data/fastdfs/storage /data/fastdfs/storage_data sudo fdfs_storaged /etc/fdfs/storage.conf start

启动后看/data/fastdfs/storage/logs/storaged.log,确认 Storage 已经向 Tracker 成功注册。

3.4 检查集群状态:fdfs_monitor 是核心体检工具

编辑 client.conf:

tracker_server=127.0.0.1:22122 base_path=/data/fastdfs/client

创建目录后运行:

sudo mkdir -p /data/fastdfs/client fdfs_monitor /etc/fdfs/client.conf

输出里重点关注 Storage 状态是否为 ACTIVE、存储容量和可用空间是否正确。看到 Storage 数量为 1 且状态正常,说明 Tracker 和 Storage 之间的通信已经通了。这个工具在后续排查问题时是第一个要看的,比乱猜日志高效得多。

一个小技巧:配合 watch 实时刷新状态,方便观察文件同步进度:

watch -n 2 'fdfs_monitor /etc/fdfs/client.conf | grep -A 5 "Storage 1"'

3.5 命令行上传文件,走通第一个最小闭环

准备一个测试文件,执行:

fdfs_upload_file /etc/fdfs/client.conf /tmp/test.png

正常会返回类似:

group1/M00/00/00/wKi8xGZfpfWAT7zTAAAAANFm17A123.png

这时去/data/fastdfs/storage_data/data/00/00/下面能看到实际文件。返回的这一串group1/M00/...是文件的唯一标识,后面 Nginx 的访问地址和 Java 客户端存储的都是它。

注意:如果上传时提示tracker_query_storage_store fail, error code: 2,基本可以断定 Storage 没有成功注册到 Tracker,优先检查 storaged.log 和 fdfs_monitor 输出,而不是怀疑 client.conf。

4. Nginx 集成:把文件存储映射成 HTTP 访问

4.1 为什么加了 FastDFS 还需要 Nginx

FastDFS 本身带了简单的 HTTP 能力,Tracker 和 Storage 都有 http.server_port 配置,但生产环境没人直接用它的内置 HTTP 服务。原因很现实:功能太弱,没有缓存、没有鉴权、没法上 HTTPS、并发性能也就那样。主流做法是在 Storage 所在机器部署 Nginx,加载 fastdfs-nginx-module,让请求进来后由模块直接映射到本地磁盘文件并返回。这样 Nginx 的高性能能力、HTTPS、限速、防盗链都可以顺手用上。

有一点需要提前理解:fastdfs-nginx-module 不是 apt 包,而是 Nginx 的第三方模块,必须编译进 Nginx,所以这节的整个流程核心就是“编译一个带模块的 Nginx”。

4.2 获取并调整 fastdfs-nginx-module

从 GitHub 获取源码:

cd /usr/local/src git clone https://github.com/happyfish100/fastdfs-nginx-module.git

模块编译前要调整它的 config 文件,否则 Nginx configure 阶段会找不到 FastDFS 的头文件。进入模块的 src 目录,把 config 文件里所有%FIRST_PATH%替换成/usr/local/include/fastdfs:

cd fastdfs-nginx-module/src sed -i 's#%FIRST_PATH%#/usr/local/include/fastdfs#g' config

前提是/usr/local/include/fastdfs下面确实有 FastDFS 安装后生成的头文件。如果编译 libfastcommon 和 FastDFS 时安装路径不一样,这里要按实际路径调整。

4.3 编译安装 Nginx

这里我强烈建议用 Nginx 1.20.x 系列。fastdfs-nginx-module 的更新节奏跟不上 Nginx 新版本的节奏,用最新版 Nginx 去编译老模块经常碰到源码兼容问题,换成 1.20.2 基本能绕开:

cd /usr/local/src wget https://nginx.org/download/nginx-1.20.2.tar.gz tar -zxvf nginx-1.20.2.tar.gz cd nginx-1.20.2 ./configure --prefix=/usr/local/nginx --add-module=/usr/local/src/fastdfs-nginx-module/src --with-http_ssl_module make -j4 sudo make install

编译过程中如果出现类型不匹配之类的报错,先确认自己用的是不是 1.20.x,绝大多数版本兼容问题都可以通过固定这个 Nginx 版本解决,不要想着去给模块源码打补丁,那样反而浪费时间。

4.4 配置 mod_fastdfs.conf 与 Nginx server 块

模块编译完成后,把模块自带的配置模板复制到 /etc/fdfs:

sudo cp /usr/local/src/fastdfs-nginx-module/src/mod_fastdfs.conf /etc/fdfs/

关键参数如下:

参数推荐值说明
connect_timeout10连接 Tracker 的超时时间
tracker_server127.0.0.1:22122指向 Tracker
storage_server_port23000模块回源到 Storage 的端口,必须和 storage.conf 保持一致
group_namegroup1当前 Storage 所在组
url_have_group_nametrueURL 中是否包含 group1 前缀
store_path_count1与 storage.conf 对应
store_path0/data/fastdfs/storage_data与 storage.conf 对应

修改 nginx.conf,在 server 块中加入:

location /group1/M00 { ngx_fastdfs_module; }

网上很多教程会在 location 里写 alias,实际跑下来这条 alias 完全可以不写,因为 ngx_fastdfs_module 在收到请求后,会根据 group_name、url_have_group_name、store_path0 自行换算物理路径。如果你照抄带 alias 的配置反而出现 404,删掉 alias,只保留模块指令就行。

启动并测试:

sudo /usr/local/nginx/sbin/nginx -t sudo /usr/local/nginx/sbin/nginx curl http://127.0.0.1/group1/M00/00/00/xxx.png

能正常输出文件内容,说明 Nginx 下载通道已经打通。

4.5 内置 HTTP 与 Nginx 模块怎么选

再回答一个反复被问的问题:storage.conf 里不已经有 http.server_port 吗,为什么还要 Nginx?

方式优点缺点
Storage 自带 HTTP零配置就能用无缓存、无鉴权、性能一般
Nginx + fastdfs-nginx-module高性能、可扩展 HTTPS/限速/防盗链需要编译安装

结论很明确:除非只是做技术验证,生产环境一律用 Nginx 模块。编译一次的成本很低,后面扩展 HTTPS、加代理、做缓存都方便,何必把自己的文件通道绑死在最简单的内置 HTTP 上。

5. Java 客户端对接:上传下载删除的完整实操

5.1 客户端选型与依赖引入

Java 侧有两类常见的 FastDFS 客户端:一类是官方 fastdfs-client-java,API 贴近 FastDFS 原生概念,适合任何框架,不在 Spring 体系内也能用;另一类是社区封装版 fastdfs-client,核心优势是集成 Spring Boot 方便,自带连接池和自动配置。

如果你使用 Maven,推荐先用社区封装版快速跑通:

<dependency> <groupId>com.github.tobato</groupId> <artifactId>fastdfs-client</artifactId> <version>1.27.2</version> </dependency>

如果更想贴近原生 API,可以用官方仓库自己构建一次:

git clone https://github.com/happyfish100/fastdfs-client-java.git cd fastdfs-client-java mvn install -DskipTests

下面代码我基于官方客户端来写,因为它的 API 最能表达 FastDFS 本身的上传流程。

5.2 初始化客户端与属性文件

创建 fdfs_client.properties,放在 classpath 根目录:

tracker_server=127.0.0.1:22122 base_path=/data/fastdfs/client

代码里初始化:

ClientGlobal.initByProperties("fdfs_client.properties");

这里有个很容易踩的问题:base_path 在 Java 进程运行的服务器上必须真实存在,否则初始化直接抛 IOException。很多新手把 base_path 当成一个随便写写的逻辑路径,服务端没建目录,结果一启动就报错。

5.3 上传文件的完整代码示例

上传流程分三步:连接 Tracker 获取连接、拿 Storage 客户端、上传得到文件 ID。

import org.csource.fastdfs.*; public class FastDFSUploader { public static void main(String[] args) throws Exception { ClientGlobal.initByProperties("fdfs_client.properties"); TrackerClient trackerClient = new TrackerClient(); TrackerServer trackerServer = trackerClient.getConnection(); if (trackerServer == null) { throw new RuntimeException("get tracker server connection failed"); } StorageServer storageServer = null; StorageClient1 storageClient = new StorageClient1(trackerServer, storageServer); String localFile = "/tmp/test.png"; String fileExt = "png"; String fileId = storageClient.upload_file1(localFile, fileExt, null); System.out.println("upload success, fileId = " + fileId); trackerServer.close(); } }

upload_file1返回的 fileId 其实就是 Nginx 访问 URL 的后半段,比如group1/M00/00/00/xxx.png。业务数据库只需要存这一个字符串,下载时拼上 Nginx 域名就能访问。这是 FastDFS 使用体验里非常舒服的一点:文件标识就是一条短字符串,没有复杂的目录树对象。

5.4 下载、查询与删除的代码套路

拿到 fileId 后,其他操作都很直观:

// 下载文件,返回字节数组 byte[] content = storageClient.download_file1(fileId); // 查询文件信息,拿到大小、时间、CRC FileInfo fileInfo = storageClient.get_file_info1(fileId); // 删除文件,返回 0 表示成功 int result = storageClient.delete_file1(fileId);

需要说明的是,这些方法签名来自官方客户端,社区封装版 API 名可能不同,但核心逻辑完全一样:你手里只要有 fileId,就能做下载、查询、删除三件事。下载时注意,如果文件比较大,download_file1一次性把整个文件装进 byte[] 会占内存,生产级实现应该用流式方式下载,或者干脆让前端直接指向 Nginx 下载,应用层只做鉴权和重定向。

5.5 Java 端生产经验

几个实践下来很重要的经验:

  • TrackerServer 连接用完必须 close。官方客户端没有自动放回池子的概念,不 close 会导致连接泄漏,最终把 Tracker 的连接数吃满。
  • 多 Tracker 场景下,属性文件里的 tracker_server 可以用逗号分隔多个地址,客户端会自动做故障切换。
  • 上传前先处理文件,尤其是图片,压缩后再传。FastDFS 擅长存静态文件,不代表适合当无限容量的对象存储用。
  • 下载路径优先让浏览器直接请求 Nginx,不要在 Java 应用里把文件流再转一次,否则大文件下载会把应用内存打爆。

6. 常见问题排查与避坑实录

6.1 安装与编译阶段的典型问题

编译安装阶段的报错,90% 出在依赖和版本组合上:

现象原因处理
./make.sh 提示找不到 gccbuild-essential 未安装sudo apt install build-essential
编译 FastDFS 时找不到 libfastcommon依赖库没装好或路径不对重新编译安装 libfastcommon,ldconfig 刷新
编译 Nginx 时报类型不匹配fastdfs-nginx-module 与 Nginx 版本不兼容换 Nginx 1.20.2
configure 提示找不到 pcre/ssl依赖缺包安装 libpcre3-dev、libssl-dev

编译阶段的坑最不值得花时间深挖,基本都能通过换版本解决,不要和源码死磕。

6.2 Tracker 与 Storage 启动注册阶段

服务起不来的最常见原因就是目录和端口:

现象原因处理
启动后进程秒退base_path 目录不存在mkdir -p 对应目录,再看日志
日志写权限不足/data 目录属主不对chown 或 chmod 调整
Storage 一直 OFFLINEtracker_server 地址写错、端口不通核对 storage.conf,测试 22122 连通性
fdfs_monitor 看不到 StorageStorage 启动失败看 storaged.log 日志
上传返回 error code 2Tracker 没有可用 Storage回到上一步排查 Storage 注册

这里想强调一个排查思路:FastDFS 的日志其实写得很清楚,问题是你愿不愿意去看。很多人上来就改配置、重启,反复试了几次毫无头绪,其实打开/data/fastdfs/storage/logs/storaged.log一眼就能看出原因。

6.3 Nginx HTTP 访问阶段

HTTP 访问 404 是出现频率最高的问题,我总结下来主要两个原因:第一,URL 里有没有 group 前缀和url_have_group_name配置不一致;第二,location 里画蛇添足写了 alias。前者改配置,后者删 alias。

现象原因处理
curl 404URL 前缀与配置不一致确认是否带 /group1,核对 url_have_group_name
nginx: [emerg] unknown directive "ngx_fastdfs_module"模块没有编译进 Nginx重新 configure 并 make install
下载时返回错误mod_fastdfs.conf 里的 store_path0 与 storage.conf 不一致统一 store_path0 路径

总之,HTTP 这层只要记住“模块负责解析路径映射”这一条,很多困惑都会消失。

6.4 Java 客户端对接阶段

Java 端的问题大多不是代码问题,而是环境没通:

现象原因处理
ClientGlobal.init 找不到配置文件路径写错或文件没在 classpath使用绝对路径,确认文件名
connect to tracker server fail22122 端口不通先 curl 测试,再查防火墙
上传报“没有可用 Storage”Storage 离线先用 fdfs_monitor 确认集群状态
上传后文件访问不了fileId 拼错或 Nginx 未配好单独 curl 测试 fileId

Java 端排查有个原则:先用命令行工具验证服务端是好的,再去怀疑代码。如果你 fdfs_upload_file 能传、curl 能下载,那 Java 端的问题基本就是配置路径或连接管理的问题。

6.5 用 systemd 管住进程,避免重启后手工拉起

手动start方式在服务器重启后会失效,生产环境要用 systemd 管理。创建/etc/systemd/system/fdfs-tracker.service:

[Unit] Description=FastDFS Tracker Server After=network.target [Service] Type=forking ExecStart=/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf start ExecStop=/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf stop Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target

Storage 同理,把二进制换成 fdfs_storaged、配置换成 storage.conf,然后:

sudo systemctl daemon-reload sudo systemctl enable fdfs-tracker fdfs-storage sudo systemctl start fdfs-tracker fdfs-storage

这样重启机器后服务自动拉起,不用再手动敲命令。

最后说两个我反复栽过的细节。第一,所有 base_path 和 storage_path 目录,一定要在启动前手工建好,FastDFS 不会替你 mkdir,目录不存在的时候它可能看起来是“启动成功”,实际进程已经退出,日志又不显眼,排查起来最耗时间。第二,版本别追求新,FastDFS 本体、fastdfs-nginx-module、Nginx 三者之间没有严格的官方兼容矩阵,但经过大量生产验证的组合就是 FastDFS 6.x、fastdfs-nginx-module 1.2x、Nginx 1.20.x,这套组合我在多台机器上跑过,编译和运行都很稳。要做最小化系统,照着这条链路一步步来,基本一次就能通。

返回列表