同一台 Ubuntu 上有两块磁盘,A 盘已经挂载并且装满数据,现在希望把 A 盘里的某个目录「变」到 B 盘某个路径下去访问——写进去的东西实际落在 A 盘。这种需求在扩容、迁移、给应用换数据盘时非常常见,标准做法是 bind mount(绑定挂载),而不是复制文件或做软链接。
本文把三种方案的适用边界讲清楚,然后给出 bind mount 的临时挂载、开机自动挂载(fstab)、systemd 单元三种落地方式,并附上排错与踩坑清单。
一、先分清三种需求,别一上来就 bind mount
很多人把「挂目录」和「挂磁盘」混在一起,结果方案选错。先对号入座:
关键区别:bind mount 让两个路径指向同一个 inode,磁盘占用不会翻倍;软链接只是一个指针,某些程序(Docker、chroot、部分备份/同步工具、需要真实目录遍历的软件)会解析失败或跳过。所以只要涉及容器、服务端程序的数据目录、或者后续要迁移到别的机器,直接选 bind mount。

二、动手前的环境确认
假设场景如下,后文都用这套路径:
数据源:
/data/photos,位于磁盘 A(挂载点/data,设备/dev/sdb1)目标路径:
/srv/app/photos,位于磁盘 B(挂载点/srv,设备/dev/sdc1)
依次确认磁盘、挂载点与 UUID:
# 看块设备与文件系统
lsblk -f
# 看当前挂载关系(比 mount 输出更好读)
findmnt -t ext4,xfs,btrfs
# 看磁盘占用,确认目标盘还有空间放元数据与索引
df -hT /data /srv
# 记录 UUID,后面写 fstab 要用 UUID 而不是 /dev/sdX(设备名会变)
sudo blkid /dev/sdb1 /dev/sdc1三、方案一:临时 bind mount(验证用,重启失效)
先临时挂一次,确认业务跑通,再考虑持久化。这一步不会修改任何配置文件,随时 umount 即可回滚。

