什么是“绑定安装”?我该如何制作一个?到底有什么好处呢?
有人告诉我要使用绑定安装来执行某些操作,但我不明白它是什么或如何使用它。
答案1
什么是绑定安装?
A绑定挂载是目录树的另一种视图。传统上,安装将存储设备的视图创建为目录树。相反,绑定安装采用现有目录树并将其复制到不同的点下。绑定挂载中的目录和文件与原来的相同。一侧的任何修改都会立即反映在另一侧,因为两个视图显示相同的数据。
例如,发出 Linux 命令后 -
mount --bind /some/where /else/where
目录/some/where
和/else/where
具有相同的内容,即 的内容/some/where
。 (如果/else/where
不为空,则其先前的内容现在被隐藏。)
与硬链接或符号链接不同,绑定安装不会影响文件系统上存储的内容。这是实时系统的一个属性。
如何创建绑定安装?
绑定文件系统
这bindfs
文件系统是一个保险丝创建目录树视图的文件系统。例如,命令
bindfs /some/where /else/where
创建/else/where
一个挂载点,在该挂载点下 的内容/some/where
可见。
由于bindfs是一个单独的文件系统,因此文件/some/where/foo
和/else/where/foo
应用程序对应用程序显示为不同的文件(bindfs文件系统有其自己的st_dev
价值)。一侧的任何更改都会“神奇地”反映在另一侧,但只有当人们知道 bindfs 的操作方式时,文件是相同的这一事实才会显而易见。
Bindfs 不知道挂载点,因此如果 下有挂载点/some/where
,它会显示为 下的另一个目录/else/where
。挂载或卸载下面的文件系统/some/where
将显示/else/where
为相应目录的更改。
Bindfs 可以更改某些文件元数据:它可以显示文件的虚假权限和所有权。请参阅手动的有关详细信息,请参阅下面的示例。
bindfs 文件系统可以以非 root 用户身份挂载,您只需要挂载 FUSE 文件系统的权限。根据您的发行版,这可能需要位于fuse
组中或允许所有用户使用。要卸载 FUSE 文件系统,请使用fusermount -u
代替umount
,例如
fusermount -u /else/where
空文件系统
FreeBSD 提供了nullfs
创建文件系统的备用视图的文件系统。以下两个命令是等效的:
mount -t nullfs /some/where /else/where
mount_nullfs /some/where /else/where
发出任一命令后,/else/where
将成为其内容/some/where
可见的安装点。
由于 nullfs 是一个单独的文件系统,因此文件/some/where/foo
和/else/where/foo
应用程序对应用程序显示为不同的文件(nullfs 文件系统有其自己的st_dev
值)。一侧的任何更改都会“神奇地”反映在另一侧,但只有当人们知道 nullfs 的操作方式时,文件是相同的这一事实才显而易见。
与作用于目录树级别的 FUSE bindfs 不同,FreeBSD 的 nullfs 作用于内核更深处,因此下面的挂载点/else/where
不可见:只有/some/where
与 下反映的属于同一挂载点一部分的树/else/where
。
nullfs 文件系统可以在其他 BSD 变体(OS X、OpenBSD、NetBSD)下使用,但它不会被编译为默认系统的一部分。
Linux 绑定挂载
在 Linux 下,绑定挂载可作为内核功能使用。您可以使用以下命令创建一个mount
命令,通过传递--bind
命令行选项或bind
安装选项。以下两个命令是等效的:
mount --bind /some/where /else/where
mount -o bind /some/where /else/where
这里,“设备”/some/where
不是像磁盘文件系统那样的磁盘分区,而是现有的目录。像往常一样,安装点/else/where
必须是现有目录。请注意,无论哪种方式都没有指定文件系统类型:进行绑定挂载不涉及文件系统驱动程序,它从原始挂载复制内核数据结构。
mount --bind
还支持将非目录挂载到非目录上:/some/where
可以是常规文件(在这种情况下/else/where
也需要是常规文件)。
Linux 绑定安装与原始安装几乎没有什么区别。该命令df -T /else/where
显示与 相同的设备和相同的文件系统类型df -T /some/where
。这些文件/some/where/foo
和文件/else/where/foo
无法区分,就好像它们是硬链接一样。可以卸载/some/where
,在这种情况下/else/where
仍保持安装状态。
对于较旧的内核(我不知道确切的时间,我想直到一些 3.x),绑定安装与原始内核确实没有什么区别。最近的内核确实跟踪绑定安装并通过 <code/proc/ 公开信息PID/mountinfo,它允许findmnt
指示绑定安装。
您可以将绑定安装条目放入/etc/fstab
.只需将bind
(或rbind
等)与您想要的任何其他选项一起包含在选项中即可。 “设备”是现有的树。文件系统列可以包含none
or bind
(它被忽略,但使用文件系统名称会令人困惑)。例如:
/some/where /readonly/view none bind,ro
如果下面有挂载点/some/where
,则其内容在下面不可见/else/where
。相反bind
,您rbind
还可以使用下面的复制安装点/some/where
。例如,如果/some/where/mnt
是挂载点那么
mount --rbind /some/where /else/where
相当于
mount --bind /some/where /else/where
mount --bind /some/where/mnt /else/where/mnt
此外,Linux 允许将挂载声明为共享,奴隶,私人的或者不可绑定的。这会影响该安装操作是否反映在复制安装点的绑定安装下。有关更多详细信息,请参阅内核文档。
Linux 还提供了一种移动挂载的方法:--bind
复制、--move
移动挂载点。
两个绑定安装目录中可以有不同的安装选项。然而,有一个怪癖:进行绑定挂载和设置挂载选项不能以原子方式完成,它们必须是两个连续的操作。 (较旧的内核不允许这样做。)例如,以下命令创建一个只读视图,但有一小段时间是/else/where
读写的:
mount --bind /some/where /else/where
mount -o remount,ro,bind /else/where
我无法让绑定安装工作!
如果您的系统不支持 FUSE,实现相同效果的经典技巧是运行 NFS 服务器,使其导出您想要公开的文件(允许访问localhost
)并将它们安装在同一台计算机上。这在内存和性能方面有很大的开销,因此绑定挂载在可用的情况下具有明显的优势(由于 FUSE,这在大多数 Unix 变体上)。
用例
只读视图
创建文件系统的只读视图可能很有用,无论是出于安全原因还是只是作为一个安全层以确保您不会意外修改它。
使用bindfs:
bindfs -r /some/where /mnt/readonly
对于 Linux,简单的方法是:
mount --bind /some/where /mnt/readonly
mount -o remount,ro,bind /mnt/readonly
/mnt/readonly
这会留下一个短暂的读写时间间隔。如果这是一个安全问题,请首先在只有 root 可以访问的目录中创建绑定挂载,将其设置为只读,然后将其移动到公共挂载点。在下面的代码片段中,请注意/root/private
(挂载点上方的目录)是私有的,这一点很重要;原来的权限/root/private/mnt
是无关紧要的,因为它们隐藏在挂载点后面。
mkdir -p /root/private/mnt
chmod 700 /root/private
mount --bind /some/where /root/private/mnt
mount -o remount,ro,bind /root/private/mnt
mount --move /root/private/mnt /mnt/readonly
重新映射用户和组
文件系统通过数字 ID 记录用户和组。有时,您最终会得到多个系统,这些系统将不同的用户 ID 分配给同一个人。这不是网络访问的问题,但当您在磁盘上将数据从一个系统传送到另一个系统时,它会使用户 ID 变得毫无意义。假设您在系统上使用多用户文件系统(例如 ext4、btrfs、zfs、UFS 等)创建了一个磁盘,其中 Alice 的用户 ID 为 1000,Bob 的用户 ID 为 1001,并且您希望使该磁盘可以在一个系统,其中 Alice 的用户 ID 为 1001,Bob 的用户 ID 为 1000。如果直接挂载磁盘,Alice 的文件将显示为 Bob 所有(因为用户 ID 为 1001),Bob 的文件将显示为 Alice 所有(因为用户 ID 为 1000)。
您可以使用bindfs 重新映射用户ID。首先将磁盘分区挂载到私有目录中,只有 root 可以访问它。然后在公共区域中创建一个bindfs视图,并进行用户ID和组ID重新映射,交换Alice和Bob的用户ID和组ID。
mkdir -p /root/private/alice_disk /media/alice_disk
chmod 700 /root/private
mount /dev/sdb1 /root/private/alice_disk
bindfs --map=1000/1001:1001/1000:@1000/1001:@1001/1000 /root/private/alice_disk /media/alice_disk
看如何允许访问非启动系统的用户主文件夹上的文件?和mount --将其他用户绑定为我自己另一个例子。
安装在监狱或容器中
Achroot监狱或者容器在系统目录树的子树中运行进程。这对于运行访问受限的程序很有用,例如运行只能访问其自己的文件及其服务的文件的网络服务器,但不能访问存储在同一计算机上的其他数据。 chroot 的一个限制是程序仅限于一个子树:它无法访问独立的子树。绑定安装允许将其他子树嫁接到该主树上。这使得它们成为 Linux 下容器的大多数实际使用的基础。
例如,假设一台机器运行一个服务/usr/sbin/somethingd
,该服务只能访问/var/lib/something
.包含这两个文件的最小目录树是根目录。如何限制服务?一种可能性是/usr/sbin/somethingd
在/var/lib/something
.但这很麻烦(每次升级文件时都需要更新硬链接),并且如果/var/lib/something
和/usr
位于不同的文件系统上,则不起作用。更好的解决方案是创建一个临时根并使用安装来填充它:
mkdir /run/something
cd /run/something
mkdir -p etc/something lib usr/lib usr/sbin var/lib/something
mount --bind /etc/something etc/something
mount --bind /lib lib
mount --bind /usr/lib usr/lib
mount --bind /usr/sbin usr/sbin
mount --bind /var/lib/something var/lib/something
mount -o remount,ro,bind etc/something
mount -o remount,ro,bind lib
mount -o remount,ro,bind usr/lib
mount -o remount,ro,bind usr/sbin
chroot . /usr/sbin/somethingd &
Linux的挂载命名空间概括 chroot。绑定挂载是如何以灵活的方式填充命名空间的方式。看使进程读取同一文件名的不同文件举个例子。
运行不同的发行版
chroot 的另一个用途是在目录中安装不同的发行版并从中运行程序,即使它们需要位于基本系统上不存在或具有不同内容的硬编码路径中的文件。这可能很有用,例如,在不支持混合软件包的 64 位系统上安装 32 位发行版、安装较旧版本的发行版或其他发行版以测试兼容性、安装较新版本以测试最新功能,同时保持稳定的基础系统等。请参阅如何在 64 位 Debian/Ubuntu 上运行 32 位程序?以 Debian/Ubuntu 为例。
假设您在目录下安装了发行版的最新软件包/f/unstable
,您可以通过使用 切换到该目录来运行程序chroot /f/unstable
。要使主目录从此安装可用,请将它们绑定挂载到 chroot 中:
mount --bind /home /f/unstable/home
该程序施鲁特自动执行此操作。
访问隐藏在安装点后面的文件
当您在目录上安装文件系统时,这会隐藏目录后面的内容。在卸载该目录之前,该目录中的文件将无法访问。由于 BSD nullfs 和 Linux 绑定挂载在比挂载基础设施更低的级别上运行,因此文件系统的 nullfs 挂载或绑定挂载会公开隐藏在原始子挂载后面的目录。
例如,假设您在 处安装了 tmpfs 文件系统/tmp
。如果/tmp
创建 tmpfs 文件系统时存在文件,这些文件可能仍然保留,实际上无法访问,但会占用磁盘空间。跑步
mount --bind / /mnt
(Linux)或
mount -t nullfs / /mnt
(FreeBSD) 在 处创建根文件系统的视图/mnt
。该目录/mnt/tmp
是根文件系统中的目录。
NFS 在不同路径导出
某些 NFS 服务器(例如 NFSv4 之前的 Linux 内核 NFS 服务器)在导出目录时始终通告实际目录位置。也就是说,当客户端请求时server:/requested/location
,服务器将在该位置提供树/requested/location
。有时希望允许客户端请求/request/location
但实际上提供/actual/location
.如果您的 NFS 服务器不支持提供备用位置,您可以为预期请求创建绑定安装,例如
/requested/location *.localdomain(rw,async)
以及/etc/exports
以下内容/etc/fstab
:
/actual/location /requested/location bind bind
符号链接的替代品
有时您想创建符号链接以使文件/some/where/is/my/file
出现在 下/else/where
,但使用的应用程序file
会扩展符号链接并拒绝/some/where/is/my/file
。绑定挂载可以解决这个问题:bind-mount /some/where/is/my
to /else/where/is/my
,然后realpath
将报告/else/where/is/my/file
为下/else/where
,不为下/some/where
。
绑定安装的副作用
递归目录遍历
如果您使用绑定挂载,则需要处理递归遍历文件系统树的应用程序,例如备份和索引(例如构建一个定位数据库)。
通常,应该从递归目录遍历中排除绑定挂载,以便每个目录树仅在原始位置遍历一次。如果可能的话,使用bindfs和nullfs,配置遍历工具以忽略这些文件系统类型。 Linux 绑定安装无法这样识别:新位置与原始位置相同。使用 Linux 绑定挂载,或者使用只能排除路径而不能排除文件系统类型的工具,您需要排除绑定挂载的挂载点。
在文件系统边界停止的遍历(例如find -xdev
,rsync -x
,du -x
,...)在遇到bindfs 或nullfs 挂载点时将自动停止,因为该挂载点是不同的文件系统。对于 Linux 绑定挂载,情况有点复杂:仅当绑定挂载嫁接不同的文件系统时才存在文件系统边界,而不是嫁接同一文件系统的另一部分。
超越绑定安装
绑定安装提供不同位置的目录树视图。它们公开相同的文件,可能具有不同的挂载选项和(使用bindfs)不同的所有权和权限。呈现目录树更改视图的文件系统称为覆盖文件系统或者可堆叠文件系统。还有许多其他覆盖文件系统可以执行更高级的转换。以下是一些常见的。如果此处未涵盖您所需的用例,请检查FUSE 文件系统存储库。
- 记录文件系统— 记录所有文件系统访问以用于调试或监视目的(配置文件语法,是否可以找出哪个程序或脚本创建了给定文件?,列出程序访问的文件)
过滤可见文件
蛤蜊— 读取文件时通过病毒扫描程序运行文件
过滤器— 隐藏文件系统的一部分
罗夫斯— 只读视图。与 类似
bindfs -r
,只是更轻量一些。联盟坐骑— 存在多个文件系统(称为分支机构) 在单个目录下:如果
tree1
包含foo
和tree2
containsbar
那么它们的联合视图同时包含foo
和bar
。新文件被写入特定分支,或根据更复杂的规则选择的分支。这个概念有多种实现方式,包括:
修改文件名和元数据
- CIOPFS— 不区分大小写的文件名(对于挂载 Windows 文件系统很有用)
- 转换文件系统— 在字符集之间转换文件名 (例子)
- 波西绍夫尔— 将 Unix 文件名和其他元数据(权限、所有权等)存储在更受限制的文件系统上,例如 VFAT(例子)
查看更改后的文件内容
- AVFS— 对于每个存档文件,提供一个包含存档内容的目录(例子,更多例子)。还有很多将特定档案公开为目录的 FUSE 文件系统。
- 熔丝— 读取文件时通过管道运行文件,例如重新编码文本文件或媒体文件(例子)
- 利佐普夫斯— 压缩文件的透明解压
- mp3fs— 读取 FLAC 文件时将其转码为 MP3 (例子)
- 脚本文件系统— 执行脚本来提供内容(一种本地 CGI)(例子)
修改内容存储方式
答案2
我发现以下解释非常有帮助,因为它不会产生实际复制/重复数据的概念:
绑定挂载可以被认为是文件系统级别的一种符号链接。使用
mount --bind
,可以为现有文件系统创建第二个挂载点,使该文件系统在命名空间中的不同位置可见。因此,绑定挂载对于创建文件系统名称空间的特定视图非常有用;例如,可以创建一个绑定挂载,使文件系统的一部分在环境中可见,否则该环境会被
chroot()
.
答案3
很简单,当您使用绑定挂载时,主机上的文件或目录将挂载到容器中,因此主机上文件目录内所做的任何更改都将自动在目录上的容器内可用。