我可以在 Docker Alpine 容器中使用 "mount" 吗?

Can I use "mount" inside a Docker Alpine container?

我正在 Docker做一个旧项目。项目中的一个功能拉入了 user-specified Git repos,由于 repo 的大小可能会导致文件系统不堪重负,我创建了一个固定大小的本地文件系统,然后挂载了它.这是为了防止虚拟主机的文件系统被填满。

一般的做法是这样的:

IMAGE=filesystem/image.img
MOUNT_POINT=filesystem/mount
SIZE=20
PROJECT_ROOT=`pwd`

# Number of M to set aside for this filing system
dd if=/dev/zero of=$IMAGE bs=1M count=$SIZE &> /dev/null

# Format: the -F permits creation even though it's not a "block special device"
mkfs.ext3 -F -q $IMAGE

# Mount if the filing system is not already mounted
$MOUNTCMD | cut -d ' ' -f 3 | grep -q "^${PROJECT_ROOT}/${MOUNT_POINT}$"
if [ $? -ne 0 ]; then
    # -p Create all parent dirs as necessary
    mkdir -p $MOUNT_POINT
    /bin/mount -t ext3 $IMAGE $MOUNT_POINT
fi

这在 Linux 本地或远程 VM 中工作正常。但是,我想 运行 这个 shell 代码,或类似的东西,inside 一个容器。我想这样做的部分原因是将所有繁琐的东西包含在一个容器中,以便构建一个新的主机尽可能简单(在我看来,设置自定义安装和 cron-restart主机上的规则与此相反)。

因此,此命令在容器内不起作用("filesystem" 是一个 on-host Docker 卷)

mount -t ext3 filesystem/image.img filesystem/mount
mount: can't setup loop device: No space left on device

它也不适用于容器文件夹("filesystem2" 是容器目录):

dd if=/dev/zero of=filesystem2/image.img bs=1M count=20
mount -t ext3 filesystem2/image.img filesystem2/mount
mount: can't setup loop device: No space left on device

我想知道容器是否没有合适的内部机制来进行安装,因此我是否应该改变方向。我不想在这上面花太多时间(我只是将一个项目移动到 Docker-only 服务器),这就是为什么我希望 mount 可以工作。

其他选项

如果那不可能,那么 size-limited Docker 卷,它适用于 Docker 和 Swarm,可能是我需要研究的替代方案。关于这是否真的有效,网上有相互矛盾的报道 (see this question)。

有一个 to say this is supported in Flocker. However, I am hesitant to use that, as it appears to be abandoned,大概是受到了ClusterHQ破产的影响。

表示我可以将 --storage-opt size=120Gdocker run 一起使用。但是,docker service create 似乎不支持它(除非该选项可能已重命名)。

更新

根据评论会议,我取得了一些进展;我发现将 --privileged 添加到 docker run 可以启用挂载,但代价是移除安全隔离。一位乐于助人的评论者说,最好使用 --cap-add SYS_ADMIN 的 more fine-grained 控制,让容器保留一些隔离。

但是,Docker Swarm 尚未实现这两个标志中的任何一个,因此我无法使用此解决方案。 This lengthy feature request 向我建议不会匆忙添加此功能;它已经等待两年了。

我找到了一个我很满意的大小限制解决方案,它根本不使用 Linux mount 命令。我还没有实现它,但下面记录的测试已经足够令人满意了。读者不妨注意最后的小警告。

在问这个问题之前,我没有尝试安装 Docker 卷,因为我的部分研究偶然发现了 Stack Overflow 海报,怀疑是否可以使 Docker 卷符合大小局限性。我的测试表明他们可以,但您可能希望在您自己的平台上测试它以确保它适合您。

Docker 容器的大小限制

以下命令是从网络上的各种来源拼凑而成的。

首先,我创建了一个像这样的卷,大小限制为 20 米:

docker volume create \
    --driver local \
    --opt o=size=20m \
    --opt type=tmpfs \
    --opt device=tmpfs \
    hello-volume

然后我创建一个 Alpine Swarm 服务,并在这个容器上挂载:

docker service create \
    --mount source=hello-volume,target=/myvol \
    alpine \
    sleep 10000

我们可以通过在此服务的单个容器上获取​​ shell 来确保容器已安装:

docker exec -it amazing_feynman.1.lpsgoyv0jrju6fvb8skrybqap
/ # ls - /myvol
total 0

好的,很好。因此,在保留此 shell 的同时,让我们尝试以 5m 的增量慢慢压倒该磁盘。我们可以看到它在第五次尝试时失败了,这是我们所期望的:

