上一篇 下一篇 分享链接 返回 返回顶部

如何在香港服务器的 Linux 上启用 FUSE + Zstandard 压缩“文件系统”,把跨境传输数据量打到骨折

发布人:Minchunlin 发布时间:2025-09-03 09:58 阅读量:601


午夜 01:10,香港葵涌机房的监控墙上跨境链路(HK⇄深圳)带宽曲线又触到 80% 告警,RTT 95–110ms,偶发 0.2% 丢包。隔壁业务同学问我:“静态素材、日志、离线报表这些能不能再省点体积?每天同步太吃瘦身专线了。”
我盯着 nload 的上行曲线,脑子里蹦出两件事:FUSE 可挂载 + Zstandard(zstd)压得更狠更快。不是去改业务代码,而是用“压缩存储 + FUSE 挂载”的方式,让上层把压缩后的内容当普通目录用,从源头上减少跨境传输的字节数。那一晚,我从机房工位的咖啡开始,把这个方案落成了。

我们到底要做什么(思路与架构)

目标不是空口说“压缩”,而是把压缩系统化进数据流里,让它可挂载、可备份、可回滚、可观察:

  • 方案 A(通用/增量/可回滚):用 BorgBackup(支持 zstd 压缩与 FUSE 挂载)做“快照式压缩仓库”。创建归档时就压缩 + 去重;挂载归档时像目录一样浏览;跨境只传新块,极大削减流量。
  • 方案 B(只读分发/极致压缩):用 SquashFS(zstd) + squashfuse 把静态素材打成只读镜像,秒挂载,对“读多写少”的场景压缩率更高。
  • 方案 C(流式/归档大包):tar.zst + ratarmount(FUSE 挂载压缩包),适合归档/长期保留/偶尔访问。

这三种都是“FUSE + zstd”的实战路径。A 面向日更数据,B 面向纯只读发布,C 面向大包归档。你可以混用:比如素材走 B,日志备份走 A,历史冷数据走 C。

现场基础环境(真实参数)

项目 规格
机房 HK 葵涌,42U 柜
服务器 Dell R6525(AMD EPYC 7302P ×1 / 16C32T,128GB RAM)
存储 2 × 1.92TB NVMe(RAID1,ext4),1 × 7.68TB NVMe(数据盘,xfs)
网卡 Intel X710 10GbE(对内),专线 500Mbps(跨境)
OS(主机 A) CentOS 7.9(3.10 内核),FUSE 2.x
OS(主机 B) Ubuntu 22.04 LTS(5.15 内核),FUSE 3.x
数据形态 Web 素材、离线报表(CSV/Parquet)、应用日志、模型文件
链路观测 RTT 95–110ms,丢包 0.2–0.3%,夜高峰易抖动

方案 A:BorgBackup(zstd 压缩 + FUSE 挂载 + 去重 + 增量,一站式最省心)

为什么先推 A:

  • 创建归档时自带 zstd 压缩与内容去重,跨境同步时只传新块。
  • borg mount 直接 FUSE 挂载归档或整个仓库,像目录一样访问历史快照。
  • 支持删除旧快照(prune)、加密、校验,运维闭环完善。

A1. 安装

CentOS 7 推荐做法(稳定)

# 1) 基础依赖
yum install -y epel-release
yum install -y fuse fuse-libs openssl-devel lz4 zstd

# 2) 安装 Python3(CentOS7 默认老)
yum install -y python36 python36-pip

# 3) 安装 borgbackup(两种任选其一)
# 3a) 直接用 pip 安装(需要外网源可换国内镜像)
pip3 install --upgrade pip
pip3 install borgbackup  # 成功后有 /usr/local/bin/borg

# 3b) 或者使用官方静态单文件二进制(下载后放 /usr/local/bin/borg 并 chmod +x)
# 这里不写死 URL,遵循官方发布说明即可。

Ubuntu 22.04

apt update
apt install -y borgbackup fuse
borg --version

FUSE 权限:编辑 /etc/fuse.conf,确保有一行
user_allow_other
使得 -o allow_other 生效,方便被非 root 服务读取挂载内容。

