一尘不染

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

linux

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

但是我在容器中获得了拒绝权限,因为容器和主机中的UID不同。Docker的最初目的不是应该使开发变得更快,更容易吗?
当将Docker容器批量安装到我的工作站时,这个答案可以解决我面临的问题。但是通过这样做,我对生产中不需要的容器进行了更改,这违背了在开发过程中使用Docker的目的。

容器是AlpineLinux,工作站Fedora29和编辑器Atom

还有另一种方法,这样我的工作站和容器都可以读取/写入相同的文件吗?


阅读 408

收藏
2020-06-02

共1个答案

一尘不染

有多种方法可以执行此操作,但主要问题是绑定安装不包含任何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命令的一部分传递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从根目录降级到应用程序用户:

#!/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

2020-06-02