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


345

我已经和Docker玩了一段时间,在处理持久性数据时继续寻找相同的问题。

我创建我的文件Dockerfile公开一个卷,或使用--volumes-from在容器中安装主机文件夹

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

我可以想到两种选择:

  • 到目前为止,我已经授予所有人读取/写入访问权限,因此我可以从Docker容器写入该文件夹。

  • 将用户从主机映射到容器,以便我可以分配更多的细化权限。不确定这是否可能,但尚未找到很多相关信息。到目前为止,我所能做的就是以某些用户身份运行容器:docker run -i -t -user="myuser" postgres,但是该用户的UID与主机不同myuser,因此权限不起作用。另外,我不确定映射用户是否会带来一些安全风险。

还有其他选择吗?

你们如何处理这个问题?



1
您可能也对该主题进行了详细讨论的线程感兴趣:groups.google.com/forum
#!msg/docker


目前,Docker团队尚未计划实施本机解决方案,以将主机目录作为具有指定uid / gid的卷挂载。请参阅我对这个问题的评论和回复:github.com/docker/docker/issues/7198#issuecomment-230636074
Quinn Comendant

Answers:


168

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


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

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

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

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

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/graphiteDockerfile也应该得到相同的UID /导报,因此它可能是这个样子:

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在Dockerfile中进行此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:写完这个答案后,我决定写一篇关于此方法的更完整的博客文章。希望对您有所帮助。

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


35
恐怕这不是解决方案,因为使用纯数据容器会遇到同样的问题。最终,这些容器将使用主机共享的卷,因此您仍然需要管理这些共享文件夹的权限。
Xabs

2
请记住,我可能需要从主机上编辑数据文件夹(即:删除测试石墨密钥,删除JIRA测试主文件夹或使用最新的生产备份进行更新...)。据您的评论所知,我应该做一些事情,例如通过第三个容器更新JIRA数据。无论如何,您将对新数据文件夹应用什么权限/data/newcontainer?我假设您docker是以root用户身份运行的(是否可以不这样做?)此外,如果将数据直接安装在主容器中或通过仅数据容器安装,则这些权限是否存在任何差异?
Xabs 2014年

2
感谢您的精心答复。只要有机会,我就会进行测试。另外,nice既引用了您的博客文章,也引用了有关将最小图像用作数据容器的文章
Xabs 2014年

3
这种方法的唯一问题是非常容易错误地删除容器。想象一下它是否恰好是您的数据容器。我认为(CMIIW)数据仍将存在于/var/lib/docker某个地方,但仍然会非常痛苦
lolski 2014年

3
“您可以仅将数据容器用作“引用/句柄”,并通过入口处的chown在另一个容器中设置所有权/权限”……@Raman:这是在遇到了许多权限问题之后最终救了我的部分想通了。使用入口点脚本并在其中设置权限对我有用。感谢您的详尽解释。这是迄今为止我在网上找到的最好的。
Vanderstaaij

59

可以在官方Redis图像上以及通常在所有官方图像中看到一个非常优雅的解决方案。

分步介绍:

  • 首先创建Redis用户/组

如Dockerfile注释所示:

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

gosu是su/ 的替代方案,sudo用于从根用户轻松降级。(Redis始终与redis用户一起运行)

  • 配置/data音量并将其设置为workdir

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

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

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

这意味着所有容器执行将通过docker-entrypoint脚本运行,并且默认情况下要运行的命令是redis-server。

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

这具有以下效果

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

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

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


11
接受的答案是有益的,但是它使我沮丧了整整一个星期,无奈地找到了可以找到解决问题的规范方法的答案。
m0meni

2
对于我的问题,此答案也更有帮助。
丹尼尔(Daniel)


所以?如何使泊坞窗内的卷可写?chown它在ENTRYPOINT脚本里面?
Gherman

34

可以说这不是大多数情况下的最佳方法,但是尚未提及,因此也许会对某人有所帮助。

  1. 绑定挂载主机卷

    Host folder FOOBAR is mounted in container /volume/FOOBAR

  2. 修改容器的启动脚本以找到您感兴趣的卷的GID

    $ TARGET_GID=$(stat -c "%g" /volume/FOOBAR)

  3. 确保您的用户属于具有此GID的组(您可能必须创建一个新组)。在本例中,我假设我的软件nobody在容器中时以用户身份运行,因此我想确保其nobody所属组的组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的容器内的任意组中没有危险。它不能与USERDockerfile中的子句一起使用(除非我认为该用户具有root特权)。此外,它还大叫黑客工作;-)

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


