文件服务器怎么搭建和选购

结论: 内网共享选 Samba(Windows 混合环境)或 NFS(纯 Linux 环境);团队网盘选 Nextcloud 或 Cloudreve;程序调用的附件、图片、视频选 MinIO 或直接用云厂商对象存储;对外分发的大文件一律交给对象存储 + CDN。选购文件时服务器的核心不是 CPU,而是磁盘容量、随机 IO 和上行带宽。

"文件服务器"是个笼统的说法,它至少包含四类完全不同的需求:办公室里的共享盘、团队的私有网盘、程序存储图片的附件服务、以及对外下载的分发站。这四类需求的技术方案几乎不重叠,用错方案就会出现"用 Samba 给外网用户传文件,慢得没法用"这类问题。

先分清需求,再谈搭建。

六种方案怎么选?

结论:协议决定适用范围——Samba/NFS 只适合内网,MinIO/对象存储才适合程序与公网。

方案协议适用场景不适合硬件要求运维量
Samba (SMB/CIFS)SMB 3.x办公室内网共享,Windows/Mac/Linux 混用公网传输、跨地域低,2 核 4 GB + HDD 阵列即可低
NFSNFSv4Linux 服务器之间共享、K8s 存储后端暴露到公网、Windows 客户端低低
NextcloudHTTP/WebDAV团队私有网盘、在线预览、协同超大文件分发、高并发下载中,4 核 8 GB + SSD中高
CloudreveHTTP轻量私有网盘,支持多存储后端企业级权限体系低,2 核 4 GB 起低
MinIO(自建 S3)S3 API程序附件、图片视频存储,需 S3 兼容人工日常使用、在线预览中高,4 核 8 GB + NVMe/多盘中
云对象存储(OSS/COS/S3)S3 API一切公网分发、程序附件、备份归档内网低延迟随机读写无需服务器极低

一个重要的成本对比:自建 1 TB 可用存储,硬件加三年电费与维护,摊下来每 TB 年成本通常在 800~2000 元;而公有云对象存储的标准存储按量计费大致在每 GB 每月 0.1 元上下(即每 TB 每年约 1200 元),另计流量费。数据量小于 2 TB 且没有强内网需求时,对象存储通常更划算;数据量超过 10 TB 且访问集中在内网,自建才明显省钱。

容量和带宽怎么估?

结论:容量按"年增长率 × 留存年限 × 冗余系数"算,带宽按"日均下载量 ÷ 高峰小时数 × 冗余"算。

text
所需裸容量 = 年新增数据量 × 留存年限 × 1.3(冗余与快照)
可用容量   = 裸容量 × RAID 利用率
  RAID 1  : 50%
  RAID 5  : (N-1)/N(N 为盘数,建议不超过 8 盘)
  RAID 6  : (N-2)/N
  RAID 10 : 50%
所需上行带宽(Mbps) = 日均下载量(GB) × 1024 × 8 / (高峰小时数 × 3600) × 1.5

举例:设计团队每年新增 2 TB 素材,留存 3 年,用 4 块 8 TB 盘做 RAID 5(可用 24 TB):

  • 所需裸容量 = 2 × 3 × 1.3 = 7.8 TB,24 TB 可用容量绰绰有余,可按 5 年规划。
  • 20 人团队每天平均下载 60 GB,集中在 4 小时高峰:60 × 1024 × 8 / (4×3600) × 1.5 ≈ 51 Mbps。千兆内网(约 940 Mbps 实际)完全够,但若要给外网成员访问,就要考虑上行带宽是否够 50 Mbps。

按团队规模估算容量时,可以直接套下面这张速查表,再按实测数据校正:

团队类型人均常驻文件量年新增量估算3 年留存所需裸容量建议配置
10 人行政 / 财务(文档为主)20~50 GB0.3~0.8 TB1.2~3.2 TB2×4 TB HDD 做 RAID 1
20~50 人设计 / 影音100~300 GB2~6 TB7.8~23 TB4×8 TB HDD 做 RAID 5 + 1 TB SSD 缓存
50~200 人综合办公30~80 GB1.5~6 TB5.8~23 TB6~8 盘 RAID 6 + 热数据 SSD 池
程序附件 / 图片(MinIO)按业务调用量视接口调用量按 2 倍冗余规划4 核 8 GB + NVMe

