一份手记,记录我在使用 rclone、FTP、SMB 和 Docker 时踩过的坑与解决方案。
最近给公司搭建共享文件服务,遇到一大堆坑。记录一下,查了一堆办法,没有完美解决问题。mac上无法读写ftp,所以想着把ftp挂载到目录,然后把目录用samba共享出去。几个服务都在docker环境中运行。
1. Linux 挂载 FTP 的传统方式
1.1 为什么 mount 不能直接挂载 FTP?
mount 命令是为内核支持的文件系统(如 ext4、NFS、CIFS)设计的,而 FTP 是一个应用层协议,并非文件系统。因此,执行 mount -t ftp ... 会直接报错,也无法在 /etc/fstab 中直接配置。
1.2 curlftpfs:基于 FUSE 的挂载工具
curlftpfs 利用 FUSE(用户态文件系统)将 FTP 映射为本地目录,用法简单:
# 安装(Ubuntu/Debian)
sudo apt install curlftpfs
# 挂载(带用户名密码)
curlftpfs ftp://user:pass@host /mnt/ftp
# 推荐使用 .netrc 安全存储凭据
echo “machine host login user password pass” > ~/.netrc
chmod 600 ~/.netrc
curlftpfs ftp://host /mnt/ftp
限制与风险: - 只读为主:写入不稳定,易出错。 - 项目已停止维护(2008 年后无更新),不推荐生产环境。 - 密码明文传输(FTP 本身不安全)。
2. rclone:现代化的全能云存储客户端
2.1 为什么选择 rclone?
| 特性 | curlftpfs | rclone |
|---|---|---|
| 维护状态 | 停滞 | 活跃更新 |
| 性能 | 较差 | 优秀 |
| 写入支持 | 不稳定 | 支持(需 VFS 缓存) |
| 协议支持 | FTP 基本 | FTP/SFTP/FTPS/HTTP/云存储等 |
| 高级功能 | 无 | 加密、校验、带宽限制 |
2.2 使用 rclone 挂载 FTP
第一步:配置远程存储
rclone config
# 交互式创建 new remote,选择 ftp,填写主机、用户名、密码等
第二步:挂载到本地
mkdir -p ~/ftp_mount
rclone mount myftp: /home/user/ftp_mount \
--daemon \
--vfs-cache-mode writes \
--allow-other
常用参数说明:
- --daemon:后台运行
- --vfs-cache-mode writes/full:启用缓存,保证写入行为
- --allow-other:允许其他用户访问挂载点
- --dir-cache-time 5m:目录缓存时间,提升响应速度
卸载:
fusermount -u /home/user/ftp_mount
3. 在 Docker 容器中挂载并共享给宿主机
3.1 核心原理:挂载传播(Mount Propagation)
Docker 容器拥有独立的挂载命名空间,默认容器内的挂载对宿主机不可见。通过 绑定挂载(bind mount)+ 共享传播(shared propagation),可以将容器内的挂载点“投射”到宿主机。
3.2 实战:rclone 容器挂载 FTP 并共享
启动 rclone 容器:
docker run -d \
--name rclone-ftp \
--restart unless-stopped \
--device /dev/fuse \
--cap-add SYS_ADMIN \
--security-opt apparmor:unconfined \
-v /share/mnt:/data:shared \
rclone/rclone:latest \
mount remote_name:path /data --allow-other --vfs-cache-mode writes
关键点:
- --device /dev/fuse:让容器能使用 FUSE。
- --cap-add SYS_ADMIN:允许修改挂载命名空间。
- -v /share/mnt:/data:shared::shared 是关键,表示该绑定挂载启用共享传播。
- --allow-other:允许宿主机用户访问。
此时,宿主机上的 /share/mnt 目录即可看到远程 FTP 内容,并且可以读写(取决于权限)。
3.3 常见错误:directory already mounted, use --allow-non-empty
原因:目标目录非空(可能宿主机目录有文件,或有残留挂载记录)。
解决方案:
- 快速修复:在
rclone mount命令中添加--allow-non-empty。 - 彻底清理:
- 检查容器内是否有残留挂载:
docker exec rclone-ftp fusermount -uz /data - 确保宿主机
/share/mnt为空目录。
- 检查容器内是否有残留挂载:
建议:保持挂载点绝对干净,避免因非空导致的数据写入歧义。
4. 容器停止后挂载点无法卸载:Transport endpoint is not connected
4.1 问题现象
容器停止后,宿主机上的 /share/mnt 目录无法访问,执行 ls 或 umount 报错:Transport endpoint is not connected。
4.2 原因分析
后端连接(FTP 会话)已断开,但内核的挂载点仍保留,处于“僵尸”状态。
4.3 应急处理流程
按顺序尝试以下命令(在宿主机上执行):
- 首选:
sudo fusermount -uz /share/mnt(针对 FUSE 的强力卸载) - 若无效:
sudo umount -l /share/mnt(懒惰卸载,立即从树中移除) - 若仍失败:
sudo systemctl restart docker(重启 Docker 守护进程释放引用) - 最后手段:
docker rm -f rclone-ftp(强制删除容器)
4.4 预防措施:优雅停止
在容器停止前主动卸载,避免残留。利用 rclone 的 RC(远程控制)API:
- 启动时加上
--rc参数。 - 在停止钩子中调用
rclone rc/unmount。
例如在 systemd 服务中定义 ExecStop,或在容器编排工具的 pre-stop 钩子中实现。
5. 通过 SMB 共享 rclone 挂载目录
5.1 问题描述
将宿主机的 /share 目录(包含 /share/mnt rclone 挂载点)通过 Samba 容器共享给 Mac 的 Finder。结果:
- /share 下的普通目录读写删除正常。
- 但 /share/mnt 内的文件删除失败,报 Operation not supported。
5.2 根因分析
这是一个多层协议转换问题,数据流为:
Mac (SMB Client) → Samba 容器 → 宿主机 /share/mnt → rclone 容器 → FTP Server
删除操作失败的原因可能出自任意一层,但线索是只有 /share/mnt 失败,说明问题不在 Samba 层,而在于 rclone → FTP 后端 对删除语义的支持或权限配置。
5.3 排查与解决清单
| 排查方向 | 操作/检查项 |
|---|---|
| 权限映射 | 确保 Samba 容器与 rclone 容器使用相同的 UID/GID;rclone 挂载加 --allow-other;Samba 可设 force user = root 测试。 |
| rclone 挂载参数 | 添加 --vfs-cache-mode writes 或 full,加 --umask 000 放宽权限。 |
| FTP 服务器权限 | 用 lftp 或 ftp 命令直接登录 FTP,手动执行 delete 看是否成功。检查 FTP 日志。 |
| SELinux/AppArmor | 临时 setenforce 0 测试,若有效则调整策略。 |
| Samba VFS 模块 | 在 smb.conf 中注释 vfs objects = recycle 等模块,看是否干扰。 |
| Mac Finder 行为 | Finder 删除时可能先移动到 .Trash,涉及重命名,若 FTP 不支持重命名则失败。可用 smbclient 命令行测试,排除 Finder 特性。 |
| rclone 版本 | 升级到最新版,修复已知 FTP 删除 bug。 |
5.4 最佳实践建议
- 优先使用 SFTP 替代 FTP:SFTP 是加密协议,语义更完整,兼容性更好。
- 避免多层嵌套挂载:尽量将远程存储直接挂载到 Samba 共享的根目录,减少中间层。
- 启用详细日志:在 rclone 挂载时加
-vv,Samba 日志级别调高,便于定位。
6. 总结与备忘
| 需求场景 | 推荐方案 |
|---|---|
| 临时挂载 FTP,只读访问 | curlftpfs(简单)或 rclone mount(更稳) |
| 需写入、长期使用、支持多种后端 | rclone 是首选 |
| 在 Docker 中挂载并共享给宿主机 | 使用 :shared 传播模式 + --allow-other |
| 容器停止后清理挂载点 | fusermount -uz 优先 |
| 通过 SMB 共享 rclone 挂载目录 | 确保权限一致,开启缓存,排查 FTP 服务器是否支持删除 |
一句话口诀:
挂载用 rclone,容器加 shared,停止前先卸载,SMB 查权限。
用到的相关命令
# rclone 挂载
rclone mount remote:path /local/mount --daemon --allow-other --vfs-cache-mode writes
# 卸载 FUSE 挂载
fusermount -uz /local/mount
# Docker 运行 rclone 容器(共享挂载)
docker run -d –device /dev/fuse –cap-add SYS_ADMIN -v /host/path:/container/path:shared rclone/rclone mount remote:path /container/path –allow-other
# 强制卸载(懒卸载)
sudo umount -l /host/path
# 查看挂载点占用进程
lsof /host/path
fuser -v /host/path
