ChatGPT解决这个技术问题 Extra ChatGPT

管理 Docker 共享卷权限的(最佳)方法是什么?

我一直在使用 Docker,并且在处理持久数据时不断发现同样的问题。

我创建我的 Dockerfileexpose a volume 或使用 --volumes-frommount a host folder inside my container

我应该对主机上的共享卷应用什么权限?

我可以想到两个选择:

到目前为止,我已经为每个人提供了读/写访问权限,因此我可以从 Docker 容器写入文件夹。

将主机中的用户映射到容器中,这样我就可以分配更精细的权限。虽然不确定这是可能的,但还没有找到太多关于它的信息。到目前为止,我所能做的就是以某个用户身份运行容器:docker run -i -t -user="myuser" postgres,但是这个用户的 UID 与我的主机 myuser 不同,因此权限不起作用。另外,我不确定映射用户是否会带来一些安全风险。

还有其他选择吗?

你们是如何处理这个问题的?

您可能还对此主题感兴趣,该主题详细讨论了该主题:groups.google.com/forum/#!msg/docker-user/cVov44ZFg_c/…
目前,Docker 团队不打算实施本地解决方案来将主机目录挂载为具有指定 uid/gid 的卷。查看我对此问题的评论和回复:github.com/docker/docker/issues/7198#issuecomment-230636074

R
Raman

2016-03-02 更新:从 Docker 1.9.0 开始,Docker 具有 named volumesreplace data-only containers。下面的答案以及我链接的博客文章在如何考虑 docker 内部的数据 的意义上仍然具有价值,但考虑使用命名卷来实现下面描述的模式而不是数据容器。

我相信解决这个问题的规范方法是使用 data-only containers。使用这种方法,对卷数据的所有访问都是通过使用 -volumes-from 数据容器的容器进行的,因此主机 uid/gid 无关紧要。

例如,文档中给出的一个用例是备份数据卷。为此,另一个容器用于通过 tar 进行备份,它也使用 -volumes-from 来安装卷。所以我认为 grok 的关键点是:与其考虑如何以适当的权限访问主机上的数据,不如考虑如何通过另一个容器做任何你需要的事情——备份、浏览等。 .容器本身需要使用一致的 uid/gids,但它们不需要映射到主机上的任何东西,从而保持可移植性。

这对我来说也相对较新,但是如果您有特定的用例,请随时发表评论,我会尝试扩展答案。

更新:对于评论中的给定用例,您可能有一个图像 some/graphite 来运行石墨,以及一个图像 some/graphitedata 作为数据容器。因此,忽略端口等,图像 some/graphitedataDockerfile 类似于:

FROM debian:jessie
# add our user and group first to make sure their IDs get assigned consistently, regardless of other deps added later
RUN groupadd -r graphite \
  && useradd -r -g graphite graphite
RUN mkdir -p /data/graphite \
  && chown -R graphite:graphite /data/graphite
VOLUME /data/graphite
USER graphite
CMD ["echo", "Data container for graphite"]

构建并创建数据容器:

docker build -t some/graphitedata Dockerfile
docker run --name graphitedata some/graphitedata

some/graphite Dockerfile 也应该获得相同的 uid/gids,因此它可能看起来像这样:

FROM debian:jessie
# add our user and group first to make sure their IDs get assigned consistently, regardless of other deps added later
RUN groupadd -r graphite \
  && useradd -r -g graphite graphite
# ... graphite installation ...
VOLUME /data/graphite
USER graphite
CMD ["/bin/graphite"]

它将按如下方式运行:

docker run --volumes-from=graphitedata some/graphite

好的,现在这为我们提供了带有正确用户/组的石墨容器和关联的纯数据容器(请注意,您也可以将 some/graphite 容器重新用于数据容器,在运行时覆盖 entrypoing/cmd,但是将它们作为单独的图像 IMO 更清晰)。

现在,假设您要编辑数据文件夹中的某些内容。因此,与其将卷绑定到主机并在那里进行编辑,不如创建一个新容器来完成这项工作。我们称之为some/graphitetools。我们还可以创建适当的用户/组,就像 some/graphite 图像一样。

FROM debian:jessie
# add our user and group first to make sure their IDs get assigned consistently, regardless of other deps added later
RUN groupadd -r graphite \
  && useradd -r -g graphite graphite
VOLUME /data/graphite
USER graphite
CMD ["/bin/bash"]

您可以通过从 Dockerfile 中的 some/graphitesome/graphitedata 继承来使此 DRY,或者不创建新映像,只需重新使用现有映像之一(根据需要覆盖 entrypoint/cmd)。

