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

本文讨论了如何管理Docker共享卷的权限问题,包括两种常见的处理方式:为所有人提供读写权限和将主机用户映射到容器。提到了Docker 1.9.0及以后版本的命名卷,以及使用数据容器来处理权限问题,强调了容器内用户和组的一致性。还提到了使用gosu和修改容器启动脚本的方法,以及官方redis图像的优雅解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文翻译自:What is the (best) way to manage permissions for Docker shared volumes?

I've been playing around with Docker for a while and keep on finding the same issue when dealing with persistent data. 我一直在玩Docker一段时间,并在处理持久数据时继续寻找相同的问题。

I create my Dockerfile and expose a volume or use --volumes-from to mount a host folder inside my container . 我创建我的Dockerfile公开一个卷或使用--volumes-from在我的容器中安装一个主机文件夹

What permissions should I apply to the shared volume on the host? 我应该对主机上的共享卷应用哪些权限?

I can think of two options: 我可以想到两个选择:

  • So far I've given everyone read/write access, so I can write to the folder from the Docker container. 到目前为止,我已经给了每个人读/写访问权限,所以我可以从Docker容器写入该文件夹。

  • 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. 到目前为止,我所能做的只是以某个用户身份运行容器: docker run -i -t -user="myuser" postgres ,但此用户的UID与我的主机myuser ,因此权限不起作用。 Also, I'm unsure if mapping the users will pose some security risks. 此外,我不确定映射用户是否会带来一些安全风险。

Are there other alternatives? 还有其他选择吗?

How are you guys/gals dealing with this issue? 你们这些人/女士如何应对这个问题?


#1楼

参考:https://stackoom.com/question/1amwU/管理Docker共享卷权限的-最佳-方法是什么


#2楼

Ok, this is now being tracked at docker issue #7198 好的,现在正在码头问题#7198上进行跟踪

For now, I'm dealing with this using your second option: 现在,我正在使用你的第二个选项处理这个问题:

Map the users from host into the container 将用户从主机映射到容器

Dockerfile 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 CLI

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

UPDATE I'm currently more inclined to Hamy answer 更新我目前更倾向于Hamy 回答


#3楼

UPDATE 2016-03-02 : As of Docker 1.9.0, Docker has named volumes which replace data-only containers . 更新2016-03-02 :从Docker 1.9.0开始,Docker已经命名了 替换仅数据容器的卷 The answer below, as well as my linked blog post, still has value in the sense of how to think about data inside docker but consider using named volumes to implement the pattern described below rather than data containers. 下面的答案以及我链接的博客文章仍然具有如何考虑docker中数据的意义,但考虑使用命名卷来实现下面描述的模式而不是数据容器。


I believe the canonical way to solve this is by using data-only containers . 我相信解决这个问题的规范方法是使用仅限数据的容器 With this approach, all access to the volume data is via containers that use -volumes-from the data container, so the host uid/gid doesn't matter. 使用这种方法,所有对卷数据的访问都是通过数据容器中使用-volumes-from容器进行的,因此主机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. 为此,使用另一个容器通过tar进行备份,并且它也使用-volumes-from来安装卷。 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. 因此,我认为grok的关键点在于:而不是考虑如何使用适当的权限访问主机上的数据,考虑如何通过另一个容器执行任何操作(备份,浏览等) 。 The containers themselves need to use consistent uid/gids, but they don't need to map to anything on the host, thereby remaining portable. 容器本身需要使用一致的uid / gids,但它们不需要映射到主机上的任何东西,从而保持可移植性。

This is relatively new for me as well but if you have a particular use case feel free to comment and I'll try to expand on the answer. 这对我来说也是相对较新的,但如果您有特定用例,请随时发表评论,我会尝试扩展答案。

UPDATE : For the given use case in the comments, you might have an image some/graphite to run graphite, and an image some/graphitedata as the data container. 更新 :对于注释中给定的用例,您可能有一个图像some/graphite来运行石墨,而图像some/graphitedata作为数据容器。 So, ignoring ports and such, the Dockerfile of image some/graphitedata is something like: 所以,忽略端口等,图像的Dockerfile 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"]

Build and create the data container: 构建并创建数据容器:

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