4
这是否旨在从已装载的卷中读取文件?我正在寻找一种写文件的解决方案,而不必由创建Docker容器的其他用户拥有这些文件。
ThorSummoner

从15年8月开始我就使用这种方法。一切都还好。只是在容器内创建的文件的权限是不同的。用户(容器内部和外部)都拥有文件的所有权,但是他们都具有读取权限,因为他们确实属于此解决方案创建的同一组。当用例强加了对通用文件的写访问权时,问题就开始了。更大的问题是共享卷具有git文件(它是在同一生产环境中测试开发源文件的卷)。Git开始警告共享代码的访问问题。
yucer

我想一个更好的grep命令$TARGET_GID将使用grep ':$TARGET_GID:',否则如果容器有,如GID 10001和你的主机是1000,这个检查会通过,但它不应该。
robhudson

16

好的,现在 在docker问题#7198上跟踪

目前,我正在使用您的第二个选项来处理此问题:

将用户从主机映射到容器

Docker文件

#=======
# 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

更新我目前更倾向于哈米 答案


1
使用命令id -u <username>id -g <username>id -G <username>获取特定用户的用户ID和组ID,而不是
lolski


15
这破坏了跨主机的容器可移植性。
拉曼

2
Docker问题#7198已经得出结论,他们将不会为此实现本地解决方案。请参阅我的评论github.com/docker/docker/issues/7198#issuecomment-230636074
Quinn Comendant


12

和您一样,我一直在寻找一种将用户/组从主机映射到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-> username(以及组名)匹配。现在,您可以在容器内配置服务的用户/组设置,就好像它在主机系统上正常工作一样。

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


如果您要在基本系统上运行处理文件的容器,而又不暴露系统的其余部分,那么这是一个很好的答案,非常简单。
icarito

这是我最喜欢的答案。另外,在docker run命令的其他地方,我也看到了类似的建议,您可以通过该命令传入当前的用户名/组,-u $( id -u $USER ):$( id -g $USER )而不必担心用户名。这对于您要生成默认情况下具有读/写访问权限的文件(例如,二进制文件)的本地开发环境非常有效。
matthewcummings516

5

这是一种仍使用仅数据容器但不要求将其与应用程序容器同步的方法(就相同的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; $@"

5

为了安全并更改Docker容器的root用户,请尝试使用docker host --uidmap--private-uidsoptions

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

另外,--cap-drop为了安全起见,您可能会删除Docker容器中的多个功能()

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

更新支持应该进来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(latest),没有看到no --uidmap--private-uidsoptions的痕迹。看起来PR没做到,也没有合并。
Leo Gallucci 2014年

它不是核心合并,如果您愿意,可以使用它进行补丁。现在只能限制某些功能,并以非root用户身份在容器中运行应用程序。
umount 2014年

2015年6月,我看不到这在docker 1.6.2中合并了,您的答案仍然有效吗?
Leo Gallucci 2015年

1
问题仍未解决。开发人员应在1.7版本中添加支持。(--root选项) github.com/docker/docker/pull/12648
卸载

2
开发人员似乎再次使用此功能移动了发行版。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 所以我认为我们应该等待下一个稳定版本。
umount

4

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

# get 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"

3

基本图片

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

要么

重要提示:这破坏了容器在主机之间的可移植性

1) init.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)运行

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

0

在我的特定情况下,我尝试使用节点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

0

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

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

-v标志将当前工作目录安装到容器中。当绑定安装的卷的主机目录不存在时,Docker会自动在主机上为您创建此目录,

但是,这里有两个问题:

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

解:

容器:创建一个用户说“ testuser”,默认情况下,用户ID将从1000开始,

主持人:创建一个名为“ testgroup”的组(组ID为1000),并将目录穿入新组(testgroup)


-5

如果使用Docker Compose,请以预授权模式启动容器:

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

2
这可能使挂载卷变得更容易,但是.. Wordpress是否以特权模式启动?那是一个可怕的想法-要求妥协。 wpvulndb.com/wordpresses/453
科林·哈灵顿
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.