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

Xab*_*abs 328 docker

我一直在玩Docker一段时间,并在处理持久数据时继续寻找相同的问题.

创建我Dockerfile暴露量或使用--volumes-from安装我的容器内的主文件夹.

我应该对主机上的共享卷应用哪些权限?

我可以想到两个选择:

  • 到目前为止,我已经给了每个人读/写访问权限,所以我可以从Docker容器写入该文件夹.

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

还有其他选择吗?

你们这些人/女士如何应对这个问题?

Ram*_*man 166

更新2016年3月2日:由于码头工人1.9.0,泊坞窗已命名的体积,其替代数据只集装箱.下面的答案以及我的链接博客文章仍然具有如何考虑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"]
Run Code Online (Sandbox Code Playgroud)

构建并创建数据容器:

docker build -t some/graphitedata Dockerfile
docker run --name graphitedata some/graphitedata
Run Code Online (Sandbox Code Playgroud)

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"]
Run Code Online (Sandbox Code Playgroud)

它将运行如下:

docker run --volumes-from=graphitedata some/graphite
Run Code Online (Sandbox Code Playgroud)

好的,现在这给了我们石墨容器和相关的仅数据容器以及正确的用户/组(请注意,您也可以重新使用some/graphite数据容器的容器,在运行时覆盖入口/ 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"]
Run Code Online (Sandbox Code Playgroud)

您可以通过继承Dockerfile some/graphitesome/graphitedata在Dockerfile中创建此DRY ,或者不是创建新映像而只是重用其中一个(根据需要覆盖入口点/ cmd).

现在,您只需运行:

docker run -ti --rm --volumes-from=graphitedata some/graphitetools
Run Code Online (Sandbox Code Playgroud)

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

因为你永远不安装/data/graphite由主机,你不关心如何主机UID/GID映射到UID/GID里面的定义graphitegraphitetools容器.这些容器现在可以部署到任何主机,它们将继续完美地工作.

关于这一点的巧妙之处在于它graphitetools可以具有各种有用的实用程序和脚本,您现在也可以以可移植的方式进行部署.

更新2:写完这个答案之后,我决定写一篇关于这种方法的更完整的博客文章.我希望它有所帮助.

更新3:我更正了这个答案并添加了更多细节.它之前包含一些关于所有权和权限的错误假设 - 所有权通常在卷创建时指定,即在数据容器中,因为这是在创建卷时.看到这个博客.这不是一个要求 - 您可以只使用数据容器作为"引用/句柄",并通过入口点中的chown在另一个容器中设置所有权/权限,以gosu结束以将命令作为正确的用户运行.如果有人对此方法感兴趣,请发表评论,我可以使用此方法提供样本的链接.

  • 我担心这不是一个解决方案,因为对于仅数据容器你会遇到同样的问题.在一天结束时,此容器将使用从主机共享的卷,因此您仍需要管理这些共享文件夹的权限. (34认同)
  • 这种方法的唯一问题是它很容易错误地删除容器.想象一下,如果它碰巧是你的数据容器.我认为(CMIIW)数据仍然会在某处的`/ var/lib/docker`中,但仍然是一个巨大的痛苦 (3认同)
  • "你可以只使用数据容器作为"引用/句柄",并通过入口点中的chown在另一个容器中设置所有权/权限"... @Raman:这是在有许多权限问题之后最终保存我的部分想通了.使用入口点脚本并在此设置权限对我来说.谢谢你的精心解释.这是我到目前为止在网上找到的最好的. (3认同)
  • 请记住,我可能需要从主机编辑数据文件夹(即:删除测试石墨键,删除我的JIRA测试主文件夹或使用最新的生产备份更新它...).据我的评论,我应该做的事情就是通过第三个容器来更新JIRA数据.在任何情况下,您将什么权限应用于新的数据文件夹`/ data/newcontainer`?我假设您以root身份运行`docker`(是否有可能不这样做?)另外,如果数据直接安装在主容器中或通过_data-only container_,这些权限是否有任何区别? (2认同)
  • 谢谢你的精心回复.一旦我有机会,我会尽快测试.另外,很好地引用了[你的博客](https://medium.com/@ramangupta/why-docker-data-containers-are-good-589b3c6c749e)和关于[使用数据容器的最小图像]的文章(http) ://container42.com/2014/11/18/data-only-container-madness/). (2认同)
  • 虽然这可能是生产环境的最佳解决方案,但如果容器用于开发,您需要通过主机内部的 IDE 编辑代码,并在调用容器时立即查看结果。 (2认同)

小智 48

在官方redis图像上以及所有官方图像中都可以看到非常优雅的解决方案.

逐步说明过程:

  • 在其他任何事情之前创建redis用户/组

如Dockerfile评论所示:

首先添加我们的用户和组,以确保一致地分配他们的ID,无论添加什么依赖项

古薮是的替代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)的所有权并逐步降低rootredis用户运行redis-server.(如果执行的命令不是redis-server,它将直接运行命令.)

这具有以下效果

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

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

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

  • 接受的答案是提供信息的,但它只是让我失去了一周的挫折感,有权找到这个实际上提供解决问题的规范方法的答案. (10认同)
  • 这里也有很好的解释:https://denibertovic.com/posts/handling-permissions-with-docker-volumes/ (4认同)
  • 对于我的问题,这个答案也更有帮助. (2认同)

Ham*_*amy 29

对于大多数情况来说,这可能不是最好的方式,但它还没有被提及,所以它可能对某人有所帮助.

  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
Run Code Online (Sandbox Code Playgroud)

我喜欢这个,因为我可以轻松修改主机卷上的组权限,并知道这些更新的权限适用于docker容器.没有对我的主机文件夹/文件的任何许可或所有权修改就会发生这种情况,这让我很开心.

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

如果你想成为硬核,你显然可以通过多种方式扩展它 - 例如搜索任何子文件,多个卷等的所有组.

  • 这是针对挂载卷中的_reading_文件吗?我正在寻找一种编写文件的解决方案,而不是由创建docker容器的用户拥有它们. (4认同)

Leo*_*cci 15

好的,现在正在码头问题#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)
Run Code Online (Sandbox Code Playgroud)

CLI

# DIR_HOST and DIR_GUEST belongs to uid:gid 1000:1000
docker run -d -v ${DIR_HOST}:${DIR_GUEST} elgalu/myservice:latest
Run Code Online (Sandbox Code Playgroud)

更新我目前更倾向于Hamy 回答

  • 这会破坏跨主机的容器可移植性. (15认同)
  • Docker问题#7198已经得出结论,他们将不会为此实现本地解决方案。看到我的评论在https://github.com/docker/docker/issues/7198#issuecomment-230636074的回复 (2认同)

Ale*_*kov 7

和你一样,我一直在寻找一种方法将用户/组从主机映射到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
        .....
Run Code Online (Sandbox Code Playgroud)

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

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

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


umo*_*unt 5

为了保护和更改 docker 容器的根,docker 主机尝试使用--uidmap--private-uids选项

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 开发人员“icecrime”说`“我们显然在 libnetwork 和用户命名空间之间确实存在一些相互冲突的设计......以及我们希望在 1.8.0 中加入的东西。所以不要认为我们正在放弃这个,在完成所有这些之后,我们肯定会休息一下,看看我们需要如何重新考虑当前的 libnetwork 设计和集成以使其成为可能。谢谢!”` https://github.com/docker/docker/pull/ 12648 所以我认为我们应该等待下一个稳定版本。 (2认同)

Eth*_*han 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"]
Run Code Online (Sandbox Code Playgroud)

然后,在entrypoint.sh中:

chown -R $USER:$USER $VOLUME_ROOT
su -s /bin/bash - $USER -c "cd $repo/build; $@"
Run Code Online (Sandbox Code Playgroud)


mun*_*764 5

我的做法是检测当前的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"
Run Code Online (Sandbox Code Playgroud)