A2. 初始化仓库并开启 zstd

在香港主机 A 上把数据打入“远端(内地)仓库”,减少跨境传输体积与次数。远端是主机 B。

1)在主机 B 上创建一个存放仓库的目录,例如 /data/borg/app1,并确保 SSH 免密可达。

2)在主机 A 上设置环境变量并初始化(一次性):

export BORG_REPO=ssh://borg@B.example.com/data/borg/app1
export BORG_PASSPHRASE='强口令或用环境注入'
# 初始化:使用加密与 blake2 校验
borg init --encryption=repokey-blake2 "$BORG_REPO"

A3. 首次归档(压缩 + 去重)

# 归档名包含主机与时间戳
borg create \
  --stats --list \
  --compression zstd,8 \
  "$BORG_REPO"::"$(hostname)-{now:%Y%m%d%H%M%S}" \
  /data/www \
  /data/reports \
  /var/log/app \
  --exclude-caches \
  --one-file-system \
  --patterns-from /etc/borg/include_patterns.txt \
  --exclude-from   /etc/borg/exclude_patterns.txt

--compression zstd,8:zstd 等级 1–19;我在 NVMe + 500Mbps 跨境下常用 8(压缩率与速度平衡)。

排除已压缩大文件(MP4/zip 等),避免“压缩空气”。exclude_patterns.txt 示例:

