使用绑定挂载的主机目录和容器之间的 Docker 文件权限不匹配

Posted

技术标签:

【中文标题】使用绑定挂载的主机目录和容器之间的 Docker 文件权限不匹配【英文标题】:Docker file permissions mismatch between host dir and container using bind-mount 【发布时间】:2020-03-16 21:39:01 【问题描述】:

问题

我的 docker-compose 堆栈由 'postgresql'、'redis' 和 'Python api server' 以及一些其他的如 opentracing 等组成,但问题区域仅限于前面提到的。

    我的 compose 文件中的入口点是一个 shell 脚本,它通过读取环境变量以及它应该做的其他事情来动态创建一些文件和文件夹。现在,这些文件的创建就像一个魅力,但这些动态生成的文件的文件和文件夹权限变得有趣。在 macOS 上,这些动态生成的文件和文件夹归运行 docker-compose up 的非 root 用户所有。但是,在运行 Ubuntu 19.01 的 linux 机器上,这些文件和文件夹归 root 所有,尽管 Dockerfile 明确对整个项目的文件夹执行 chown non-root-user:non-root-group 并将活动用户设置为 non-root-user

    postgres 容器将自己挂载到给定路径上,但该目录的所有者不再是创建它的人,而是一些奇怪的 systemd-coredump 我猜这是因为 Postgres 的 Dockerfile 上的用户 ID 和组映射到我的这个用户名linux服务器?如果是,避免这种情况的推荐方法是什么?

由于运行docker-compose up 的非root 用户无法保留主机上的文件和文件夹权限,我遇到了permission denied 问题。虽然chmod 777 有助于解决问题,但我相信 chmod 777 从来没有真正解决任何问题。

重申所有这些都只是 Linux 机器上的问题。在运行 Docker-For-Mac 的 Macos 上,预先存在的和动态生成的文件/文件夹都将非 root 登录用户保留为其所有者,并且在容器内,Dockerfile 中指定的用户仍然是所有预先存在的(那些通过COPY 传输)和新生成的动态文件/文件夹。

例子

文件和文件夹所有权更改示例:

drwxrwxr-x 13 sparkle_deployment_2 sparkle_deployment_2 4096 Nov 21 01:00 PROTON/
drwx------ 19 systemd-coredump     docker               4096 Nov 21 01:00 proton_db/

从上面看,proton_db 是 Postgres 应该挂载的位置。此文件夹最初由用户 - sparkle_deployment_2 创建。在docker-compose up之后,所有者和组分别更改为system-coredumpdocker

这是我的一部分:docker-compose.yaml

version: "3.4"
services:
  pg:
    container_name: proton_postgres
    restart: always
    image: postgres
    environment:
      - POSTGRES_USER=$PG_USERNAME
      - POSTGRES_PASSWORD=$PG_PASSWORD
      - POSTGRES_DB=$PG_TARGET_DB
    volumes:
      - $PROTON_POSTGRES_VOLUME_MOUNT:/var/lib/postgresql/data
    ports:
      - $PG_TARGET_PORT:$PG_TARGET_PORT
  redis:
    container_name: proton_redis
    restart: always
    image: redis
    volumes:
      - $PROTON_REDIS_VOLUME_MOUNT:/data
    ports:
      - $REDIS_TARGET_PORT:$REDIS_TARGET_PORT
  proton:
    container_name: proton
    restart: always
    image: proton_stretch
    ports:
      - $PROTON_TARGET_PORT:$PROTON_TARGET_PORT
    expose:
      - $PROTON_TARGET_PORT
    volumes:
      - .:/PROTON
      - $PROTON_SQLITE_VOLUME_MOUNT:/PROTON/proton-db
    depends_on:
      - pg
      - redis
    entrypoint: ["./proton.sh"]

还有,这是我的 API 服务器的 Dockerfile:

FROM python:3.7.3-stretch

RUN apt-get update
RUN apt-get install bash
RUN apt-get install -y gcc g++ unixodbc-dev

RUN groupadd -g proton_user_group
RUN useradd -G proton_user_group default_proton_user

RUN mkdir -p /PROTON
WORKDIR /PROTON
COPY . /PROTON

RUN python3 -m pip install -r requirements.txt --no-cache-dir

RUN chown -R default_proton_user:proton_user_group /PROTON
USER default_proton_user

EXPOSE 3000/tcp

