允许 Docker 容器和主机用户在绑定挂载的主机目录上写入



感谢任何来源的帮助
Server有一个包含alpine、nginx、php的Docker容器。这个容器能够在绑定安装的主机目录中写入,只有当我设置";chown-R nobody目录";到主机目录(容器中没有用户)。我正在使用VSCode的扩展";远程-SSH";以用户ubuntu的身份连接到服务器。VSCode能够编辑同一主机目录中的文件(用于绑定装载);chown-R ubuntu目录";。

问题:如果我设置";ubuntu";作为所有者,容器不能写(使用php来写),如果我设置";没有人";作为所有者,VSCode SSH无法写入。我正在寻找一种方法,允许两者在不反复更改目录所有者用户或类似轻松的情况下进行编写。

使用的图像:https://hub.docker.com/r/trafex/php-nginx

我尝试过的:
在Container中,我添加了用户"没有人";以分组";ubuntu";。在主机上,目录(用作装载)被设置为";sudo chown-Rubuntu:ubuntu目录";,用户";ubuntu";已添加到组"中;ubuntu">
VSCode已编辑,容器无法编辑。(编辑:工作正常,我更改了组的目录权限以允许写入)

编辑:在没有Dockerfile的情况下已经创建的容器也运行了,可能进行了重要的修改,所以我可能无法使用Dockerfile或entrypoint.sh方法来解决问题。它可以通过在容器内运行命令来实现吗?或者不需要再次创建容器?可以停止此容器。

编辑:我想知道,在Triet Doan的回答中,一个选项是修改容器中已经创建的用户的UID和GID,将为用户和组"这样做;没有人";可能会在容器内引起任何问题,我想知道,因为可能有许多设置命令已经在容器内执行,文件已经由php在挂载目录&集装箱运行天

编辑:我发现alpine没有usermod&groupmod。

这篇文章对这个问题写得很好。我只想在这里总结一下主要观点。

解决此权限问题的最简单方法是将容器中的UID和GID修改为主机中使用的相同UID和GID。

在您的情况下,我们尝试获取用户ubuntu的UID和GID,并在容器中使用它们。


作者提出了三种方法:

1.在entrypoint.sh中创建一个具有主机相同UID和GID的新用户

这是Ubuntu基本镜像的Dockerfile版本。

FROM ubuntu:latest
RUN apt-get update && apt-get -y install gosu
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]

entrypoint.sh创建如下:

#!/bin/bash
USER_ID=${LOCAL_UID:-9001}
GROUP_ID=${LOCAL_GID:-9001}
echo "Starting with UID: $USER_ID, GID: $GROUP_ID"
useradd -u $USER_ID -o -m user
groupmod -g $GROUP_ID user
export HOME=/home/user
exec /usr/sbin/gosu user "$@"

只需使用docker build命令构建容器。

docker build -t ubuntu-test1 .

LOCAL_UIDLOCAL_GID可以通过docker run命令传递给容器。

$ docker run -it --name ubuntu-test -e LOCAL_UID=$(id -u $USER) -e LOCAL_GID=$(id -g $USER) ubuntu-test1 /bin/bash
Starting with UID: 1001, GID: 1001
user@1291224a8029:/$ id
uid=1001(user) gid=1001(user) groups=1001(user)

我们可以看到,容器中的UID和GID与主机中的相同。

2.将主机的/etc/passwd/etc/group安装到容器

这也是一种很好的方法,而且看起来更简单。这种方法的一个缺点是,在容器中创建的新用户无法访问绑定安装的文件和目录,因为UID和GID与主机的不同。

必须小心使/etc/passwd/etc/group具有只读访问权限,否则容器可能会访问并覆盖主机的/etc/passwd/etc/group。因此,笔者不建议采用这种方式。

$ docker run -it --name ubuntu-test --mount type=bind,source=/etc/passwd,target=/etc/passwd,readonly --mount type=bind,source=/etc/group,target=/etc/g
roup,readonly -u $(id -u $USER):$(id -g $USER) ubuntu /bin/bash
ether@903ad03490f3:/$ id
uid=1001(user) gid=1001(user) groups=1001(user)

3.使用主机的相同UID和GID修改UID和GID

这与No.1的方法基本相同,但只需修改UID和GID,以防容器中已经创建了新用户。假设您在Dockerfile中创建了一个新用户,那么只需在Dockerfle或entrypoint.sh中调用这些命令。

如果您的用户名和组名是";测试";,则可以使用usermodgroupmod命令修改容器中的UID和GID。来自主机的作为环境变量的UID和GID将用于该";测试";使用者