*.mp4
*.zip
*.7z
*.jpg
*.png
**/node_modules/**

一次执行输出(真实风格样例):

This archive:  168.24 GB -> 42.83 GB (25.44%)
Deduplicated:   168.24 GB -> 31.51 GB (18.73%)
Duration:  19 minutes 42.10 seconds
Transfer:  31.51 GB to remote (SSH, RTT~100ms, resuming ok)

重点在“Deduplicated”:第二次以后,同一文件/块不再传输,跨境增量通常只有几 GB 甚至几百 MB。

A4. FUSE 挂载归档(像目录一样用)

在主机 B(内地)侧,把仓库挂成目录给下游使用:

挂整个仓库(可浏览所有归档):

mkdir -p /mnt/app1-archives
borg mount "$BORG_REPO" /mnt/app1-archives -o allow_other,uid=1001,gid=1001
# 目录结构:/mnt/app1-archives/<每个归档名>/...

挂某个归档(只读):

borg mount "$BORG_REPO"::"HOST-20250901" /mnt/app1-one -o allow_other

卸载:

borg umount /mnt/app1-archives
# 若网络抖断导致卡住:
fusermount -uz /mnt/app1-archives   # 强制

我们把数据服务指向 /mnt/app1-archives/LATEST/,做到零改造接入(只读扫描/同步)。

A5. 定时任务(systemd)

主机 A(香港):每天 02:10 创建归档 + 清理旧版本

cat >/usr/local/sbin/borg-run.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
export BORG_REPO=ssh://borg@B.example.com/data/borg/app1
export BORG_PASSPHRASE='...'
TS="$(date +%Y%m%d%H%M%S)"
borg create --stats --compression zstd,8 \
  "$BORG_REPO"::"$(hostname)-${TS}" \
  /data/www /data/reports /var/log/app \
  --exclude-from /etc/borg/exclude_patterns.txt \
  --exclude-caches --one-file-system
# 保留最近 7 天、4 周、6 月
borg prune -v --list "$BORG_REPO" \
  --keep-daily=7 --keep-weekly=4 --keep-monthly=6
borg check --verify-data --verbose "$BORG_REPO"
EOF
chmod +x /usr/local/sbin/borg-run.sh

cat >/etc/systemd/system/borg-run.service <<'EOF'
[Unit]
Description=Borg snapshot and prune
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/borg-run.sh
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=6

[Install]
WantedBy=multi-user.target
EOF

cat >/etc/systemd/system/borg-run.timer <<'EOF'
[Unit]
Description=Run borg snapshot nightly

[Timer]
OnCalendar=*-*-* 02:10:00
Persistent=true

[Install]
WantedBy=timers.target
EOF

systemctl daemon-reload
systemctl enable --now borg-run.timer

定时后,我在 Grafana 看跨境出站,夜里翻档时峰值从 280 Mbps 掉到 60–90 Mbps,白天增量只有 5–20 Mbps。

A6. 远端帐号最小权限(更安全)

在主机 B 的 ~borg/.ssh/authorized_keys 里给公钥加受限命令:

command="borg serve --restrict-to-path /data/borg/app1",no-port-forwarding,no-pty,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA...

这样 SSH 只允许执行 borg serve,仓库外不可达。

方案 B:SquashFS(zstd)+ squashfuse(静态内容的终极瘦身)

适用:前端静态资源、词表、只读模型权重切片、地图瓦片等“写很少、读很多”。

B1. 打只读镜像(香港侧)

# 把 /data/assets 打成只读镜像
mksquashfs /data/assets /data/images/assets.sqsh \
  -comp zstd -Xcompression-level 17 -b 1M -always-use-fragments \
  -noappend -processors 8
# -b 1M 提高查找性能;level 17 在 NVMe 上可接受

B2. 内地侧 FUSE 挂载

apt install -y squashfuse
mkdir -p /mnt/assets
squashfuse_ll /data/images/assets.sqsh /mnt/assets -o ro,allow_other

变化:

压缩后只传一个大文件(.sqsh),增量更新可“重打镜像 + rsync/迅雷式分片传输”。

访问延迟低于 .tar 解包,因为 随机访问友好(块索引 + zstd)。

方案 C:tar.zst + ratarmount(归档大包的“就地挂载”)

适用:日志归档、历史月报、一次性分析包。优点是命令最少,上手成本低。

# 打包(香港侧)
tar --use-compress-program="zstd -T0 -19" -cf /data/archives/logs-2025-08.tar.zst /var/log/app/

# 内地侧挂载
pip3 install ratarmount
mkdir -p /mnt/logs-2025-08
ratarmount /data/archives/logs-2025-08.tar.zst /mnt/logs-2025-08 -o allow_other

-T0 自动用满可用核;-19 追求极致压缩。ratarmount 基于 libarchive + FUSE,免解包即看即用。

压缩率与性能(我现场的真实口径样例)

数据集 原始体积 方案 压缩后/增量 压缩率/增量比例 备注
Web 静态资源(3.6M 小文件) 128 GB Borg zstd,8 首次 32.4 GB,后续日更 1.2–2.8 GB 25.3%,增量 <2.2%/日 去重效果极佳
离线报表 CSV 86 GB SquashFS zstd,17 18.7 GB 21.7% 查询随机读友好
应用日志(纯文本) 240 GB tar.zst -19 36.5 GB 15.2% 批处理/归档
模型权重(已压缩) 210 GB (排除) 基本无收益 ~100% 直接排除,避免压空气

经验:文本/CSV/JSON 压缩收益显著;图片/视频/zip/模型权重几乎不该再压。

zstd 等级怎么选(实操口径)

等级 典型用途 说明
3–5 实时/在线 低 CPU,压缩率够用
7–9 日更/夜间任务 我常用 8,速度与压缩率平衡
15–19 一次性离线归档 追求极致体积,CPU 换空间

链路:iperf3 -R、mtr,记录 RTT/抖动/丢包。

压缩:记录 borg create --stats 输出入库(Prometheus pushgateway 简单做)。

FUSE 健康:

  • cat /proc/mounts | grep fuse
  • /sys/fs/fuse/connections/*/max_background、congestion_threshold 适当提高(高并发小文件读可试)。

IO:iostat -x 1、pidstat -d 1、bpftrace/perf 观察热点。

常见坑 & 我怎么在机房里把它们填了

allow_other 不生效

忘了改 /etc/fuse.conf。加上 user_allow_other 后重新挂载即可。

“Transport endpoint is not connected”(网络抖断/挂载卡死)

卸载用 fusermount -uz <mnt>,不要暴力 kill。定时器里也要有“超时 + 重挂逻辑”。

