Docker cp之后的文件所有权

27

如何控制我复制进出容器的文件所属的用户?

docker cp 命令 关于文件所有权的说明如下:

cp 命令的行为类似于 Unix 的 cp -a 命令,即目录以尽可能保留权限进行递归复制。所有权设置为目标处的用户和主组。例如,复制到容器中的文件由 root 用户的 UID:GID 创建。复制到本地机器上的文件使用调用 docker cp 命令的用户的 UID:GID 创建。然而,如果您指定了 -a 选项,docker cp 将把所有权设置为源处的用户和主组。

它说复制到容器中的文件将被创建为 root 用户,但这不是我看到的情况。我创建了两个属于用户 id 1005 和 1006 的文件。这些所有者被翻译成容器的用户命名空间。当我将文件复制到容器中时,-a 选项似乎没有任何区别。

$ sudo chown 1005:1005 test.txt
$ ls -l test.txt
-rw-r--r-- 1 1005 1005 29 Oct  6 12:43 test.txt
$ docker volume create sandbox1
sandbox1
$ docker run --name run1 -vsandbox1:/data alpine echo OK
OK
$ docker cp test.txt run1:/data/test1005.txt
$ docker cp -a test.txt run1:/data/test1005a.txt
$ sudo chown 1006:1006 test.txt
$ docker cp test.txt run1:/data/test1006.txt
$ docker cp -a test.txt run1:/data/test1006a.txt
$ docker run --rm -vsandbox1:/data alpine ls -l /data
total 16
-rw-r--r--    1 1005     1005            29 Oct  6 19:43 test1005.txt
-rw-r--r--    1 1005     1005            29 Oct  6 19:43 test1005a.txt
-rw-r--r--    1 1006     1006            29 Oct  6 19:43 test1006.txt
-rw-r--r--    1 1006     1006            29 Oct  6 19:43 test1006a.txt

当我从容器中复制文件时,它们总是归我所有。同样,-a选项似乎没有起作用。

$ docker run --rm -vsandbox1:/data alpine cp /data/test1006.txt /data/test1007.txt
$ docker run --rm -vsandbox1:/data alpine chown 1007:1007 /data/test1007.txt
$ docker cp run1:/data/test1006.txt .
$ docker cp run1:/data/test1007.txt .
$ docker cp -a run1:/data/test1006.txt test1006a.txt
$ docker cp -a run1:/data/test1007.txt test1007a.txt
$ ls -l test*.txt
-rw-r--r-- 1 don  don  29 Oct  6 12:43 test1006a.txt
-rw-r--r-- 1 don  don  29 Oct  6 12:43 test1006.txt
-rw-r--r-- 1 don  don  29 Oct  6 12:47 test1007a.txt
-rw-r--r-- 1 don  don  29 Oct  6 12:47 test1007.txt
-rw-r--r-- 1 1006 1006 29 Oct  6 12:43 test.txt
$ 
4个回答

35

你还可以通过以 root 用户身份登录到容器中来更改所有权:

docker exec -it --user root <container-id> /bin/bash
chown -R <username>:<groupname> <folder/file>

3
好的!您可以使用以下命令进行优化: docker exec -it --user root <container-id> chown -R <username>:<groupname> <folder/file> (意思是将容器中 <folder/file> 文件夹/文件的所有权更改为 <username>:<groupname> 用户/用户组,以便更好地管理它们) - Obotor

20

除了@Don Kirkby的答案之外,让我在bash / shell脚本中提供一个类似的示例,以防您想要将某些内容复制到容器中,同时应用与原始文件不同的所有权和权限。

让我们从一个小镜像创建一个新的容器,该容器将自行运行:

docker run -d --name nginx nginx:alpine

现在我们将创建一个由当前用户拥有并具有默认权限的新文件:

touch foo.bar
ls -ahl foo.bar
>> -rw-rw-r-- 1 my-user my-group 0 Sep 21 16:45 foo.bar

将此文件复制到容器中会将所有权和组设置为我的用户的UID,并保留权限:

docker cp foo.bar nginx:/foo.bar
docker exec nginx sh -c 'ls -ahl /foo.bar'
>> -rw-rw-r--    1 4098     4098           0 Sep 21 14:45 /foo.bar

不过,通过使用一些 tar 技巧,我可以改变容器内应用的所有权和权限。

tar -cf - foo.bar --mode u=+r,g=-rwx,o=-rwx --owner root --group root | docker cp - nginx:/
docker exec nginx sh -c 'ls -ahl /foo.bar'
>> -r--------    1 root     root           0 Sep 21 14:45 /foo.bar

tar 选项说明:

  • c 创建一个新的归档文件,而不是解压已有的文件。
  • f - 将内容写入 stdout 而不是文件。
  • foo.bar 是要被打包的输入文件。
  • --mode 指定目标文件的权限。类似于 chown 命令,权限可以用符号表示法或八进制数字表示。
  • --owner 设置文件的新所有者。
  • --group 设置文件的新群组。

docker cp -stdin 读取要复制到容器中的文件。

当需要在容器启动前将文件复制到创建的容器中,而 docker exec 不可用时(只能用于运行的容器),这种方法非常有用。


我在docker容器内使用非root用户时遇到了问题。通过在容器中使用“-n”列出文件,即“docker exec nginx sh -c 'ls -ahln /foo.bar'”,并在tar参数上使用数字,即“tar -cf - foo.bar --mode u=+r,g=-rwx,o=-rwx --owner 1000 --group 1000 | docker cp - nginx:/”,我已经解决了这个问题。 - MadMike

9

只需一行命令(类似于@ramu的答案),使用根目录来进行调用:

docker exec -u 0 -it <container-id> chown node:node /home/node/myfile

5
为了完全控制文件所有权,我使用了docker cptar流功能:

如果SRC_PATHDEST_PATH中有-,则可以从STDIN流式传输tar存档文件,或者将其流式传输到STDOUT

我启动docker cp进程,然后将tar文件流式传输到进程中或从进程中流式传输。当tar条目经过时,我可以随意调整所有权和权限。
以下是一个简单的Python示例,将sandbox1容器中/outputs的所有文件复制到当前目录,排除当前目录以防其权限被更改,并强制所有文件对用户具有读/写权限。
from subprocess import Popen, PIPE, CalledProcessError
import tarfile

def main():
    export_args = ['sudo', 'docker', 'cp', 'sandbox1:/outputs/.', '-']
    exporter = Popen(export_args, stdout=PIPE)
    tar_file = tarfile.open(fileobj=exporter.stdout, mode='r|')
    tar_file.extractall('.', members=exclude_root(tar_file))
    exporter.wait()
    if exporter.returncode:
        raise CalledProcessError(exporter.returncode, export_args)

def exclude_root(tarinfos):
    print('\nOutputs:')
    for tarinfo in tarinfos:
        if tarinfo.name != '.':
            assert tarinfo.name.startswith('./'), tarinfo.name
            print(tarinfo.name[2:])
            tarinfo.mode |= 0o600
            yield tarinfo

main()

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