为什么Dockerfile中的chown命令无效?

112

我的Dockerfile创建一个目录,使用chown命令更改它的所有者,然后列出该目录的内容。但是该目录仍然由root用户拥有。为什么会这样呢?

以下是Dockerfile的内容:

FROM ubuntu:precise
RUN useradd -d /home/testuser -m -s /bin/bash testuser
RUN mkdir -p /var/local/testrunner/logs
VOLUME ["/var/local/testrunner/logs"]
RUN grep testuser /etc/passwd
RUN grep root /etc/passwd
RUN chown -R testuser:testuser /var/local/testrunner/logs
RUN ls -ld /var/local/testrunner/logs 

以下是 "docker build" 命令的输出结果:

Sending build context to Docker daemon 10.24 kB
Sending build context to Docker daemon 
Step 0 : FROM ubuntu:precise
 ---> ab8e2728644c
Step 1 : RUN useradd -d /home/testuser -m -s /bin/bash testuser
 ---> Using cache
 ---> 640f12671c86
Step 2 : RUN mkdir -p /var/local/testrunner/logs
 ---> Using cache
 ---> bf7756fd5b1f
Step 3 : VOLUME ["/var/local/testrunner/logs"]
 ---> Using cache
 ---> 65c73ee76c20
Step 4 : RUN grep testuser /etc/passwd
 ---> Using cache
 ---> db72fff0b965
Step 5 : RUN grep root /etc/passwd
 ---> Running in ebff78df7a9a
root:x:0:0:root:/root:/bin/bash
 ---> ead0ff704a59
Removing intermediate container ebff78df7a9a
Step 6 : RUN chown -R testuser:testuser /var/local/testrunner/logs
 ---> Running in c925f67b2ab4
 ---> 253132be935e
Removing intermediate container c925f67b2ab4
Step 7 : RUN ls -ld /var/local/testrunner/logs
 ---> Running in 978bc66aa47e
drwxr-xr-x 2 root staff 4096 Oct  1 15:15 /var/local/testrunner/logs

Docker版本为1.2.0,构建版本号为fa7b24f。

主机运行的是Ubuntu 12.04操作系统,但内核版本为3.13.0-36-generic。


2
有关COPY后所有权的问题,请参见:https://dev59.com/qVcP5IYBdhLWcg3wLnRC - That Brazilian Guy
我想知道是哪些蠢货决定这个问题不符合StackOverflow的准则... - Mike
6个回答

163

回答我的问题:它被声明为一个卷。如果您删除VOLUME指令,则chown会生效。

更重要的是,如果在运行chown之后声明卷,则chown设置仍然有效。