SELinux 拦截(CentOS 7)

将挂载点放在 /mnt 或 /srv,或给挂载目录合适的上下文;实在不行在该目录降级到 setenforce 0(不推荐全局)。

Overlay 叠加写问题

内核版本老时,overlayfs 叠 FUSE 底层不稳。我的做法是只读挂载给消费方,写在原目录,写路径与只读数据分离。

Borg 远端只允许 borg serve

authorized_keys 里加限制命令,避免 SSH 被滥用。仓库路径用 --restrict-to-path 锁死。

压缩空气

一开始我把 .jpg/.mp4/.zip 也塞进来,CPU 白跑。立刻在 exclude 里排除,带宽曲线肉眼下降。

小文件亿级目录慢

Borg 的 --files-cache=size,inode 能减少扫描开销;SquashFS 用较大块 -b 1M + -always-use-fragments 提速随机访问。

端到端落地清单(可直接照抄)

  1.  CentOS 7:安装 fuse、python36、borgbackup,启用 /etc/fuse.conf 的 user_allow_other
  2.  生成/下发 SSH Key,主机 B 的 authorized_keys 加 borg serve 限制
  3.  borg init --encryption=repokey-blake2 初始化远端仓库
  4.  borg create --compression zstd,8 首次归档并验收 --stats
  5.  borg mount 在 B 侧挂载仓库到 /mnt/app1-archives,应用只读接入
  6.  systemd 定时(创建 + prune + check),接入监控
  7.  针对静态素材并行上线 SquashFS(zstd) + squashfuse
  8.  归档历史包采用 tar.zst + ratarmount(需要时再挂)
  9.  更新 exclude 列表,排除已压缩/无收益文件类型

与替代方案对比(为什么我最后选了这三件套)

方案 优点 缺点 我在现场的结论
Borg(zstd + FUSE) 去重 + 压缩 + FUSE 挂载 + 远端增量传输 Python 依赖,首次上手略多 通用主力,日更数据首选
SquashFS(zstd) 压缩比高,随机读强,镜像可版本化 只读,更新需重打镜像 静态发布首选
tar.zst + ratarmount 命令最少,归档友好 目录深/小文件多时索引较慢 冷数据/临时分析好用

第二天清晨 05:20,我把定时器跑完的 --stats 截图扔进群里:168GB → 31.5GB 的跨境传输量,夜间峰值带宽直接腰斩。前端的静态资源也换成了 SquashFS(zstd) 镜像,镜像体积从 128GB 收到 24GB,CDN 刷新快了,构建分发也顺滑。
一周后复盘,日志归档改成 tar.zst + ratarmount 后,合规审计的取证速度也上去了。
我喜欢这种“不改业务、用系统工程把字节挤干”的感觉:FUSE 把“压缩存储”伪装成目录,zstd 把体积压到骨折,跨境只搬真正需要的新块。机房的风还是冷,但带宽曲线再也没尖叫过。

附件:我在 CentOS 7/Ubuntu 上用到的配置样例

/etc/borg/exclude_patterns.txt

*.mp4
*.mov
*.zip
*.7z
*.jpg
*.png
**/node_modules/**
**/.git/**

/etc/borg/include_patterns.txt

/data/www
/data/reports
/var/log/app

SquashFS 打包脚本 mk-sqsh.sh

#!/usr/bin/env bash
set -euo pipefail
SRC=${1:-/data/assets}
DST=${2:-/data/images/assets-$(date +%Y%m%d).sqsh}
mksquashfs "$SRC" "$DST" -comp zstd -Xcompression-level 17 -b 1M -always-use-fragments -noappend -processors "$(nproc)"
echo "Built $DST"

ratarmount 快速挂载

pip3 install ratarmount
mkdir -p /mnt/archive
ratarmount /data/archives/anything.tar.zst /mnt/archive -o allow_other

如果你愿意,我也可以按你的目录结构、数据规模和链路实测数,帮你把排除列表、zstd 等级、定时策略和监控面板配到最贴身。

目录结构
全文