当前位置: 首页 > 面试题库 >

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

艾俊晖
2023-03-14
问题内容

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

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

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

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


问题答案:

有多种方法可以执行此操作,但主要问题是绑定安装不包含任何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



 类似资料:
  • 问题内容: 我想这样做,以便我启动的Docker容器使用与我运行的主机相同的设置。有没有办法做到这一点? 我知道docker run 有一个选项,但这并不是我想要的,因为主机的文件在不同的机器上可能不同,所以用硬编码确切的IP /主机对我来说不是很好。 问题答案: 使用在泊坞窗运行命令。这告诉Docker使容器使用主机的网络堆栈。您可以在此处了解更多信息。

  • 问题内容: 我在Mac上创建了一个Ubuntu Docker容器 我将端口设置为123。 我的容器IP是 在Mac上,我尝试ping我的容器, ,我收到icmp_seq 0的请求超时…。 我该怎么办?因此,我的本地计算机可以ping通我安装的容器。我是否在容器(这是一个普通的ubuntu系统)上缺少一些应用程序安装? 问题答案: 您不能直接使用Docker for Mac ping或访问容器接口

  • 我是新来的Docker和试图了解什么是最好的方式插入docker父主机IP到容器主机文件。 我在我的Dockerfile中使用以下命令

  • 我有一个js文件,其中包含一些字典结构,如下例- 文件:read_js。js公司 我想使用typecript在此字典中添加一些数据。如何实现这一点?我尝试了,但这返回文件中存在的所有文本,因此无法读取字典并附加我自己的键值并重新写入js文件。

  • 如何写这个问题?老实说,我不明白这个问题的意思。A) 编写读者和作者优先于读者的解决方案,并评论每个信号量的功能。(记住变量和信号量的定义和初始化)B)读卡器的优先级意味着什么?当一个作家在写作时,到达的读者会发生什么?当编写器结束其操作时会发生什么?

  • 我有一个Dockerfile: 当demo.php执行时,它会创建一个文件,我想将该文件复制到主机上的卷。是否可以使用这样的卷将文件从容器中恢复到主机上?