如何让主机和容器使用 Docker 读/写相同的文件? [英] How can I have a host and container read/write the same files with Docker?

查看:28
本文介绍了如何让主机和容器使用 Docker 读/写相同的文件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将一个目录从 Docker 容器批量挂载到我的工作站,所以当我从我的工作站编辑卷挂载中的内容时,它也会在容器中更新.一般而言,它对于测试和开发 Web 应用程序非常有用.

I would like to volume mount a directory from a Docker container to my work station, so when I edit the content in the volume mount from my work station it updated in the container as well. It would be very useful for testing and develop web applications in general.

但是我在容器中获得了拒绝的权限,因为容器中的 UID 和主机中的 UID 不同.Docker 的初衷不就是让开发更快更容易吗?

However I get a permission denied in the container, because the UID's in the container and host isn't the same. Isn't the original purpose of Docker that it should make development faster and easier?

这个答案 解决了我在将 Docker 容器批量安装到我的工作站时面临的问题.但是通过这样做,我对生产中不需要的容器进行了更改,这违背了在开发过程中使用 Docker 的目的.

This answer works around the issue I am facing when volume mounting a Docker container to my work station. But by doing this, I make changes to the container that I won't want in production, and that defeats the purpose of using Docker during development.

容器为Alpine Linux,工作站Fedora 29 和编辑器 原子.

The container is Alpine Linux, work station Fedora 29, and editor Atom.

问题

还有其他方法可以让我的工作站和容器可以读/写相同的文件吗?

Is there another way, so both my work station and container can read/write the same files?

推荐答案

有多种方法可以做到这一点,但核心问题是绑定挂载不包含任何 UID 映射功能,主机上的 UID 是显示在里面的容器,反之亦然.如果这两个 UID 不匹配,您将使用不同的 UID 读取/写入文件,并且可能会遇到权限问题.

There are multiple ways to do this, but the central issue is that bind mounts do not include any UID mapping capability, the UID on the host is what appears inside the container and vice versa. If those two UID's do not match, you will read/write files with different UID's and likely experience permission issues.

选项 1:获取 Mac 或在 VirtualBox 内部署 docker.这两种环境都有一个文件系统集成,可以动态更新 UID.对于 Mac,这是通过 OSXFS 实现的.请注意,这种便利会带来性能损失.

Option 1: get a Mac or deploy docker inside of VirtualBox. Both of these environments have a filesystem integration that dynamically updates the UID's. For Mac, that is implemented with OSXFS. Be aware that this convenience comes with a performance penalty.

选项 2:更改您的主机.如果主机上的 UID 与容器内的 UID 匹配,您将不会遇到任何问题.您只需在主机上的用户上运行 usermod 以更改那里的 UID,事情就会发生,至少在您在容器内运行具有不同 UID 的不同图像之前.

Option 2: Change your host. If the UID on the host matches the UID inside the container, you won't experience any issues. You'd just run a usermod on your user on the host to change your UID there, and things will happen to work, at least until you run a different image with a different UID inside the container.

选项 3:更改您的图片.有些人会将图像修改为与其环境匹配的静态 UID,通常是为了匹配生产中的 UID.其他人会传递一个像 --build-arg UID=$(id -u) 这样的构建参数作为构建命令的一部分,然后 Dockerfile 像这样:

Option 3: Change your image. Some will modify the image to a static UID that matches their environment, often to match a UID in production. Others will pass a build arg with something like --build-arg UID=$(id -u) as part of the build command, and then the Dockerfile with something like:

FROM alpine
ARG UID=1000
RUN adduser -u ${UID} app

这样做的缺点是每个开发人员可能需要不同的图像,因此他们要么在每个工作站上本地构建,要么您集中构建多个图像,一个用于开发人员之间存在的每个 UID.这两个都不理想.

The downside of this is each developer may need a different image, so they are either building locally on each workstation, or you centrally build multiple images, one for each UID that exists among your developers. Neither of these are ideal.

选项 4:更改容器 UID.这可以在撰写文件中完成,也可以在具有 docker run -u $(id -u) your_image 之类的一次性容器中完成.容器现在将使用新的 UID 运行,并且可以访问卷中的文件.但是,容器内的用户名不一定会映射到您的 UID,这对于您在容器内运行的任何命令来说可能看起来很奇怪.更重要的是,容器内用户拥有的任何文件,如果您没有隐藏在您的卷中,将具有原始 UID,并且可能无法访问.

Option 4: Change the container UID. This can be done in the compose file, or on a one off container with something like docker run -u $(id -u) your_image. The container will now be running with the new UID, and files in the volume will be accessible. However, the username inside the container will not necessarily map to your UID which may look strange to any commands you run inside the container. More importantly, any files own by the user inside the container that you have not hidden with your volume will have the original UID and may not be accessible.

