docker cp 后的文件所有权

Don*_*kby 13 file-permissions docker

如何控制哪个用户拥有我复制进和复制出容器的文件?

docker cp命令说明了文件所有权:

cp命令的行为类似于 Unixcp -a命令,因为如果可能,将递归复制目录并保留权限。所有权设置为目的地的用户和主要组。例如,复制到容器的文件是使用UID:GIDroot 用户创建的。复制到本地机器的文件是使用UID:GID调用该docker cp命令的用户名创建的。但是,如果您指定该-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
Run Code Online (Sandbox Code Playgroud)

当我从容器中复制文件时,它们始终归我所有。同样,该-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
$ 
Run Code Online (Sandbox Code Playgroud)

Dir*_*irk 14

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

让我们从一个小镜像创建一个新的容器,它会继续自己运行:

docker run -d --name nginx nginx:alpine
Run Code Online (Sandbox Code Playgroud)

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

touch foo.bar
ls -ahl foo.bar
>> -rw-rw-r-- 1 my-user my-group 0 Sep 21 16:45 foo.bar
Run Code Online (Sandbox Code Playgroud)

将此文件复制到容器中会将所有权和组设置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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

tar 选项解释:

  • c 创建一个新的存档而不是解压一个。
  • f -将写入stdout而不是文件。
  • foo.bar 是要打包的输入文件。
  • --mode指定目标的权限。与 类似chown,它们可以用符号表示法或八进制数给出。
  • --owner 设置文件的新所有者。
  • --group 设置文件的新组。

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

当文件需要在启动之前复制到创建的容器中时,这种方法很有用,这docker exec不是一个选项(只能在运行的容器上操作)。


Aid*_*din 9

只是一行(类似于@ramu的答案),使用root进行调用:

docker exec -u 0 -it <container-id> chown node:node /home/node/myfile
Run Code Online (Sandbox Code Playgroud)


ram*_*amu 7

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

docker exec -it --user root <container-id> /bin/bash
chown -R <username>:<groupname> <folder/file>
Run Code Online (Sandbox Code Playgroud)

  • 好的!您也可以使用以下命令对其进行优化:`docker exec -it --user root &lt;container-id&gt; chown -R &lt;username&gt;:&lt;groupname&gt; &lt;folder/file&gt;` (3认同)

Don*_*kby 5

为了完全控制文件所有权,我使用了tar 流功能docker cp

如果为或-指定,您还可以从或 向传输 tar 存档。SRC_PATHDEST_PATHSTDINSTDOUT

我启动该docker cp进程,然后将 tar 文件流式传输到该进程或从该进程流式传输。当 tar 条目过去时,我可以根据需要调整所有权和权限。

/outputs下面是一个简单的 Python 示例,它将容器中的所有文件复制到sandbox1当前目录,排除当前目录,使其权限不会更改,并强制所有文件具有用户的读/写权限。

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()
Run Code Online (Sandbox Code Playgroud)