/ # cd /myvol
/myvol # ls
/myvol # dd if=/dev/zero of=image1 bs=1M count=5
5+0 records in
5+0 records out
/myvol # dd if=/dev/zero of=image2 bs=1M count=5
5+0 records in
5+0 records out
/myvol # ls -l
total 10240
-rw-r--r--    1 root     root       5242880 Sep 16 13:11 image1
-rw-r--r--    1 root     root       5242880 Sep 16 13:12 image2
/myvol # dd if=/dev/zero of=image3 bs=1M count=5
5+0 records in
5+0 records out
/myvol # dd if=/dev/zero of=image4 bs=1M count=5
5+0 records in
5+0 records out
/myvol # ls -l
total 20480
-rw-r--r--    1 root     root       5242880 Sep 16 13:11 image1
-rw-r--r--    1 root     root       5242880 Sep 16 13:12 image2
-rw-r--r--    1 root     root       5242880 Sep 16 13:12 image3
-rw-r--r--    1 root     root       5242880 Sep 16 13:12 image4
/myvol # dd if=/dev/zero of=image5 bs=1M count=5
dd: writing 'image5': No space left on device
1+0 records in
0+0 records out
/myvol # 

最后,让我们看看我们是否可以通过一次压倒磁盘来获得错误,以防限制仅适用于在已满磁盘中新打开的文件句柄:

/ # cd /myvol
/ # rm *
/myvol # dd if=/dev/zero of=image1 bs=1M count=21
dd: writing 'image1': No space left on device
21+0 records in
20+0 records out

事实证明我们可以,所以这对我来说看起来非常稳健。

注意事项

该卷是用 type 和 "tmpfs" 的 device 创建的,这听起来像 RAM 磁盘,令人担忧。我已成功检查该卷在系统重新启动后是否保持连接且完好无损,因此对我来说看起来不错,至少目前如此。

但是,我要说的是,在组织您的数据持久性系统时,不要只是复制我所拥有的。在将其投入生产之前,请确保该卷对于您的用例足够强大,当然,请确保将其包含在您的备份过程中。

(这适用于 Docker 版本 18.06.1-ce,构建 e68fc7a)。

您将无法在容器内安全地执行此操作。 Docker 从容器中删除挂载权限,因为使用它你可以挂载主机文件系统并逃离容器。但是,您可以在容器外部执行此操作,并使用默认的本地驱动程序将文件系统作为卷安装到容器中。大多数文件系统不支持 size 选项,tmpfs 是少数例外之一。它们中的大多数使用您使用图像文件创建命令定义的底层设备的大小:

dd if=/dev/zero of=filesystem/image.img bs=1M count=$SIZE

我无法让 docker 动态创建循环设备,所以这里是手动创建它的过程:

$ sudo losetup --find --show ./vol-image.img
/dev/loop0
$ sudo mkfs -t ext3 /dev/loop0
mke2fs 1.43.4 (31-Jan-2017)
Creating filesystem with 10240 1k blocks and 2560 inodes
Filesystem UUID: 25c95fcd-6c78-4b8e-b923-f808517b28df
Superblock backups stored on blocks:
        8193

Allocating group tables: done
Writing inode tables: done
Creating journal (1024 blocks): done
Writing superblocks and filesystem accounting information: done

当定义卷挂载选项时,几乎是从挂载命令中逐字传递的,您 运行 在命令行上:

docker volume create --driver local --opt type=ext3 \
  --opt device=filesystem/image.img app_vol
docker service create --mount type=volume,src=app_vol,dst=/filesystem/mount ...

或在单个服务创建命令中:

docker service create \
  --mount type=volume,src=app_vol,dst=/filesystem/mount,volume-driver=local,volume-opt=type=ext3,volume-opt=device=filesystem/image.img ...

使用 docker run,命令如下所示:

$ docker run -it --rm --mount type=volume,dst=/data,src=ext3vol,volume-driver=local,volume-opt=type=ext3,volume-opt=device=/dev/loop0 busybox /bin/sh
/ # ls -al /data
total 17
drwxr-xr-x    3 root     root          1024 Sep 19 14:39 .
drwxr-xr-x    1 root     root          4096 Sep 19 14:40 ..
drwx------    2 root     root         12288 Sep 19 14:39 lost+found

唯一的先决条件是在创建服务之前创建此文件和循环设备,并且在安排服务的任何地方都可以访问此文件。我还建议使这些命令中的所有路径完全限定,而不是相对于当前目录。我很确定有几个地方的相对路径不起作用。