The some/graphite Dockerfile should also get the same uid/gids, therefore it might look something like this: some/graphite Dockerfile也应该获得相同的uid / gids,因此它可能看起来像这样:

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"]

And it would be run as follows: 它将运行如下:

docker run --volumes-from=graphitedata some/graphite

Ok, now that gives us our graphite container and associated data-only container with the correct user/group (note you could re-use the some/graphite container for the data container as well, overriding the entrypoing/cmd when running it, but having them as separate images IMO is clearer). 好的,现在这给了我们石墨容器和相关的仅数据容器以及正确的用户/组(请注意,您可以重新使用数据容器的some/graphite容器,在运行时覆盖入口/ cmd,但是将它们作为单独的图像IMO更清晰)。

Now, lets say you want to edit something in the data folder. 现在,假设您要编辑数据文件夹中的某些内容。 So rather than bind mounting the volume to the host and editing it there, create a new container to do that job. 因此,不是将卷装入主机并在那里进行编辑,而是创建一个新的容器来完成这项工作。 Lets call it some/graphitetools . 让我们称之为some/graphitetools Lets also create the appropriate user/group, just like the some/graphite image. 还可以创建适当的用户/组,就像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"]

You could make this DRY by inheriting from some/graphite or some/graphitedata in the Dockerfile, or instead of creating a new image just re-use one of the existing ones (overriding entrypoint/cmd as necessary). 您可以通过继承Dockerfile中的some/graphitesome/graphitedata来创建此DRY,或者不是创建新映像而只是重用其中一个(根据需要覆盖入口点/ cmd)。

Now, you simply run: 现在,您只需运行:

docker run -ti --rm --volumes-from=graphitedata some/graphitetools

and then vi /data/graphite/whatever.txt . 然后是vi /data/graphite/whatever.txt This works perfectly because all the containers have the same graphite user with matching uid/gid. 这非常有效,因为所有容器都具有相同的石墨用户和匹配的uid / gid。

Since you never mount /data/graphite from the host, you don't care how the host uid/gid maps to the uid/gid defined inside the graphite and graphitetools containers. 由于您从不从主机安装/data/graphite ,因此您不关心主机uid / gid如何映射到graphitegraphitetools容器中定义的uid / gid。 Those containers can now be deployed to any host, and they will continue to work perfectly. 这些容器现在可以部署到任何主机,它们将继续完美地工作。

The neat thing about this is that graphitetools could have all sorts of useful utilities and scripts, that you can now also deploy in a portable manner. 关于这一点的好处是graphitetools可以有各种有用的实用程序和脚本,您现在也可以以可移植的方式部署。

UPDATE 2 : After writing this answer, I decided to write a more complete blog post about this approach. 更新2 :写完这个答案之后,我决定写一篇关于这种方法的更完整的博客文章 I hope it helps. 我希望它有所帮助。

UPDATE 3 : I corrected this answer and added more specifics. 更新3 :我更正了这个答案并添加了更多细节。 It previously contained some incorrect assumptions about ownership and perms -- the ownership is usually assigned at volume creation time ie 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. 这不是一个要求 - 您可以只使用数据容器作为“引用/句柄”,并通过入口点中的chown在另一个容器中设置所有权/权限,以gosu结束以将命令作为正确的用户运行。 If anyone is interested in this approach, please comment and I can provide links to a sample using this approach. 如果有人对此方法感兴趣,请发表评论,我可以使用此方法提供样本的链接。


#4楼

For secure and change root for docker container an docker host try use --uidmap and --private-uids options 为了安全和更改--uidmap容器的root, --uidmap主机尝试使用--uidmap--private-uids选项

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

Also you may remove several capabilities ( --cap-drop ) in docker container for security 此外,您可以删除--cap-drop容器中的多个功能( --cap-drop )以确保安全性

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

UPDATE support should come in docker > 1.7.0 更新支持应该在docker > 1.7.0

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


#5楼

This is arguably not the best way for most circumstances, but it's not been mentioned yet so perhaps it will help someone. 对于大多数情况来说,这可能不是最好的方式,但它还没有被提及,所以它可能对某人有所帮助。

  1. Bind mount host volume 绑定装载主机卷

    Host folder FOOBAR is mounted in container /volume/FOOBAR

  2. Modify your container's startup script to find GID of the volume you're interested in 修改容器的启动脚本以查找您感兴趣的卷的GID

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

  3. Ensure your user belongs to a group with this GID (you may have to create a new group). 确保您的用户属于具有此GID的组(您可能必须创建一个新组)。 For this example I'll pretend my software runs as the nobody user when inside the container, so I want to ensure nobody belongs to a group with a group id equal to TARGET_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