如您所见,我正在执行chown 以明确拥有非root 用户拥有的目录。尽管如此,当有动态生成的文件/文件夹时,它们会得到root 作为它们的所有者。而且,这只发生在 linux 上。

就像在 MacOS 中一样,我希望 linux 机器上的非 root 主机保留所有预先存在和动态生成的文件/文件夹的所有权,因此不会导致任何“权限被拒绝”问题。

加上 linux 机器上相同的非 root 主机,以保留 Postgres 容器卷安装位置的所有权。

【问题讨论】:

您是否在 Dockerfile 中明确设置了 addgroupadduser,并分别设置了明确的 giduid? Docker 不会对 giduid 做任何特殊处理,因此如果您的映像中的值是主机上组和/或用户的影子值,那么所有权将显示为影子组和/或用户。跨度> 为了让这个问题更容易回答,我建议您忽略/删除 macos 部分。 Docker Desktop 增加了跨操作系统的权限,因此您的 mac 用户拥有在绑定挂载中创建的文件。 Linux 不是那样的,它只是使用本机 Linux 用户权限。提出这个关于修复你的 Linux 文件权限的问题 它也将有助于给出一个使用 dockerfile 和 compose 文件的可重现示例。通常,在 docker-compose 中绑定挂载数据库是一种反模式。请改用命名卷。 感谢@BretFisher 完全同意命名卷。这就是我在撰写 yaml 中使用的内容。我对您之前对 docker-for-mac 增强权限的评论更感兴趣。我们如何强制非 root 用户和组到 docker-compose' 容器?我将使用 docker-compose 和 Dockerfile 示例更新我的问题,以更好地重现我的问题。感谢您的帮助。 @wmorrell 我已经用我的 docker-compose.yaml 的一部分 + 我的 API 服务器的 Dockerfile 更新了我的问题。是的。正如你在上面看到的,我明确地做了addgroupadduser。感谢您的时间和帮助。 【参考方案1】:

由于您在 docker-compose 中绑定挂载 Python 容器,因此 Dockerfile 文件和现有权限无关紧要。在运行时,它将pwd 挂载到/PROTON,因此/PROTON 映像中的任何内容都被隐藏,容器只能看到主机上的pwd

容器中的用户是与主机匹配的简单 UID 和 GID 号。例如,在主机上使用id 命令来获取您的 UID 和 GID。对我来说,它们是 1000 和 1000。您只需要确保在容器中运行的用户和组是相同的 UID/GID。

RUN groupadd --gid 1000 proton \
  && useradd --uid 1000 --gid proton --create-home proton

现在您的主机用户和容器用户 UID/GID 相同,您会注意到在 pwd 中创建的文件与每个用户的用户名匹配。主机上的 Linux 将查找 UID 1000 并查看它的主机用户(对我来说是 bret),如果您执行 docker-compose exec proton ls -al /PROTON,您应该注意到它将在容器中查找用户 1000 并查看 proton用户名只是 ID 的友好名称,因此只需确保它们在主机用户和容器使用之间匹配即可。

无关提示:

您可以change the user that compose starts your container with,使用user: username,但如果它是您使用 USER 放入 Dockerfile 的那个,那么在这种情况下就不需要了。 您的Dockerfile COPY command can use chown 内联,为您节省一个步骤和图像空间: COPY --chown=1000:1000 . /PROTON , 或 COPY --chown=proton:proton . /PROTON

【讨论】:

太棒了!我看到 docker-for-mac 现在如何增强它。谢谢布雷特!对此可能有一个警告 - 如果新主机登录,他们可能无法使用为具有其他 UID/GID 的用户构建的图像?这是预期的还是有办法解决这个问题?感谢你的宝贵时间。真的很感激 当然,您希望两个用户都属于对文件具有适当访问权限的组,但这些都不是特定于容器的。容器不会触及权限。它们与任何 Linux 进程一样,只需要确保它们具有文件和目录的用户或组权限。

以上是关于使用绑定挂载的主机目录和容器之间的 Docker 文件权限不匹配的主要内容,如果未能解决你的问题,请参考以下文章

docker-tmpfs挂载

解决 Docker 数据卷挂载的文件权限问题

Docker 基础知识 - 使用卷(volume)管理应用程序数据

#云原生征文# docker数据卷与DockerFile学习

Docker 绑定挂载目录中的文件未更新

docker添加挂载目录或者添加端口