现在,您只需运行:

docker run -ti --rm --volumes-from=graphitedata some/graphitetools

然后是 vi /data/graphite/whatever.txt。这非常有效,因为所有容器都有相同的石墨用户和匹配的 uid/gid。

由于您从不从主机装载 /data/graphite,因此您不必关心主机 uid/gid 如何映射到 graphitegraphitetools 容器内定义的 uid/gid。这些容器现在可以部署到任何主机上,并且它们将继续完美运行。

这样做的好处是 graphitetools 可以具有各种有用的实用程序和脚本,您现在还可以以可移植的方式进行部署。

更新 2:写完这个答案后,我决定写一篇关于这种方法的more complete blog post。我希望它有所帮助。

更新 3:我更正了这个答案并添加了更多细节。它以前包含一些关于所有权和权限的错误假设——所有权通常是在创建卷时分配的,即在数据容器中,因为那是创建卷的时候。请参阅this blog。但这不是必需的——您可以将数据容器用作“引用/句柄”,并通过入口点中的 chown 在另一个容器中设置所有权/权限,最后以 gosu 结尾,以正确用户身份运行命令。如果有人对这种方法感兴趣,请发表评论,我可以提供使用这种方法的示例链接。


恐怕这不是一个解决方案,因为您对纯数据容器也会有同样的问题。归根结底,这些容器将使用从主机共享的卷,因此您仍然需要管理这些共享文件夹的权限。
请记住,我可能需要从主机编辑数据文件夹(即:删除测试石墨密钥,删除我的 JIRA 测试主文件夹或使用最新的生产备份更新它......)。据我从你的评论中了解到,我应该做一些事情,比如通过第三个容器更新 JIRA 数据。无论如何,您会向新的数据文件夹 /data/newcontainer 应用什么权限?我假设您以 root 身份运行 docker(可以不这样做吗?)另外,如果数据直接挂载在主容器中或通过 仅数据容器
感谢您的精心回复。我一有机会就会对此进行测试。此外,很好地参考了 your blog 帖子和关于 using minimal images for data containers 的帖子。
这种方法的唯一问题是很容易误删除容器。想象一下,如果它恰好是您的数据容器。我认为(CMIIW)数据仍将在 /var/lib/docker 某处,但仍然是一个巨大的痛苦
“您可以将数据容器用作“参考/句柄”,并通过入口点中的 chown 在另一个容器中设置所有权/权限”... @Raman:这是在遇到许多权限问题后终于救了我的部分想通了。在此使用入口点脚本并设置权限对我有用。感谢您的详尽解释。这是迄今为止我在网上找到的最好的。
M
Mustapha-Belkacim

在官方 redis image 以及所有官方图片中都可以看到一个非常优雅的解决方案。

分步过程描述:

先创建redis用户/组

如 Dockerfile 评论所示:

首先添加我们的用户和组,以确保一致地分配他们的 ID,而不管添加了任何依赖项

使用 Dockerfile 安装 gosu

gosu 是 su / sudo 的替代品,可轻松从 root 用户降级。 (Redis 始终使用 redis 用户运行)

配置 /data 卷并将其设置为 workdir

通过使用 VOLUME /data 命令配置 /data 卷,我们现在有一个单独的卷,它可以是 docker 卷或绑定安装到主机目录。

将其配置为工作目录 (WORKDIR /data) 使其成为执行命令的默认目录。

添加 docker-entrypoint 文件并使用默认 CMD redis-server 将其设置为 ENTRYPOINT

这意味着所有的容器执行都会通过 docker-entrypoint 脚本运行,默认运行的命令是 redis-server。

docker-entrypoint 是一个执行简单功能的脚本:更改当前目录 (/data) 的所有权并从 root 降级为 redis 用户以运行 redis-server。 (如果执行的命令不是redis-server,则直接运行该命令。)

这具有以下效果

如果 /data 目录绑定挂载到主机,docker-entrypoint 将在 redis 用户下运行 redis-server 之前准备用户权限。

这使您可以放心,零设置可以在任何卷配置下运行容器。

当然,如果您需要在不同图像之间共享卷,则需要确保它们使用相同的用户 ID/组 ID,否则最新的容器将劫持前一个容器的用户权限。


接受的答案是信息丰富的,但它只是让我在长达一周的挫折中找到了这个答案,这个答案实际上提供了解决问题的规范方法。
所以?如何从 docker 内部使卷可写? chown 它在 ENTRYPOINT 脚本中?
2
2 revs

在大多数情况下,这可以说不是最好的方法,但它还没有被提及,所以也许它会对某人有所帮助。

