我使用Docker已经有一段时间了,在处理持久数据时不断发现同样的问题。

我创建Dockerfile并公开一个卷或使用——volumes-from在容器中挂载一个主机文件夹。

主机上的共享卷需要申请哪些权限?

我可以想到两个选择:

So far I've given everyone read/write access, so I can write to the folder from the Docker container. Map the users from host into the container, so I can assign more granular permissions. Not sure this is possible though and haven't found much about it. So far, all I can do is run the container as some user: docker run -i -t -user="myuser" postgres, but this user has a different UID than my host myuser, so permissions do not work. Also, I'm unsure if mapping the users will pose some security risks.

还有其他选择吗?

你们是如何处理这个问题的?


当前回答

为了保护和更改docker容器和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)add——users -remap标志 https://github.com/docker/docker/blob/master/CHANGELOG.md#security-2

其他回答

基地图片

使用这个图片:https://hub.docker.com/r/reduardo7/docker-host-user

or

重要:这会破坏容器在主机间的可移植性。

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构建

4)跑!

sh run.sh

如果你这样做是为了开发,一个很好的解决方案是使用bindfs:

保持容器用户拥有源代码。(如果可能的话,让容器克隆源代码。) 使用bindfs并为主机用户映射文件夹。

下面是我的docker-compose设置现在的样子:

project:
  web/src # Container clones it using init scripts.
  web/log
  __web__/src # Host user uses this. It's just bindfs mirror.
  __web__/log

我已经考虑这个问题一年多了,bindfs是我遇到过的最简单的选择。除了克隆,没有运行成本。

在大多数情况下,这可能不是最好的方法,但它还没有被提及,所以也许它会帮助到某些人。

绑定挂载主机卷 主机文件夹FOOBAR挂载在容器/卷/FOOBAR中 修改容器的启动脚本,以找到感兴趣的卷的GID $ TARGET_GID=$(stat -c "%g" /volume/FOOBAR) 确保您的用户属于一个具有此GID的组(您可能必须创建一个新组)。对于本例,我将假装我的软件在容器内以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子句一起使用(除非该用户具有根权限)。此外,它是一种黑客工作;-)

如果你想要硬核,你显然可以在许多方面扩展它——例如,搜索任何子文件上的所有组,多个卷,等等。

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

假设,你想在容器中运行一些应用程序作为非根$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; $@"

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


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

For example, one use case given in the documentation is backing up a data volume. To do this another container is used to do the backup via tar, and it too uses -volumes-from in order to mount the volume. So I think the key point to grok is: rather than thinking about how to get access to the data on the host with the proper permissions, think about how to do whatever you need -- backups, browsing, etc. -- via another container. The containers themselves need to use consistent uid/gids, but they don't need to map to anything on the host, thereby remaining portable.

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

更新:对于注释中的给定用例,您可能有一个image some/graphite来运行graphite,并有一个image some/graphitedata作为数据容器。因此,忽略端口等,image 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"]

构建并创建数据容器:

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容器,在运行时覆盖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中的一些/graphite或一些/ graphitdata使其成为DRY,或者只是重用现有的一个映像(必要时重写entrypoint/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:写完这个答案后,我决定写一篇关于这种方法的更完整的博客文章。我希望这能有所帮助。

UPDATE 3: I corrected this answer and added more specifics. It previously contained some incorrect assumptions about ownership and perms -- the ownership is usually assigned at volume creation time i.e. in the data container, because that is when the volume is created. See this blog. This is not a requirement though -- you can just use the data container as a "reference/handle" and set the ownership/perms in another container via chown in an entrypoint, which ends with gosu to run the command as the correct user. If anyone is interested in this approach, please comment and I can provide links to a sample using this approach.