在Docker容器中将只读挂载变为可写

52次阅读
没有评论

问题描述

在开发大型代码库时,我希望能够在容器中并行运行多个构建/测试任务。我需要将我的代码库或其他大型目录放入容器中。如果我将其以读/写方式挂载,那么并行进程会互相干扰,我需要在它们之后进行清理,而且还存在权限升级的问题。但如果我将其以只读方式挂载,那么就无法运行需要在目录内部写文件的进程。
Docker建议创建一个新的容器,将代码库复制到其中,保存为新的镜像层,然后从这个新镜像中启动多个容器。但是,每次想要运行测试时都需要复制几百兆的数据,这会导致花费太长时间。
我应该如何高效解决这个问题?

解决方案

请注意以下操作可能涉及特权权限和版本差异,请确保事先做好备份。

方案

为了解决这个问题,可以创建一个覆盖文件系统(overlay filesystem),使其覆盖在只读挂载上。但如果直接进行操作,覆盖文件系统会拒绝在另一个覆盖文件系统上设置上层和工作目录。解决方法是为上层和工作目录创建一个tmpfs,如下所示:

  1. 创建一个名为run-in-c.sh的脚本文件。
  2. 在脚本中设置必要的变量,如NAMEHOSTNAMECONTAINER等。
  3. 使用docker container create命令创建一个容器,并指定挂载只读目录和基础镜像。
  4. 使用docker container start命令启动容器。
  5. 使用docker container exec命令在容器内部创建tmpfs并挂载为上层和工作目录。
  6. 使用docker container exec命令挂载覆盖文件系统。
  7. 使用docker container exec命令在容器内部执行需要的命令。
  8. 使用docker container stop命令停止容器。
  9. 使用docker container rm命令删除容器。

以下是一个示例run-in-c.sh脚本:

#!/bin/bash
NAME=$1
shift
HOSTNAME=dock${NAME}
CONTAINER=dc-${USER}-${NAME}
REPOSITORY=${HOME}/repository
BASEIMAGE=hub.docker.io/my-org/my-base-container
OVERLAY=/mnt/overlay
LOWERDIR=/mnt/lower
UPPERDIR=${OVERLAY}/upper
WORKDIR=${OVERLAY}/work
TARGET=/mnt/repository
PRIVILEGED="--cap-add SYS_ADMIN"
docker container create --name $CONTAINER $PRIVILEGED --hostname $HOSTNAME --volume ${REPOSITORY}:${LOWERDIR}:ro $BASEIMAGE
docker container start $CONTAINER
docker container exec $CONTAINER mkdir -p $OVERLAY
docker container exec $CONTAINER mount -t tmpfs tmpfs $OVERLAY
docker container exec $CONTAINER mkdir -p $WORKDIR $UPPERDIR $TARGET
docker container exec $CONTAINER mount -t overlay overlay -o lowerdir=${LOWERDIR},upperdir=${UPPERDIR},workdir=${WORKDIR} $TARGET
docker container exec -it --workdir $TARGET $CONTAINER $*
docker container stop $CONTAINER
docker container rm $CONTAINER

使用上述脚本,你可以在容器内部运行命令,例如:

run-in-c.sh test01 'cd dir && command args'

或者获取交互式shell:

run-in-c.sh naming-stuff-is-too-hard bash

请注意,这个解决方案需要在容器内作为root用户运行,因为挂载的卷是以root:root的身份挂载到容器中的。如果你以用户身份执行命令,就无法写入文件(除非你的所有目录都是全局可写的)。如果尝试对代码库中的所有文件进行chown操作,会在上层创建它们的副本,这将产生相反的效果。如果有办法让这个解决方案以普通用户身份运行,我会很感兴趣。

正文完