绑定挂载主机卷 主机文件夹 FOOBAR 挂载在容器 /volume/FOOBAR 修改容器的启动脚本以找到您感兴趣的卷的 GID $ TARGET_GID=$(stat -c "%g" /volume/FOOBAR) 确保您的用户属于具有此 GID 的组(您可能必须创建一个新组)。对于此示例,我将假设我的软件在容器内以无人用户身份运行,因此我想确保无人属于组 ID 等于 TARGET_GID 的组

  EXISTS=$(cat /etc/group | grep $TARGET_GID | wc -l)

  # Create new group using target GID and add nobody user
  if [ $EXISTS == "0" ]; then
    groupadd -g $TARGET_GID tempgroup
    usermod -a -G tempgroup nobody
  else
    # GID exists, find group name and add
    GROUP=$(getent group $TARGET_GID | cut -d: -f1)
    usermod -a -G $GROUP nobody
  fi

我喜欢这个,因为我可以轻松地修改主机卷上的组权限,并且知道这些更新的权限适用于 docker 容器内。这发生在没有对我的主机文件夹/文件进行任何许可或所有权修改的情况下,这让我很高兴。

我不喜欢这样,因为它假定将自己添加到容器内的任意组中没有危险,而这些组恰好使用了您想要的 GID。它不能与 Dockerfile 中的 USER 子句一起使用(除非我认为该用户具有 root 权限)。此外,它尖叫黑客工作;-)

如果您想成为铁杆,您显然可以通过多种方式扩展它 - 例如在任何子文件、多个卷等上搜索所有组。


这是否针对从已安装的卷中读取文件?我正在寻找一种解决方案来编写文件,而不是由创建 docker 容器的其他用户拥有它们。
我从 Aug'15 开始使用这种方法。一切都很好。只是在容器内创建的文件的权限是不同的。用户(容器内部和外部)都拥有其文件的所有权,但都具有读取权限,因为它们确实属于此解决方案创建的同一个组。当用例确实对公共文件施加写访问权时,问题就开始了。更大的问题是共享卷有 git 文件(它是在同一生产环境中测试开发源文件的卷)。 Git 开始警告共享代码的访问问题。
我认为$TARGET_GID 更好的 grep 是使用 grep ':$TARGET_GID:',否则如果容器有,例如 gid 10001 并且您的主机是 1000,则此检查将通过但它不应该。
此解决方案也适用于写作。 – 您最终需要在 git 存储库中运行 chmod g+w -R OR 在已安装的卷上,以便 tempgroup 仍然可以从容器内部写入。
我的评论方法的唯一缺点是,根据您的操作系统(如在 macOS 上),您可能设置了 umask 022,而不是 umask 002,因此您在主机端(例如从 IDE)创建的每个文件都会对于需要在容器中写入的任何文件,都需要运行相同的 chmod g+w 命令。但大多数情况下,对于通过 IDE 创建的源代码文件,情况并非如此。 🙃
F
FDisk

尝试向 Dockerfile 添加命令

RUN usermod -u 1000 www-data

学分归 https://github.com/denderello/symfony-docker-example/issues/2#issuecomment-94387272


A
Alex Myznikov

和你一样,我正在寻找一种将用户/组从主机映射到 docker 容器的方法,这是迄今为止我发现的最短的方法:

  version: "3"
    services:
      my-service:
        .....
        volumes:
          # take uid/gid lists from host
          - /etc/passwd:/etc/passwd:ro
          - /etc/group:/etc/group:ro
          # mount config folder
          - path-to-my-configs/my-service:/etc/my-service:ro
        .....

这是我的 docker-compose.yml 的摘录。

这个想法是从主机挂载(以只读模式)用户/组列表到容器,因此在容器启动后它将具有与主机相同的 uid-> 用户名(以及组)匹配。现在,您可以在容器内为您的服务配置用户/组设置,就好像它在您的主机系统上工作一样。

当您决定将容器移动到另一台主机时,您只需将服务配置文件中的用户名更改为您在该主机上拥有的用户名。


这是一个很好的答案,如果您想在基本系统上运行处理文件的容器而不暴露系统的其余部分,这非常简单。
这是我最喜欢的答案。此外,我在 docker run 命令的其他地方看到了类似的建议,您可以通过 -u $( id -u $USER ):$( id -g $USER ) 传入当前的用户名/组,您不再需要担心用户名。这适用于您想要生成默认具有读/写访问权限的文件(例如二进制文件)的本地开发环境。
老实说,这是预期的最终用户行为。发现我的 docker 容器的用户具有主机的 uid/gid,但相同的 uid 不在我在 docker run 命令之前明确设置的其他 gid 中,这让我感到困惑。当 docker 提出他们的默认行为时,没有考虑最终用户
C
Community

