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/graphitedata
的Dockerfile
是这样的:
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如何映射到graphite
和graphitetools
容器中定义的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)的所有权,以及从root
到redis
用户的step-down以运行redis-server
。 (如果执行的命令不是redis-server,将直接运行命令。)
这有以下的效果
如果将/ data目录绑定到主机,docker-entrypoint将在redis
用户下运行redis-server之前准备用户权限。
这使您可以轻松地进行零设置,以便在任何卷configuration下运行容器。
当然,如果你需要在不同的映像之间共享卷,你需要确保它们使用相同的userid / groupid,否则最新的容器会劫持前一个用户的权限。
对于大多数情况来说,这可能不是最好的方式,但是它还没有被提及,所以它可能会帮助某人。
-
绑定安装主机卷
Host folder FOOBAR is mounted in container /volume/FOOBAR
-
修改您的容器的启动脚本以查找您感兴趣的卷的GID
$ TARGET_GID=$(stat -c "%g" /volume/FOOBAR)
-
确保你的用户属于这个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)与docker
build立
4)跑!
sh run.sh
要在docker主机和docker容器之间共享文件夹,请尝试下面的命令
$ docker run -v
pwd
:pwd
-i -t ubuntu
-v标志将当前工作目录挂载到容器中。 当绑定挂载卷的主机目录不存在时,Docker会自动在主机上为你创build这个目录,
但是,我们在这里有两个问题:
- 如果您是非root用户,则无法写入挂载的卷,因为共享文件将由主机中的其他用户拥有,
- 你不应该在你的容器中以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