如何让主机和容器使用Docker读取/写入相同的文件?

时间:2019-05-09 12:54:30

标签: javascript python linux docker

我想将目录从Docker容器批量装载到我的工作站,因此当我从工作站编辑批量装载中的内容时,它也在容器中更新。通常对于测试和开发Web应用程序非常有用。

但是我在容器中获得了拒绝权限,因为容器和主机中的UID不同。 Docker的最初目的不是应该使开发变得更快,更容易吗?

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

容器是Alpine,工作站Fedora 29和编辑器Atom。

问题

有人可以告诉我是否还有其他方法,这样我的工作站和容器都可以读取/写入相同的文件吗?

2 个答案:

答案 0 :(得分:2)

执行此操作的方法有多种,但主要问题是绑定安装不包含任何UID映射功能,主机上的UID是容器内显示的内容,反之亦然。如果这两个UID不匹配,则您将读写具有不同UID的文件,并且可能会遇到权限问题。


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


选项2:更改主机。如果主机上的UID与容器内的UID相匹配,则不会遇到任何问题。您只需在主机上的用户上运行一个usermod来在该主机上更改您的UID,事情就会发生,至少要等到您在容器内运行具有不同UID的其他映像为止。


选项3:更改图像。有些人会将图像修改为与环境匹配的静态UID,通常会与生产环境中的UID匹配。其他人将在构建命令中传递带有--build-arg UID=$(id -u)之类的build arg,然后传递诸如以下内容的Dockerfile:

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

缺点是每个开发人员可能需要不同的映像,因此他们要么在每个工作站上本地构建,要么集中构建多个映像,一个针对开发人员中存在的每个UID。这些都不是理想的。


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


选项5:放弃,以root用户身份运行所有内容,或将权限更改为777,以允许所有人不受限制地访问目录。这不会映射到您应如何在生产环境中运行事物,并且容器可能仍会以受限的权限编写新文件,从而使您无法在容器外部访问它们。这也带来了安全风险,即以root用户身份运行代码或使文件系统保持打开状态,以便主机上任何用户均可读写。


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

为实现此目的,我进行了以下更改。首先,Dockerfile现在包含一个修复程序脚本和我推送到集线器的基础映像中的gosu(这是一个Java示例,但是更改可以移植到其他环境中):

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拖放到应用程序用户:

#!/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身份启动

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

我的基本映像存储库中提供了有关修补程序代码和其他示例的代码:https://github.com/sudo-bmitch/docker-base

答案 1 :(得分:0)

由于容器中的UID已烘焙到容器定义中,因此可以放心地假定它们是相对静态的。在这种情况下,您可以在主机系统中使用计算机UID和GID创建用户。将用户更改为新帐户,然后对文件进行编辑。您的主机操作系统不会抱怨,因为它认为只是用户在访问自己的文件,而容器操作系统也会看到相同的内容。

或者,您可以考虑以root身份编辑这些文件。