好的,现在是 tracked at docker issue #7198

现在,我正在使用您的第二个选项来处理这个问题:

将用户从主机映射到容器

Dockerfile

#=======
# Users
#=======
# TODO: Idk how to fix hardcoding uid & gid, specifics to docker host machine
RUN (adduser --system --uid=1000 --gid=1000 \
        --home /home/myguestuser --shell /bin/bash myguestuser)

命令行界面

# DIR_HOST and DIR_GUEST belongs to uid:gid 1000:1000
docker run -d -v ${DIR_HOST}:${DIR_GUEST} elgalu/myservice:latest

更新我目前更倾向于Hamy answer


使用命令 id -u <username>id -g <username>id -G <username> 来获取特定用户的用户 ID 和组 ID
这破坏了跨主机的容器可移植性。
Docker issue #7198 已经得出结论,他们不会为此实施本机解决方案。在 github.com/docker/docker/issues/7198#issuecomment-230636074 上查看我的评论和回复
A
Anton Krug

我的做法是检测当前的UID/GID,然后在容器内创建这样的用户/组,并在他下面执行脚本。结果,他将创建的所有文件都将匹配主机上的用户:

# get the location of this script no matter what your current folder is, this might break between shells so make sure you run bash
LOCAL_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

# get current IDs
USER_ID=$(id -u)
GROUP_ID=$(id -g)

echo "Mount $LOCAL_DIR into docker, and match the host IDs ($USER_ID:$GROUP_ID) inside the container."

docker run -v $LOCAL_DIR:/host_mount -i debian:9.4-slim bash -c "set -euo pipefail && groupadd -r -g $GROUP_ID lowprivgroup && useradd -u $USER_ID lowprivuser -g $GROUP_ID && cd /host_mount && su -c ./runMyScriptAsRegularUser.sh lowprivuser"

E
Ethan

这是一种仍然使用纯数据容器但不要求它与应用程序容器同步的方法(就具有相同的 uid/gid 而言)。

据推测,您想在容器中以非 root $USER 身份运行某些应用程序,而无需登录 shell。

在 Dockerfile 中:

RUN useradd -s /bin/false myuser

# Set environment variables
ENV VOLUME_ROOT /data
ENV USER myuser

...

ENTRYPOINT ["./entrypoint.sh"]

然后,在 entrypoint.sh 中:

chown -R $USER:$USER $VOLUME_ROOT
su -s /bin/bash - $USER -c "cd $repo/build; $@"

c
czerasz

为了安全和更改 docker 容器的根目录,docker 主机尝试使用 --uidmap--private-uids 选项

https://github.com/docker/docker/pull/4572#issuecomment-38400893

您还可以删除 docker 容器中的多项功能 (--cap-drop) 以确保安全

http://opensource.com/business/14/9/security-for-docker

UPDATE 支持应该在 docker > 1.7.0

更新 版本 1.10.0 (2016-02-04) 添加 --userns-remap 标志 https://github.com/docker/docker/blob/master/CHANGELOG.md#security-2


我正在运行 docker 1.3.2 build 39fa2fa(最新),没有看到 --uidmap--private-uids 选项的痕迹。看起来 PR 没有成功,也没有合并。
它不是在核心中合并,如果你愿意,你可以使用它来打补丁。现在只能限制某些功能并从非 root 用户在容器中运行您的应用程序。
2015 年 6 月,我没有看到它在 docker 1.6.2 中合并,你的答案仍然有效吗?
问题仍然开放。开发者应该在 1.7 版本中添加支持。 (--root 选项)github.com/docker/docker/pull/12648
似乎开发人员再次使用此功能移动了该版本。 Docker 开发者“icecrime”说 "We apparently do have so some of conflicting designs between libnetwork and user namespaces ... and something we'd like to get in for 1.8.0. So don't think we're dropping this, we're definitely going to take a break after all these, and see how we need to reconsider the current design and integration of libnetwork to make this possible. Thanks!" github.com/docker/docker/pull/12648 所以我认为我们应该等待下一个稳定版本。
C
Community

基本图像

使用此图片:https://hub.docker.com/r/reduardo7/docker-host-user

或者

重要提示:这会破坏跨主机的容器可移植性。

1) 初始化.sh

#!/bin/bash

