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

我一直在玩泊坞坞一段时间,并在处理持久性数据时不断发现同样的问题。

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

我的问题是,我应该对主机上的共享卷应用什么权限? 我可以想到2个选项:

  • 到目前为止,我一直在做的,给每个人读/写访问,所以我可以从docker容器写入文件夹

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

任何其他的select?

你们怎么处理这个问题?

更新2016-03-02 :从Docker 1.9.0开始,Docker已经命名了用于replace纯数据容器的卷 。 下面的答案,以及我的链接的博客post,仍然有价值的意义在于如何考虑泊坞窗内的数据,但考虑使用命名卷来实现下面描述的模式,而不是数据容器。


我相信解决这个问题的标准方法是使用纯数据容器 。 使用这种方法,所有对卷数据的访问都是通过使用-volumes-from容器-volumes-from数据容器访问的,所以host uid / gid无关紧要。

例如,文档中给出的一个用例是备份数据卷。 要做到这一点,另一个容器被用来通过tar进行备份,而且它也使用-volumes-from来装入卷。 所以我认为最重要的一点是:不要考虑如何以适当的权限访问主机上的数据,而是考虑如何通过另一个容器来执行备份,浏览等操作。 容器本身需要使用一致的uid / gid,但是它们不需要映射到主机上的任何东西,从而保持便携。

这对我来说也是比较新的,但是如果你有一个特殊的用例,可以随意发表评论,我会尽量扩大答案。

更新 :对于给定的使用情况下的评论,你可能有一个图像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"] 

构build并创build数据容器:

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

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

 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容器,在运行时覆盖input/ cmd)让他们作为独立的图像IMO更清晰)。

现在,让我们说你想编辑数据文件夹中的东西。 因此,不是将卷挂载到主机上,而是在那里编辑,创build一个新的容器来完成这项工作。 让我们叫它some/graphitetools 。 让我们也创build适当的用户/组,就像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中inheritancesome/graphite或者some/graphitedata来做到这一点,或者不用创build一个新的镜像,而是重新使用现有的镜像(根据需要重写入口点/ 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 :我更正了这个答案,并添加了更多的细节。 它之前包含了关于所有权和权限的一些不正确的假设 – 所有权通常在创build卷时(即在数据容器中)被分配,因为创build卷时。 看到这个博客 。 这不是要求 – 你可以使用数据容器作为“引用/句柄”,并通过chown在一个入口点中设置所有权/另一个容器,以gosu结束,以正确的用户身份运行该命令。 如果有人对这种方法感兴趣,请评论,我可以提供使用这种方法的样本的链接。

一个非常优雅的解决scheme可以在正式的redis映像上看到,一般在所有的官方映像中都可以看到。

一步一步描述stream程:

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

正如在Dockerfile中所看到的那样:

首先添加我们的用户和组,确保他们的ID得到一致的分配,不pipe依赖关系是否被添加

  • 用Dockerfile安装gosu

gosu是su / sudo一个替代scheme,可以轻松从根用户下载。 (Redis总是使用redis用户运行)

  • configuration/data卷并将其设置为workdir

通过使用VOLUME /data命令configuration/ data卷,我们现在有一个单独的卷,可以是docker卷或bind-mounted到主机目录。

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

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

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

docker-entrypoint是一个执行简单function的脚本:更改当前目录(/ data)的所有权,以及从rootredis用户的step-down以运行redis-server 。 (如果执行的命令不是redis-server,将直接运行命令。)

这有以下的效果

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

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

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

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

  1. 绑定安装主机卷

    Host folder FOOBAR is mounted in container /volume/FOOBAR

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

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

  3. 确保你的用户属于这个GID组(你可能需要创build一个新的组)。 对于这个例子,我假装我的软件在容器内部作为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。 它不能与Dockerfile中的USER子句一起使用(除非该用户具有我认为的root权限)。 另外,它尖叫黑客的工作;-)

如果你想要硬核,你可以用很多方式来扩展它 – 例如search任何子文件,多个卷的所有组。

好的,现在正在docker问题#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) 

CLI

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

更新我目前更倾向于哈密​​的 回答

尝试将一个命令添加到Dockerfile

 RUN usermod -u 1000 www-data 

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

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

据推测,你想在容器中运行一个应用程序作为一个非root用户$ USER而没有loginshell。

在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; $@" 

为了安全并更改--uidmap容器的根目录,请尝试使用--uidmap--private-uids选项

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

您也可以删除--cap-drop容器中的几个function( --cap-drop )以获得安全性

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

更新支持应该在docker > 1.7.0

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

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

  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->用户名(以及组)。 现在,您可以在容器内为您的服务configuration用户/组设置,就好像它在您的主机系统上一样。

当你决定将你的容器移动到另一个主机时,你只需要在服务configuration文件中将用户名改成你在主机上的名字。

基本图像

使用此图像: 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)run.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)与dockerbuild立

4)跑!

 sh run.sh 

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

$ docker run -v pwdpwd -i -t ubuntu

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

但是,我们在这里有两个问题:

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

解:

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

主机:创build一个组,并将组名称为'testgroup',组名为1000,并将该目录设置为新组(testgroup

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

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