24
如果您在运行chown之后声明了卷,那么chown设置将继续生效。这刚好回答了我困惑了两天的问题。谢谢! - CashIsClay
2
回答自己:这里的解释对我来说很有道理http://container-solutions.com/2014/12/understanding-volumes-docker/ - Michael Härtl
5
上面文章中的一个重要点是:“[当在RUN命令修改卷后指定了VOLUME]时,Docker会聪明地将任何存在于映像下的文件复制到卷中并正确设置其所有权。但如果您为卷指定了主机目录(以免意外覆盖主机文件),则不会发生这种情况。” - Gezim
5
@MichaelHärtl提供的解释链接已更新为:https://blog.container-solutions.com/understanding-volumes-docker - Kamafeather
10
我没有在dockerfile中声明任何VOLUME,但仍然遇到了这个问题... :-( - fccoelho
显示剩余5条评论

10

这篇博客http://container42.com/2014/11/03/docker-indepth-volumes/详细阐述了这种行为。

Dockerfile中的每个指令都会创建一个新的容器。指令对该容器进行更改,并成为一个新层。在VOLUME指令之前对“/var/local/testrunner/logs”所做的更改是针对实际容器文件系统进行的。然而,在VOLUME指令之后,目录“/var/local/testrunner/logs”是挂载的目录。在VOLUME指令之后对此目录所做的更改将应用于挂载的目录而不是实际容器文件系统。


8

对于没有卷的用户,我找到了一个复杂的解决方法。

问题:

使用以下简单的Dockerfile:

FROM ubuntu:16.04
RUN useradd -m -d /home/new_user new_user
COPY test_file.txt /home/new_user
RUN chown -R new_user:new_user /home/new_user
CMD ls -RFlag /home

运行之后:
echo "A file to test permissions." > test_file.txt
docker build -t chown-test -f Dockerfile .
docker run --rm -it chown-test

输出结果为:

/home:
total 12
drwxr-xr-x 1 root 4096 Jun 15 21:37 ./
drwxr-xr-x 1 root 4096 Jun 15 21:39 ../
drwxr-xr-x 1 root 4096 Jun 15 21:39 new_user/

/home/new_user:
total 24
drwxr-xr-x 1 root 4096 Jun 15 21:39 ./
drwxr-xr-x 1 root 4096 Jun 15 21:37 ../
-rw-r--r-- 1 root  220 Aug 31  2015 .bash_logout
-rw-r--r-- 1 root 3771 Aug 31  2015 .bashrc
-rw-r--r-- 1 root  655 Jul 12  2019 .profile
-rw-r--r-- 1 root   28 Jun 11 19:48 test_file.txt

如您所见,文件所有权(例如test_file.txt)仍与用户root相关。

解决方案:

我发现,如果我在chown命令中使用数字UID,我可以更改所有权,但前提是UID不是1000。因此,我将new_user的UID加1,然后更改了所有权。

FROM ubuntu:16.04
RUN useradd -m -d /home/new_user new_user
# change the uid of new_user to ensure it has whatever it was assigned plus 1 (e.g. if UID was 1000, now it'll be 1001)
RUN id -u new_user | awk '{print $1+1}' | xargs -I{} usermod -u {} new_user
COPY test_file.txt /home/new_user
RUN id -u new_user | xargs -I{} chown -R {}:{} /home/new_user
CMD ls -RFlag /home

运行后:

echo "A file to test permissions." > test_file.txt
docker build -t chown-test -f Dockerfile .
docker run --rm -it chown-test

输出结果为:
/home:
total 12
drwxr-xr-x 1 root 4096 Jun 15 21:37 ./
drwxr-xr-x 1 root 4096 Jun 15 21:37 ../
drwxr-xr-x 1 1001 4096 Jun 15 21:37 new_user/

/home/new_user:
total 24
drwxr-xr-x 1 1001 4096 Jun 15 21:37 ./
drwxr-xr-x 1 root 4096 Jun 15 21:37 ../
-rw-r--r-- 1 1001  220 Aug 31  2015 .bash_logout
-rw-r--r-- 1 1001 3771 Aug 31  2015 .bashrc
-rw-r--r-- 1 1001  655 Jul 12  2019 .profile
-rw-r--r-- 1 1001   28 Jun 11 19:48 test_file.txt

我不确定为什么一开始会出现这个问题。然而,既然其他人也遇到了同样的问题,我想我应该发布我的解决方法。我的使用情况是创建一个docker容器,用于提供jupyter笔记本服务。我创建了一个非root用户来提供笔记本服务。


2
手动修改usermod文件中的UID是一种丑陋的hack。您可以使用useradd -u 1001在创建时指定UID。 - tripleee
同样的道理也适用于用户名;如果 new_user 已经存在会怎么样呢?但是你已经解释得足够清楚了,任何有能力的读者都应该能够解决这些边角案例。 - tripleee
1
@tripleee 我考虑过根据您的初始评论编辑答案,但没有这样做,因为我和您达成了相同的意见,即“任何有能力的读者可能都能够解决这些边角情况。” 我想,在我提供的hack和您的评论之间,读者会有两个选择。 再次感谢。我只是希望我能够更深入地解释为什么 chown 一开始无法工作。 - Hari S. Khalsa
1
如果你最近(2023年8月)也因此而掉发,不妨看看这个链接:https://bugs.launchpad.net/ubuntu/+source/docker.io-app/+bug/2029523 - Damien
这似乎在Docker桌面版上无法运行 - undefined
显示剩余2条评论

3
根据我的经验,当挂载到根目录 (VOLUME /test) 时,chown 命令不起作用。请使用非根位置 (VOLUME /var/test)。

这个!尽管我使用的是COPY而不是VOLUME,但它为我解决了问题。 - mcsoini

1

如果您正在使用 Volumen,可以在运行后设置权限,如下所示:

docker exec -u 0 my-container chmod -R 700 /my/path
docker exec -u 0 my-container chown -R my-non-user-root /my/path

0
对于Alpine Linux用户,我必须在我尝试拥有的工作区中执行chown -R root .。这必须在dockerfile的CMD中完成,因为我相信卷挂载可能会在挂载时覆盖文件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接