if ! getent passwd $DOCKDEV_USER_NAME > /dev/null
  then
    echo "Creating user $DOCKDEV_USER_NAME:$DOCKDEV_GROUP_NAME"
    groupadd --gid $DOCKDEV_GROUP_ID -r $DOCKDEV_GROUP_NAME
    useradd --system --uid=$DOCKDEV_USER_ID --gid=$DOCKDEV_GROUP_ID \
        --home-dir /home --password $DOCKDEV_USER_NAME $DOCKDEV_USER_NAME
    usermod -a -G sudo $DOCKDEV_USER_NAME
    chown -R $DOCKDEV_USER_NAME:$DOCKDEV_GROUP_NAME /home
  fi

sudo -u $DOCKDEV_USER_NAME bash

2) Dockerfile

FROM ubuntu:latest
# Volumes
    VOLUME ["/home/data"]
# Copy Files
    COPY /home/data/init.sh /home
# Init
    RUN chmod a+x /home/init.sh

3) 运行.sh

#!/bin/bash

DOCKDEV_VARIABLES=(\
  DOCKDEV_USER_NAME=$USERNAME\
  DOCKDEV_USER_ID=$UID\
  DOCKDEV_GROUP_NAME=$(id -g -n $USERNAME)\
  DOCKDEV_GROUP_ID=$(id -g $USERNAME)\
)

cmd="docker run"

if [ ! -z "${DOCKDEV_VARIABLES}" ]; then
  for v in ${DOCKDEV_VARIABLES[@]}; do
    cmd="${cmd} -e ${v}"
  done
fi

# /home/usr/data contains init.sh
$cmd -v /home/usr/data:/home/data -i -t my-image /home/init.sh

4) 使用 docker 构建

4)跑!

sh run.sh

j
johnklawlor

在我的特定情况下,我试图使用节点 docker 映像构建我的节点包,这样我就不必在部署服务器上安装 npm。它运行良好,直到在容器外部和主机上,我尝试将文件移动到节点 docker 映像创建的 node_modules 目录中,但我被拒绝访问该目录,因为它归 root 所有。我意识到我可以通过将目录从容器中复制到主机上来解决这个问题。通过 docker docs...

复制到本地计算机的文件是使用调用 docker cp 命令的用户的 UID:GID 创建的。

这是我用来更改在 docker 容器中创建的目录的所有权的 bash 代码。

NODE_IMAGE=node_builder
docker run -v $(pwd)/build:/build -w="/build" --name $NODE_IMAGE node:6-slim npm i --production
# node_modules is owned by root, so we need to copy it out 
docker cp $NODE_IMAGE:/build/node_modules build/lambda 
# you might have issues trying to remove the directory "node_modules" within the shared volume "build", because it is owned by root, so remove the image and its volumes
docker rm -vf $NODE_IMAGE || true

如果需要,您可以使用第二个 docker 容器删除该目录。

docker run -v $(pwd)/build:/build -w="/build" --name $RMR_IMAGE node:6-slim rm -r node_modules

A
Alexander Mills

要在 docker 主机和 docker 容器之间共享文件夹,请尝试以下命令

docker run -v "$(pwd):$(pwd)" -i -t ubuntu

-v 标志将当前工作目录挂载到容器中。当绑定挂载的卷的主机目录不存在时,Docker会自动为你在主机上创建这个目录,

但是,这里有两个问题:

如果您是非 root 用户,则无法写入挂载的卷,因为共享文件将由主机中的其他用户拥有,您不应在容器内以 root 身份运行该进程,但即使您以某些硬编码用户身份运行它仍然与您的笔记本电脑/Jenkins 上的用户不匹配,

解决方案:

容器:创建一个用户说'testuser',默认用户ID将从1000开始,

主机:创建一个组 id 为 1000 的组,例如“testgroup”,并将目录添加到新组(testgroup


N
Nishant

如果您这样做是为了开发,一个好的解决方案是使用 bindfs

保留容器用户拥有的源代码。 (如果可能,让容器克隆源代码。)使用 bindfs 并为主机用户映射文件夹。

这是我的 docker-compose 设置现在的样子:

project:
  web/src # Container clones it using init scripts.
  web/log
  __web__/src # Host user uses this. It's just bindfs mirror.
  __web__/log

我已经考虑这个问题一年多了,bindfs 是我遇到的最简单的选项。除了克隆之外,没有运行时成本。


R
Renato Alencar

如果您使用 Docker Compose,请以 previleged 模式启动容器:

wordpress:
    image: wordpress:4.5.3
    restart: always
    ports:
      - 8084:80
    privileged: true

这可能会使安装卷更容易,但是.. Wordpress 以特权模式启动?这是一个可怕的想法——要求妥协。 wpvulndb.com/wordpresses/453