我一直在玩Docker一段时间,并在处理持久数据时继续发现同样的问题。
我创建了Dockerfile
和expose a volume或使用--volumes-from
来mount a host folder inside my container。
我应该对主机上的共享卷应用哪些权限?
我可以想到两个选择:
到目前为止,我已经给予每个人读/写访问权限,因此我可以从Docker容器中写入该文件夹。
将用户从主机映射到容器,这样我就可以分配更细化的权限。不确定这是否可行,但并未发现它有多少。到目前为止,我所能做的就是以某个用户身份运行容器:docker run -i -t -user="myuser" postgres
,但此用户的UID与我的主机myuser
不同,因此权限不起作用。此外,我不确定映射用户是否会带来一些安全风险。
还有其他选择吗?
你们/ gals如何处理这个问题?
答案 0 :(得分:160)
更新2016-03-02 :截至Docker 1.9.0,Docker的named volumes为replace data-only containers。下面的答案,以及我链接的博客文章,仍然具有如何考虑docker 中的数据的意义,但考虑使用命名卷来实现下面描述的模式而不是数据容器。 / p>
我认为解决这个问题的规范方法是使用data-only containers。使用这种方法,所有对卷数据的访问都是通过使用-volumes-from
数据容器的容器进行的,因此主机uid / gid并不重要。
例如,文档中给出的一个用例是备份数据卷。为此,使用另一个容器通过tar
进行备份,并且它也使用-volumes-from
来安装卷。因此,我认为grok的关键点在于:而不是考虑如何使用适当的权限访问主机上的数据,考虑如何通过另一个容器执行任何操作(备份,浏览等) 。容器本身需要使用一致的uid / gids,但它们不需要映射到主机上的任何东西,从而保持可移植性。
这对我来说也是比较新的,但如果您有特定用例,请随时发表评论,我会尝试扩展答案。
更新:对于评论中的给定用例,您可能拥有运行石墨的图像some/graphite
和图像some/graphitedata
作为数据容器。因此,忽略端口等,图像Dockerfile
的{{1}}类似于:
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
Dockerfile也应该获得相同的uid / gids,因此它可能看起来像这样:
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
# ... graphite installation ...
VOLUME /data/graphite
USER graphite
CMD ["/bin/graphite"]
好的,现在它为我们提供了石墨容器和相关的仅数据容器以及正确的用户/组(请注意,您也可以重新使用数据容器的docker run --volumes-from=graphitedata some/graphite
容器,覆盖入口/ cmd在运行时,将它们作为单独的图像IMO更清晰。)
现在,假设您要编辑数据文件夹中的内容。因此,不是将卷装入主机并在那里进行编辑,而是创建一个新的容器来完成这项工作。让我们称之为some/graphite
。还可以创建适当的用户/组,就像some/graphitetools
图像一样。
some/graphite
您可以通过继承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
VOLUME /data/graphite
USER graphite
CMD ["/bin/bash"]
或some/graphite
来创建此DRY,或者不是创建新映像而只是重新使用其中一个(根据需要覆盖入口点/ cmd)
现在,您只需运行:
some/graphitedata
然后docker run -ti --rm --volumes-from=graphitedata some/graphitetools
。这非常有效,因为所有容器都具有相同的石墨用户和匹配的uid / gid。
由于您从未在主机上挂载vi /data/graphite/whatever.txt
,因此您并不关心主机uid / gid如何映射到/data/graphite
和graphite
容器内定义的uid / gid 。这些容器现在可以部署到任何主机,它们将继续完美地工作。
关于这一点的巧妙之处在于graphitetools
可以拥有各种有用的实用程序和脚本,您现在也可以以可移植的方式进行部署。
更新2 :写完这个答案之后,我决定写一篇关于这种方法的more complete blog post。我希望它有所帮助。
更新3 :我更正了此答案并添加了更多细节。它之前包含一些关于所有权和权限的错误假设 - 所有权通常在卷创建时分配,即在数据容器中分配,因为这是在创建卷时。见this blog。这不是一个要求 - 你可以只使用数据容器作为"参考/句柄"并通过入口点中的chown在另一个容器中设置所有权/权限,该入口点以gosu结束,以便以正确的用户身份运行该命令。如果有人对这种方法感兴趣,请发表评论,我可以使用这种方法提供样本的链接。
答案 1 :(得分:46)
官方redis image以及所有官方图片中都可以看到一个非常优雅的解决方案。
逐步说明:
如Dockerfile评论所示:
首先添加我们的用户和组,以确保一致地分配他们的ID,无论添加什么依赖项
gosu是su
/ sudo
的替代方案,可以让root用户轻松降级。 (Redis始终使用redis
用户运行)
/data
卷并将其设置为workdir 通过使用VOLUME /data
命令配置/ data卷,我们现在有一个单独的卷,可以是docker卷,也可以绑定挂载到主机目录。
将其配置为workdir(WORKDIR /data
)使其成为执行命令的默认目录。
这意味着所有容器执行都将通过docker-entrypoint脚本运行,默认情况下,要运行的命令是redis-server。
docker-entrypoint
是一个执行简单功能的脚本:更改当前目录(/ data)的所有权,并从root
逐步降级为redis
用户以运行redis-server
。 (如果执行的命令不是redis-server,它将直接运行命令。)
具有以下效果
如果/ data目录已绑定挂载到主机,则docker-entrypoint将在redis
用户下运行redis-server之前准备用户权限。
这使您可以轻松地设置零设置,以便在任何卷配置下运行容器。
当然,如果您需要在不同图像之间共享卷,则需要确保它们使用相同的userid / groupid,否则最新容器将劫持前一个容器的用户权限。
答案 2 :(得分:28)
对于大多数情况来说,这可能不是最好的方式,但它还没有被提及,所以它可能会帮助某人。
绑定装载主机卷
Host folder FOOBAR is mounted in container /volume/FOOBAR
修改容器的启动脚本以查找您感兴趣的卷的GID
$ TARGET_GID=$(stat -c "%g" /volume/FOOBAR)
确保您的用户属于具有此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。它不能与Dockerfile中的USER
子句一起使用(除非该用户具有我认为的root权限)。此外,它尖叫黑客工作; - )
如果你想成为硬核,你显然可以通过多种方式扩展它 - 例如搜索任何子文件,多个卷等的所有组。
答案 3 :(得分:15)
好的,现在是tracked at docker issue #7198
目前,我正在使用您的第二个选项处理此问题:
将用户从主机映射到容器
#=======
# 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
答案 4 :(得分:11)
尝试向Dockerfile添加命令
RUN usermod -u 1000 www-data
积分转到https://github.com/denderello/symfony-docker-example/issues/2#issuecomment-94387272
答案 5 :(得分: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
.....
这是我的docker-compose.yml的摘录。
这个想法是从主机到容器挂载(以只读模式)用户/组列表,因此在容器启动后它将具有相同的uid->用户名(以及用于组)匹配主人。现在,您可以在容器内配置服务的用户/组设置,就像它在主机系统上工作一样。
当您决定将容器移动到另一台主机时,您只需将服务配置文件中的用户名更改为该主机上的用户名。
答案 6 :(得分: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; $@"
答案 7 :(得分:4)
对于docker容器的安全和更改root,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
更新支持应该在docker > 1.7.0
更新版本1.10.0
(2016-02-04)添加--userns-remap
标志
https://github.com/docker/docker/blob/master/CHANGELOG.md#security-2
答案 8 :(得分:3)
使用此图片:https://hub.docker.com/r/reduardo7/docker-host-user
重要提示:会破坏主机间的容器可移植性。
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
Dockerfile
FROM ubuntu:latest
# Volumes
VOLUME ["/home/data"]
# Copy Files
COPY /home/data/init.sh /home
# Init
RUN chmod a+x /home/init.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
docker
sh run.sh
答案 9 :(得分:2)
我的方法是检测当前的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"
答案 10 :(得分:0)
要在泊坞主机和泊坞窗容器之间共享文件夹,请尝试以下命令
$ docker run -v“$(pwd):$(pwd)” - i -t ubuntu
-v标志将当前工作目录挂载到容器中。当装载装入卷的主机目录不存在时,Docker将自动为您创建此目录,
但是,我们遇到了两个问题:
<强>解决方案:强>
容器: 创建用户说'testuser',默认情况下用户ID将从1000开始,
主机: 创建一个组名为'testgroup',组ID为1000,并将目录chown到新组(testgroup
答案 11 :(得分:0)
在我的具体情况下,我试图使用节点docker镜像构建我的节点包,这样我就不必在部署服务器上安装npm了。它运行良好,直到容器外部和主机上,我试图将文件移动到节点docker映像创建的node_modules目录中,因为它归root所有,我被拒绝了。我意识到我可以通过将容器中的目录复制到主机上来解决这个问题。通过docker docs ......
使用UID:GID创建复制到本地计算机的文件 用户调用了docker cp命令。
这是我用来更改由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
答案 12 :(得分:-3)
如果您使用Docker Compose,请以previleged模式启动容器:
wordpress:
image: wordpress:4.5.3
restart: always
ports:
- 8084:80
privileged: true