usermod -u $USER_ID -o -m -d <path-to-new-home> test
groupmod -g $GROUP_ID test

问题:如果我设置"ubuntu";作为所有者,容器不能写(使用php来写),如果我设置";没有人";作为所有者,VSCode SSH无法写入。我正在寻找一种方法,允许两者在不反复更改目录所有者用户或类似轻松的情况下进行编写。

首先,我建议容器映像应该为容器内的文件创建一个新的用户名,而不是重用nobody,因为该用户也可能用于其他不应该具有任何特殊访问权限的操作系统任务。

接下来,正如Triet所建议的,首选一个调整容器的用户/组以匹配卷的入口点。我自己版本的这些脚本可以在这个基本映像中找到,该映像包括一个fix-perms脚本,该脚本使容器用户的用户id和组id与已安装卷的id相匹配。特别是该脚本的以下几行,其中$opt_u是容器用户名,$opt_g是容器组名称,$1是卷装载位置:

# update the uid
if [ -n "$opt_u" ]; then
OLD_UID=$(getent passwd "${opt_u}" | cut -f3 -d:)
NEW_UID=$(stat -c "%u" "$1")
if [ "$OLD_UID" != "$NEW_UID" ]; then
echo "Changing UID of $opt_u from $OLD_UID to $NEW_UID"
usermod -u "$NEW_UID" -o "$opt_u"
if [ -n "$opt_r" ]; then
find / -xdev -user "$OLD_UID" -exec chown -h "$opt_u" {} ;
fi
fi
fi
# update the gid
if [ -n "$opt_g" ]; then
OLD_GID=$(getent group "${opt_g}" | cut -f3 -d:)
NEW_GID=$(stat -c "%g" "$1")
if [ "$OLD_GID" != "$NEW_GID" ]; then
echo "Changing GID of $opt_g from $OLD_GID to $NEW_GID"
groupmod -g "$NEW_GID" -o "$opt_g"
if [ -n "$opt_r" ]; then
find / -xdev -group "$OLD_GID" -exec chgrp -h "$opt_g" {} ;
fi
fi
fi

然后,我以root身份启动容器,容器从入口点运行fix-perms脚本,然后是类似于以下命令的命令:

exec gosu ${container_user} ${orig_command}

这会将以root身份运行的入口点替换为以指定用户身份运行的应用程序。我在中有更多这样的例子

  • DockerCon演示
  • 类似SO问题

我尝试了什么:在Container中,我添加了用户";没有人";以分组";ubuntu";。在主机上,目录(用作装载)被设置为";sudo chown-Rubuntu:ubuntu目录";,用户";ubuntu";已添加到组"ubuntu";。VSCode已编辑,容器无法编辑。

我会避免这种情况,并创建一个新用户。没有人被设计成尽可能没有特权,所以给它更多的访问权限可能会产生意想不到的后果。

编辑:在没有Dockerfile的情况下创建的容器也运行并可能经过了重要的修改,所以我可能无法使用Dockerfile或entrypoint.sh解决问题的方法。可以通过以下方式实现吗在容器内运行命令还是不重新创建容器?可以停止此容器。

这是容器中相当大的代码气味。它们的设计应该是短暂的。如果你不能轻易地替换它们,你就失去了升级到新映像的能力,并产生了许多最终需要清理的状态漂移。应该保留的更改需要在一个卷中。如果在删除容器时会丢失其他更改,则它们将在docker diff中可见,我建议现在修复此问题,而不是增加技术债务的大小。

编辑:我想知道,在Triet Doan的回答中,有一个选项是修改容器中已创建用户的UID和GID,将执行此操作对于用户和组";没有人";可能会导致内部出现任何问题容器,我想知道,因为可能有很多设置命令已经在容器中执行,文件已经由php在已装入目录&集装箱运行天

我会构建一个不依赖于此用户名的新映像。在容器中,如果有需要保存的数据,那么它应该在一个卷中。

编辑:我发现alpine没有usermod&groupmod。

我在入口点脚本中使用以下内容来动态安装它,但影子包应该包含在您构建的映像中,而不是为每个新容器动态安装:

if ! type usermod >/dev/null 2>&1 || 
! type groupmod >/dev/null 2>&1; then
if type apk /dev/null 2>&1; then
echo "Warning: installing shadow, this should be included in your image"
apk add --no-cache shadow
else
echo "Commands usermod and groupmod are required."
exit 1
fi
fi

相关内容

  • 没有找到相关文章

最新更新