# 1. 建目标目录(-p 保证父目录存在)
sudo mkdir -p /srv/app/photos
# 2. 绑定挂载
sudo mount --bind /data/photos /srv/app/photos
# 3. 验证:这里应该出现 /data 的设备,而不是 /srv 的
findmnt /srv/app/photos
df -h /srv/app/photos
mount | grep photos验证通过的标志是 df -h /srv/app/photos 显示的设备是 /dev/sdb1(也就是磁盘 A),说明目标路径已经在读磁盘 A 的数据了。
如果源目录下还有子挂载(比如 /data/photos/2026 本身又是一个独立挂载点),普通 --bind 不会带过去,需要用递归绑定:
sudo mount --rbind /data/photos /srv/app/photos需要只读映射(比如给审计账号、备份进程看,不允许写)时:
# 先 bind,再 remount 成只读,顺序不能反
sudo mount --bind /data/photos /srv/app/photos
sudo mount -o remount,ro,bind /srv/app/photos卸载:
sudo umount /srv/app/photos四、方案二:写入 /etc/fstab 实现开机自动挂载(推荐)
临时挂载重启就没了,生产环境要写进 fstab。bind mount 的 fstab 行格式是「源目录 → 目标目录 → 文件系统类型 none → 选项 bind」。
# 先备份,改坏 fstab 会导致系统进 emergency mode
sudo cp /etc/fstab /etc/fstab.bak.$(date +%F)
# 追加两行:先保证源盘挂载,再做 bind
sudo tee -a /etc/fstab <<'EOF'
UUID=1111-AAAA /data ext4 defaults,nofail 0 2
/data/photos /srv/app/photos none bind,nofail,x-systemd.requires-mounts-for=/data 0 0
EOF
# 校验语法并实际执行一次
sudo mount -a
findmnt /srv/app/photos
几个必须理解的点:
用 UUID 而不是 /dev/sdX。设备名在插拔硬盘、改 BIOS 顺序后会漂移,UUID 不会。
bind 行要能等到源盘挂载完成。
x-systemd.requires-mounts-for=/data告诉 systemd:/data 没挂好之前不要执行这一条(systemd 244+)。如果系统版本较老,退而求其次用x-systemd.after=,或者干脆把源盘用defaults写在前面并依赖默认顺序。加
nofail。源盘故障或未插上时,不至于让系统卡在启动阶段进不去。bind 行不能自带
defaults之外的复杂选项。绑定挂载会复用源挂载的选项,写uid=、umask=之类基本无效(只读除外,用 remount 或ro,bind)。改完 fstab 建议
sudo systemctl daemon-reload,因为 systemd 会把 fstab 翻译成 mount unit,不 reload 可能读到旧配置。
如果目标目录所在的磁盘 B 尚未格式化/未挂载,那本质上是「挂分区」而不是「挂目录」,fstab 里直接写分区行的 UUID 指向目标路径即可,不需要 bind。
五、方案三:用 systemd mount unit 精细控制顺序
当依赖关系复杂(比如 bind 目标要等 Docker、等网络存储、等某个脚本准备好),fstab 的隐式顺序不够用,改用原生 systemd 单元更可控。fstab 的一行等价于一个 .mount 单元,命名规则是「把路径里的 / 换成 -」。
# 目标 /srv/app/photos → 单元名 srv-app-photos.mount
sudo tee /etc/systemd/system/srv-app-photos.mount <<'EOF'
[Unit]
Description=Bind /data/photos to /srv/app/photos
RequiresMountsFor=/data
After=data.mount
[Mount]
What=/data/photos
Where=/srv/app/photos
Type=none
Options=bind
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now srv-app-photos.mount
systemctl status srv-app-photos.mountRequiresMountsFor= 会自动把「/data 的挂载单元」加进依赖,比手写 After= 更稳。注意单元文件名必须和挂载路径严格对应,否则 systemd 会报「Where 与单元名不匹配」。
六、权限与安全上下文
bind mount 不复制数据,也不改变属主与权限,目标路径看到的权限就是源目录的权限。这带来两个常见误解:
以为要 chown 目标目录:改目标路径的属主实际上改的是源目录的 inode,会连带影响原路径。要改就明确知道自己在改源数据。
跨文件系统类型时权限「变了」:如果源盘是 NTFS/exFAT(
ntfs3、exfat),挂载时本身就带uid=/umask=,表现出来就是所有文件同一个属主。这不是 bind 的问题,是源文件系统不支持 POSIX 权限。
另外两个容易踩的点:
AppArmor:Ubuntu 默认启用,服务进程的 profile 里写的是原始路径时,通过新路径访问可能被拒。需要给 profile 增加新路径或改用源路径。排查看
sudo dmesg | grep -i apparmor。Docker / 容器:容器里要看到 bind mount,宿主机这一层要先把挂载传播打开(
mount --make-shared或在 fstab 用bind,rslave),否则容器内是空的。更简单的做法是启动容器时直接把源目录-v /data/photos:/app/photos挂进去,不要依赖宿主机的 bind 结果。
七、卸载与排错
「target is busy」几乎是必踩的坑,含义是还有进程占用该挂载点(包括 shell 的当前工作目录)。
# 谁在占用
sudo fuser -vm /srv/app/photos
sudo lsof +D /srv/app/photos
# 退出占用进程后再卸
sudo umount /srv/app/photos
# 实在无法定位占用(如 NFS/僵尸句柄),惰性卸载:先解除挂载,引用释放后自动清理
sudo umount -l /srv/app/photos
八、落地检查清单
确认需求是「挂目录」而非「挂整块盘」,前者用 bind,后者用 UUID 挂分区。
目标目录已创建并确认为空;如需保留原内容先备份,不做「先放数据再挂载」。
源盘已挂载,且在 fstab 中位于 bind 行之前。
fstab 用 UUID、带
nofail,bind 行带x-systemd.requires-mounts-for。sudo mount -a无报错,findmnt与df显示的设备是源盘。重启一次做真实演练,确认开机后
systemctl status显示挂载单元 active。确认应用通过新路径能读写,日志无 permission denied / apparmor denied。
记录本次改动(fstab 备份路径、新增单元名)到运维文档,便于回滚。
总结一句:bind mount 解决的是「同一份数据、多个访问路径」,它便宜、无损、可回滚;代价是隐式的依赖顺序和权限共用。把 fstab 的依赖写清楚、把备份留好,这件事就只剩两条命令。
Ubuntu 把一块磁盘的目录挂载到另一块磁盘的目录下:bind mount 完整实践
https://www.hcjike.com/archives/Ub3ntBnd9x