选项 5:放弃,以 root 身份运行所有内容,或将权限更改为 777,允许所有人无限制地访问目录.这不会映射到您应该如何在生产中运行事物,并且容器可能仍会写入具有有限权限的新文件,使您无法在容器外部访问它们.这也会产生以 root 身份运行代码或让文件系统对主机上的任何用户进行读写的安全风险.

Option 5: Give up, run everything as root, or change permissions to 777 allowing everyone to access the directory with no restrictions. This won't map to how you should run things in production, and the container may still write new files with limited permissions making them inaccessible to you outside the container. This also creates security risks of running code as root or leaving filesystems open to both read and write from any user on the host.

选项 6:设置动态更新容器的入口点.尽管不想更改您的图像,但这是我首选的完整性解决方案.您的容器确实需要以 root 身份启动,但仅限于开发阶段,并且应用程序仍将以用户身份运行,与生产环境相匹配.但是,该入口点的第一步是更改容器内用户的 UID/GID 以匹配您卷的 UID/GID.这类似于选项 4,但现在图像中未被卷替换的文件具有正确的 UID,并且容器内的用户现在将显示更改后的 UID,因此像 ls 这样的命令显示容器内的用户名,而不是 UID,可以映射到另一个用户或根本没有用户.虽然这是对您的映像的更改,但代码仅在开发中运行,并且仅作为为该开发人员设置容器的简短入口点,之后容器内的过程将与生产环境中的过程相同.

Option 6: Setup an entrypoint that dynamically updates your container. Despite not wanting to change your image, this is my preferred solution for completeness. Your container does need to start as root, but only in development, and the app will still be run as the user, matching the production environment. However, the first step of that entrypoint will be to change the user's UID/GID inside the container to match your volume's UID/GID. This is similar to option 4, but now files inside the image that were not replaced by the volume have the right UID's, and the user inside the container will now show with the changed UID so commands like ls show the username inside the container, not a UID to may map to another user or no one at all. While this is a change to your image, the code only runs in development, and only as a brief entrypoint to setup the container for that developer, after which the process inside the container will look identical to that in a production environment.

为了实现这一点,我进行了以下更改.首先,Dockerfile 现在包含一个 fix-perms 脚本和来自我推送到集线器的基本映像的 gosu(这是一个 Java 示例,但更改可移植到其他环境):

To implement this I make the following changes. First the Dockerfile now includes a fix-perms script and gosu from a base image I've pushed to the hub (this is a Java example, but the changes are portable to other environments):

FROM openjdk:jdk as build
# add this copy to include fix-perms and gosu or install them directly
COPY --from=sudobmitch/base:scratch / /
RUN  apt-get update 
 &&  apt-get install -y maven 
 &&  useradd -m app
COPY code /code
RUN  mvn build
# add an entrypoint to call fix-perms
COPY entrypoint.sh /usr/bin/
ENTRYPOINT ["/usr/bin/entrypoint.sh"]
CMD ["java", "-jar", "/code/app.jar"]
USER app

entrypoint.sh 脚本调用 fix-perms,然后 exec 和 gosu 从 root 到应用程序用户:

The entrypoint.sh script calls fix-perms and then exec and gosu to drop from root to the app user:

#!/bin/sh
if [ "$(id -u)" = "0" ]; then
  # running on a developer laptop as root
  fix-perms -r -u app -g app /code
  exec gosu app "$@"
else
  # running in production as a user
  exec "$@"
fi

开发人员撰写文件装载卷并以 root 身份启动:

The developer compose file mounts the volume and starts as root:

version: '3.7'
volumes:
  m2:
services:
  app:
    build:
      context: .
      target: build
    image: registry:5000/app/app:dev
    command: "/bin/sh -c 'mvn build && java -jar /code/app.jar'"
    user: "0:0"
    volumes:
    - m2:/home/app/.m2
    - ./code:/code

这个例子取自我的演示文稿:https://sudo-bmitch.github.io/presentations/dc2019/tips-and-tricks-of-the-captains.html#fix-perms

This example is taken from my presentation available here: https://sudo-bmitch.github.io/presentations/dc2019/tips-and-tricks-of-the-captains.html#fix-perms

fix-perms 和其他示例的代码可在我的基础镜像仓库中找到:https://github.com/sudo-bmitch/docker-base

Code for fix-perms and other examples are available in my base image repo: https://github.com/sudo-bmitch/docker-base

这篇关于如何让主机和容器使用 Docker 读/写相同的文件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