为什么 WORKDIR 创建的文件夹归 root 而不是 USER 所有

Cod*_*ith 9 docker dockerfile

我有以下 Dockerfile

ARG DEV_USER=dev
# Other stuff ...
USER $DEV_USER
# Other stuff ...
WORKDIR /home/$DEV_USER/Projects
Run Code Online (Sandbox Code Playgroud)

当我启动容器并执行时ls /home/dev,该Projects文件夹属于root. WORKDIR 是否忽略了 USER 之前被调用的事实?

atl*_*ine 8

我没有找到这方面的详细文档,但我对此很感兴趣,所以我只是查找了docker 源代码,我想我们可以从源代码中找到线索:

moby/builder/dockerfile/dispatcher.go(第 299 行):

// Set the working directory for future RUN/CMD/etc statements.
//
func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
    ......
    if err := d.builder.docker.ContainerCreateWorkdir(containerID); err != nil {
        return err
    }

    return d.builder.commitContainer(d.state, containerID, runConfigWithCommentCmd)
}
Run Code Online (Sandbox Code Playgroud)

上面,我们可以看到它会调用ContainerCreateWorkdir,接下来是代码:

moby/daemon/workdir.go:

func (daemon *Daemon) ContainerCreateWorkdir(cID string) error {
    ......
    return container.SetupWorkingDirectory(daemon.idMapping.RootPair())
}
Run Code Online (Sandbox Code Playgroud)

上面我们可以看到它的调用SetupWorkingDirectory,接下来是代码:

moby/container/container.go(第 259 行):

func (container *Container) SetupWorkingDirectory(rootIdentity idtools.Identity) error {
    ......
    if err := idtools.MkdirAllAndChownNew(pth, 0755, rootIdentity); err != nil {
        pthInfo, err2 := os.Stat(pth)
        if err2 == nil && pthInfo != nil && !pthInfo.IsDir() {
            return errors.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)
        }

        return err
    }

    return nil
}
Run Code Online (Sandbox Code Playgroud)

上面我们可以看到它的调用MkdirAllAndChownNew(pth, 0755, rootIdentity),接下来是代码:

moby/pkg/idtools/idtools.go(第 54 行):

// MkdirAllAndChownNew creates a directory (include any along the path) and then modifies
// ownership ONLY of newly created directories to the requested uid/gid. If the
// directories along the path exist, no change of ownership will be performed
func MkdirAllAndChownNew(path string, mode os.FileMode, owner Identity) error {
    return mkdirAs(path, mode, owner, true, false)
}
Run Code Online (Sandbox Code Playgroud)

上面将在中间构建容器中设置文件夹,并使用rootIdentity.

最后,rootIdentity这里是什么?

它在这里传递为daemon.idMapping.RootPair(),接下来是声明:

moby/pkg/idtools/idtools.go(第 151 行):

// RootPair returns a uid and gid pair for the root user. The error is ignored
// because a root user always exists, and the defaults are correct when the uid
// and gid maps are empty.
func (i *IdentityMapping) RootPair() Identity {
    uid, gid, _ := GetRootUIDGID(i.uids, i.gids)
    return Identity{UID: uid, GID: gid}
}
Run Code Online (Sandbox Code Playgroud)

参见函数说明:

RootPair 返回 root 用户的 uid 和 gid 对

你可以继续看看GetRootUIDGID是什么,但我认为现在从函数 desc 就足够了。它将最终使用将所有权更改WORKDIRroot

并且,额外看什么USER做?

__moby/builder/dockerfile/dispatcher.go (Line 543):__

// USER foo
//
// Set the user to 'foo' for future commands and when running the
// ENTRYPOINT/CMD at container run time.
//
func dispatchUser(d dispatchRequest, c *instructions.UserCommand) error {
    d.state.runConfig.User = c.User
    return d.builder.commit(d.state, fmt.Sprintf("USER %v", c.User))
}
Run Code Online (Sandbox Code Playgroud)

上面,只是设置用户运行配置并直接提交进一步的命令,但没有执行任何与WORKDIR设置相关的操作。

而且,如果您想更改所有权,我想您必须自己使用chowninRUN或 来完成ENTRYPOINT/CMD

  • 恕我直言,如果包含摘要和/或结论,这个答案将会得到改进。 (3认同)