我使用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.

还有其他选择吗?

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


当前回答

更新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.

其他回答

一个非常优雅的解决方案可以在官方的redis图像和一般的所有官方图像中看到。

详细描述:

首先创建redis用户/组

如Dockerfile评论所示:

首先添加我们的用户和组,以确保他们的id被一致分配,不管添加什么依赖项

使用Dockerfile安装gosu

Gosu是su / sudo的替代品,便于从根用户降级。(Redis总是与Redis用户一起运行)

配置/data卷,设置为workdir

通过使用volume /data命令配置/data卷,我们现在有了一个单独的卷,它可以是docker卷,也可以绑定挂载到主机目录。

将其配置为工作目录(workdir /data)将使其成为执行命令的默认目录。

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

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

docker-entrypoint是一个脚本,它做了一个简单的功能:改变当前目录(/data)的所有权,并从根用户降级到redis用户以运行redis-server。(如果执行的命令不是redis-server,则直接执行该命令。)

这有以下影响

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

这使您可以轻松地在任何卷配置下运行容器,无需进行任何设置。

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

为了保护和更改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

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

$(pwd):$(pwd)-i -t ubuntu

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

然而,这里有两个问题:

如果您不是root用户,则无法对挂载的卷进行写操作,因为共享文件将由主机中的其他用户拥有。 你不应该以root用户在容器中运行进程,但即使你以硬编码用户运行,它仍然不会与你笔记本电脑上的用户匹配。

解决方案:

容器: 创建一个用户testuser,默认用户id从1000开始,

主持人: 创建一个名为“testgroup”的组,组id为1000,并将目录更改为新组(testgroup . testgroup . testgroup)

如果你使用Docker Compose,以特权模式启动容器:

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