I like this because I can easily modify group permissions on my host volumes and know that those updated permissions apply inside the docker container. 我喜欢这个,因为我可以轻松修改主机卷上的组权限,并知道这些更新的权限适用于docker容器。 This happens without any permission or ownership modifications to my host folders/files, which makes me happy. 没有对我的主机文件夹/文件的任何许可或所有权修改就会发生这种情况,这让我很开心。

I don't like this because it assumes there's no danger in adding yourself to an arbitrary groups inside the container that happen to be using a GID you want. 我不喜欢这个,因为它假设将自己添加到容器内的任意组中没有危险,这些组恰好使用了您想要的GID。 It cannot be used with a USER clause in a Dockerfile (unless that user has root privileges I suppose). 它不能与Dockerfile中的USER子句一起使用(除非该用户具有我认为的root权限)。 Also, it screams hack job ;-) 此外,它尖叫黑客工作;-)

If you want to be hardcore you can obviously extend this in many ways - eg search for all groups on any subfiles, multiple volumes, etc. 如果你想成为硬核,你显然可以通过多种方式扩展它 - 例如搜索任何子文件,多个卷等的所有组。


#6楼

A very elegant solution can be seen on the official redis image and in general in all official images. 在官方redis图像上以及所有官方图像中都可以看到非常优雅的解决方案。

Described in step-by-step process: 逐步说明过程:

  • Create redis user/group before anything else 在其他任何事情之前创建redis用户/组

As seen on Dockerfile comments: 如Dockerfile评论所示:

add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added 首先添加我们的用户和组,以确保一致地分配他们的ID,无论添加什么依赖项

  • Install gosu with Dockerfile 安装古薮与Dockerfile

gosu is an alternative of su / sudo for easy step-down from root user. gosu是su / sudo的替代品,可以从root用户轻松降级。 (Redis is always run with redis user) (Redis始终使用redis用户运行)

  • Configure /data volume and set it as workdir 配置/data卷并将其设置为workdir

By configuring the /data volume with the VOLUME /data command we now have a separate volume that can either be docker volume or bind-mounted to a host dir. 通过使用VOLUME /data命令配置/ data卷,我们现在有一个单独的卷,可以是docker volume或bind-mounted to host dir。

Configuring it as the workdir ( WORKDIR /data ) makes it be the default directory where commands are executed from. 将其配置为workdir( WORKDIR /data )使其成为执行命令的默认目录。

  • Add docker-entrypoint file and set it as ENTRYPOINT with default CMD redis-server 添加docker-entrypoint文件并使用默认CMD redis-server将其设置为ENTRYPOINT

This means that all container executions will run through the docker-entrypoint script, and by default the command to be run is redis-server. 这意味着所有容器执行都将通过docker-entrypoint脚本运行,默认情况下,要运行的命令是redis-server。

docker-entrypoint is a script that does a simple function: Change ownership of current directory (/data) and step-down from root to redis user to run redis-server . docker-entrypoint是一个执行简单功能的脚本:更改当前目录(/ data)的所有权,并从root用户降级为redis用户以运行redis-server (If the executed command is not redis-server, it will run the command directly.) (如果执行的命令不是redis-server,它将直接运行命令。)

This has the following effect 这具有以下效果

If the /data directory is bind-mounted to the host, the docker-entrypoint will prepare the user permissions before running redis-server under redis user. 如果将/ data目录绑定到主机,则docker-entrypoint将在redis用户下运行redis-server之前准备用户权限。

This gives you the ease-of-mind that there is zero-setup in order to run the container under any volume configuration. 这使您可以轻松地设置零设置,以便在任何卷配置下运行容器。

Of course if you need to share the volume between different images you need to make sure they use the same userid/groupid otherwise the latest container will hijack the user permissions from the previous one. 当然,如果您需要在不同映像之间共享卷,则需要确保它们使用相同的userid / groupid,否则最新容器将劫持前一个容器的用户权限。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值