并发下载带宽的估算公式更简单:并发人数 × 单用户期望吞吐 × 1.2。例如 20 人同时在线编辑素材、每人期望 10 MB/s(约 80 Mbps),则需要约 1920 Mbps,已远超千兆内网 940 Mbps 的实际吞吐。这时要么改成本地缓存编辑 + 集中归档的工作流,要么把内网升级到 2.5G 或 10G 交换机与网卡——对文件服务器来说,内网带宽往往比磁盘容量更早成为瓶颈。

需要提醒的是,公式里的"人均常驻文件量"和"年新增量"最好用现有数据实测,而不是拍脑袋估。下面这组命令可以在规划阶段摸清真实占用:

bash
# 规划阶段:摸清现有数据的真实占用与增长速度
du -sh /data/* | sort -hr | head -20          # 找出占用最大的目录
df -hT /data                                   # 剩余空间与文件系统类型
df -i /data                                    # inode 使用率,小文件场景必看

# 统计近 30 天新增量,推算年增长
find /data -type f -mtime -30 -printf '%s\n' | awk '{s+=$1} END {printf "近30天新增: %.2f GB\n", s/1024/1024/1024}'

拿到"近 30 天新增"后乘 12 即得年增长量。若现有系统已运行多年,建议再按"总占用 ÷ 已运行年数"交叉验证一次,两者取较大值更稳妥。

方案一:Samba 内网共享盘(最常用)

结论:Samba 是办公室文件服务器的默认选择,配置重点在用户认证、目录权限和回收站。

bash
# Ubuntu 24.04 安装 Samba
sudo apt update && sudo apt install -y samba samba-common-bin

# 创建共享目录与用户组
sudo mkdir -p /data/share /data/recycle
sudo groupadd smbusers
sudo useradd -M -s /sbin/nologin -G smbusers alice
sudo smbpasswd -a alice          # 设置 Samba 独立密码
sudo chgrp smbusers /data/share && sudo chmod 2775 /data/share

配置文件:

ini
# /etc/samba/smb.conf
[global]
   workgroup = WORKGROUP
   server string = File Server
   security = user
   map to guest = Bad User
   # 禁用已不安全的 SMB1,只允许 SMB2 及以上
   server min protocol = SMB2
   client min protocol = SMB2
   log file = /var/log/samba/log.%m
   max log size = 1000

[share]
   path = /data/share
   browseable = yes
   writable = yes
   valid users = @smbusers
   create mask = 0664
   directory mask = 2775
   # 开启回收站,防误删(vfs_recycle 需要额外配置)
   vfs objects = recycle
   recycle:repository = /data/recycle/%U
   recycle:keeptree = yes
   recycle:versions = yes

上面这段是"最小可用配置",只解决"能访问、能读写"。真正投产时要按部门拆成多个共享段,每个段用 valid users 限定可访问组,并用 write list / read list 细分读写级别:

ini
# /etc/samba/smb.conf —— 按部门拆分共享段的写法
[design]
   path = /data/share/design
   browseable = yes
   writable = yes
   valid users = @design @manager
   write list = @design
   read list = @dev
   create mask = 0664
   directory mask = 2775
   force group = design
   vfs objects = recycle full_audit
   recycle:repository = /data/recycle/%U
   recycle:keeptree = yes
   # 审计:把打开、改名、删除等动作写进 syslog local5
   full_audit:prefix = %u|%I|%m|%S
   full_audit:success = open close rename unlink rmdir mkdir
   full_audit:failure = none
   full_audit:facility = local5
   full_audit:priority = NOTICE

[finance]
   path = /data/share/finance
   browseable = no          # 不在网上邻居里列出,需手动输入路径访问
   writable = yes
   valid users = @finance
   create mask = 0660
   directory mask = 2770

几个参数的含义值得单独说明:force group = design 强制该共享下新建的文件属组为 design,配合目录的 setgid 位可以彻底避免"张三建的文件李四改不了";browseable = no 只是隐藏,不是保护,真正的安全边界仍然是 valid users;full_audit 把增删改动作写进 local5 设施,再用 rsyslog 单独落一个文件,出问题就能查到"谁在什么时候删了哪个文件"。审计日志会明显增大 IO 与磁盘占用,只建议对财务、合同这类敏感目录开启。

bash
# 校验配置语法并生效
testparm
sudo systemctl enable --now smbd nmbd

# 防火墙只放通内网网段(切勿对公网开放 445!)
sudo ufw allow from 192.168.1.0/24 to any port 445 proto tcp
sudo ufw allow from 192.168.1.0/24 to any port 139 proto tcp

# 本地验证共享是否可见
smbclient -L localhost -U alice

安全红线:Samba 的 445 端口绝不能对公网开放。 历史上多起勒索病毒(如 WannaCry 类)正是通过暴露在公网的 SMB 服务横向传播的。

方案二:NFS 用于 Linux 服务器之间共享

结论:NFS 适合服务器之间挂载同一份数据(如 K8s 的共享存储、多台 Web 共享上传目录),性能好但几乎没有安全加固手段。

bash
# 服务端安装
sudo apt install -y nfs-kernel-server
sudo mkdir -p /data/nfs && sudo chown nobody:nogroup /data/nfs

# /etc/exports:只允许内网段,sync 保证数据安全落盘
echo '/data/nfs 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)' | sudo tee -a /etc/exports
sudo exportfs -rav
sudo systemctl enable --now nfs-server
showmount -e localhost

客户端挂载:

bash
sudo apt install -y nfs-common
sudo mkdir -p /mnt/nfs
# 推荐用 NFSv4,并加 soft/timeo 避免服务端宕机时客户端卡死
sudo mount -t nfs -o vers=4,soft,timeo=100,retrans=3 192.168.1.10:/data/nfs /mnt/nfs

# 写入 /etc/fstab 实现开机自动挂载
echo '192.168.1.10:/data/nfs /mnt/nfs nfs vers=4,soft,timeo=100,retrans=3,_netdev 0 0' | sudo tee -a /etc/fstab

挂载选项的取舍要清楚:vers=4 明确指定 NFSv4,避免客户端与服务端协商回 v3 造成行为不一致;soft 表示服务端超时后返回错误而不是无限等待,配合 timeo=100(单位是 0.1 秒,即 10 秒)与 retrans=3(重试 3 次)控制等待上限;若业务要求数据强一致可改用 hard,代价是服务端宕机时客户端进程会卡在 IO 上无法中断。_netdev 告诉系统这是网络设备、需等网络就绪后再挂载,漏掉它可能导致开机时挂载失败甚至进不了系统。

另外,/etc/exports 里的 no_root_squash 是个高危选项:它意味着客户端的 root 用户在共享目录上拥有完全权限。只在可信内网、且确实需要 root 写文件时才开,一般场景应保留默认的 root_squash(把客户端 root 映射成 nobody)。

方案三:Nextcloud / MinIO / Cloudreve(团队网盘与程序存储)

结论:这三者都用 Docker 部署最快,区别在定位——Nextcloud 重协同,Cloudreve 轻量,MinIO 面向程序。

yaml
# docker-compose.yml —— Nextcloud + MariaDB + Redis
version: "3.8"
services:
  db:
    image: mariadb:11.4
    restart: always
    command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
    volumes:
      - ./db:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: <改成强密码>
      MYSQL_PASSWORD: <改成强密码>
      MYSQL_DATABASE: nextcloud
      MYSQL_USER: nextcloud

  redis:
    image: redis:7-alpine
    restart: always

  app:
    image: nextcloud:30-apache
    restart: always
    ports:
      - "8080:80"
    depends_on:
      - db
      - redis
    volumes:
      - ./nextcloud:/var/www/html
      - ./data:/var/www/html/data
    environment:
      MYSQL_HOST: db
      MYSQL_DATABASE: nextcloud
      MYSQL_USER: nextcloud
      MYSQL_PASSWORD: <改成强密码>
      REDIS_HOST: redis

MinIO 单机部署(适合做程序的 S3 兼容存储):

bash
# 二进制方式部署 MinIO(Linux x86_64)
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio && sudo mv minio /usr/local/bin/

# 准备数据目录(生产建议用独立的数据盘,且不要与系统盘共用)
sudo mkdir -p /data/minio

# 启动,控制台端口 9001,API 端口 9000
MINIO_ROOT_USER=admin MINIO_ROOT_PASSWORD='<改成强密码>' \
  minio server /data/minio --console-address ":9001"

用 mc(MinIO Client)做桶管理和同步备份:

bash
# 安装 mc 并配置别名
wget https://dl.min.io/client/mc/release/linux-amd64/mc && chmod +x mc && sudo mv mc /usr/local/bin/
mc alias set local http://127.0.0.1:9000 admin '<密码>'
mc alias set remote https://s3..amazonaws.com  

# 创建桶、设置匿名只读(用于图片外链)
mc mb local/images
mc anonymous set download local/images

# 镜像同步到远端(备份/容灾)
mc mirror --watch local/images remote/backup-bucket

权限模型怎么设计:按部门与角色的目录权限矩阵

结论:文件服务器的权限要按"组"而不是按"人"授权——目录先按部门/项目切分,再用 POSIX 权限或 ACL 给每个组分配读写级别,人员进出只改组成员。 这样 20 人和 200 人的权限维护成本是一样的。

一张典型的权限矩阵如下(rw 为读写,r 为只读,- 为无权限):

共享目录财务组设计组研发组管理层外部协作
/data/share/public读写读写读写读写只读(限时链接)
/data/share/finance读写无权限无权限只读无权限
/data/share/design无权限读写只读只读只读(限时链接)
/data/share/dev无权限无权限读写只读无权限
/data/share/archive只读只读只读读写无权限

落地时遵守三条原则:

  • 默认拒绝:新建目录先给 2770(组外无权限),再按需放开,不要先设 777 再想起来收紧。
  • 只用组授权:人员入职离职只改组成员(gpasswd -a / gpasswd -d),永远不要为某个人单独改目录权限。
  • 权限可继承:目录设 setgid 位(权限首位为 2)+ ACL 的 default 条目,保证新建文件自动继承属组与权限,否则新文件会按创建者的 umask 生成,组权限就断了。

POSIX 权限与 ACL 的取舍标准是:POSIX 只有"属主 / 属组 / 其他人"三组,一个目录最多表达两级差异;当需要对三个以上群体分配不同权限时(如上表的 design 目录),就必须用 ACL。 ACL 的代价是需要用 getfacl 才能看全权限,且备份时必须带 -A 参数,否则迁移后权限会静默丢失。

bash
# 建组与目录骨架
sudo groupadd finance && sudo groupadd design && sudo groupadd dev
sudo mkdir -p /data/share/{public,finance,design,dev,archive}
sudo chgrp finance /data/share/finance && sudo chmod 2770 /data/share/finance

# design 目录:设计组读写、研发组只读、其他人无权限(POSIX 表达不了,改用 ACL)
sudo chgrp design /data/share/design && sudo chmod 2770 /data/share/design
sudo setfacl -m g:design:rwx -m g:dev:rx -m o:--- /data/share/design
# default 条目让新建文件自动继承,缺少它新文件仍按 umask 生成
sudo setfacl -d -m g:design:rwx -m g:dev:rx /data/share/design

# 人员进出只改组成员
sudo gpasswd -a alice design
sudo gpasswd -d bob design

# 查看与校验(ACL 必须用 getfacl 才能看全)
getfacl -p /data/share/design

其中 chmod 2770 的首位 2 是 setgid 位,作用是让该目录下新建的文件自动继承目录的属组而不是创建者的主组。ext4 与 xfs 默认已启用 ACL 支持,可用 mount | grep -w acl 确认;如果输出里没有 acl,需要在 /etc/fstab 的挂载选项里补上再重新挂载。

权限、审计与备份容灾

结论:文件服务器的价值不在于能存多少,而在于"谁能看、谁能改、删了能不能找回"。

权限设计建议:

  • 按部门/项目建组,用组授权而不是给个人授权(Linux 下用 setgid 目录保证新建文件继承组)。
  • 共享目录设 2775(目录)与 0664(文件),避免 777。
  • 对外分享一律用带有效期和提取码的链接,不要直接开匿名访问。
  • 开启审计:Samba 的 full_audit、Linux 的 auditd 都能记录删除与改名操作。
bash
# 用 auditd 监控关键目录的删除与权限变更
sudo apt install -y auditd audispd-plugins
sudo auditctl -w /data/share -p wa -k fileserver_watch
sudo ausearch -k fileserver_watch -i | tail -20

备份策略(本地快照 + 异地同步):

bash
# 1) 本地快照:使用 btrfs/lvm 快照或 rsnapshot 做小时级回滚点
sudo apt install -y rsnapshot
# /etc/rsnapshot.conf 中保留 hourly.0~5、daily.0~6、weekly.0~3

# 2) 异地同步:rsync over ssh,保留硬链接做增量
rsync -aHAX --delete --numeric-ids \
  -e "ssh -p 22" /data/share/ backup@<异地IP>:/backup/share/

# 3) 上云归档:rclone 同步到对象存储
rclone sync /data/share remote:backup-bucket/share \
  --transfers 16 --checkers 32 --log-file=/var/log/rclone.log
bash
# 定时任务:每日 02:00 增量同步,每周日凌晨全量校验
crontab -l | { cat; echo '0 2 * * * /usr/bin/rsync -aHAX --delete /data/share/ backup@<异地IP>:/backup/share/ >> /var/log/rsync.log 2>&1'; } | crontab -