问题
I have the following Dockerfile
ARG DEV_USER=dev
# Other stuff ...
USER $DEV_USER
# Other stuff ...
WORKDIR /home/$DEV_USER/Projects
When I start a container and execute ls /home/dev
, the Projects
folder is owned by root
. Does WORKDIR ignore the fact that USER was invoked earlier?
回答1:
I failed to find detail documents for this, but I'm interested on this, so I just had a look for docker source code, I guess we can get the clue from sourcecode:
moby/builder/dockerfile/dispatcher.go (Line 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)
}
Above, we can see it will call ContainerCreateWorkdir
, next is the code:
moby/daemon/workdir.go:
func (daemon *Daemon) ContainerCreateWorkdir(cID string) error {
......
return container.SetupWorkingDirectory(daemon.idMapping.RootPair())
}
Above, we can see it call SetupWorkingDirectory
, next is the code:
moby/container/container.go (Line 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
}
Above, we can see it call MkdirAllAndChownNew(pth, 0755, rootIdentity)
, next is the code:
moby/pkg/idtools/idtools.go (Line 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)
}
Above will setup folder in intermediate build container & also change the ownership of the folder with rootIdentity
.
Finally, what is rootIdentity
here?
It's passed here as daemon.idMapping.RootPair()
, next is the declare:
moby/pkg/idtools/idtools.go (Line 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}
}
See the function desc:
RootPair returns a uid and gid pair for the root user
You can continue to see what GetRootUIDGID
is, but I think it's enough now from the function desc. It will finally use change the ownership of WORKDIR
to root
.
And, additional to see what USER
do?
__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))
}
Above, just set user to run config and directly commit for further command, but did nothing related to WORKDIR
setup.
And, if you want to change the ownership, I guess you will have to do it by yourself use chown
either in RUN
or ENTRYPOINT/CMD
.
回答2:
Docker is always run as root user in its environment, SO instead we specify it to use another user.
So before running anything we can add a user to its environment then specify that useR to perform action in Dockerfile.
This example add user to www-data
ARG USER_ID=1000
ARG GROUP_ID=1000
RUN groupadd -g ${GROUP_ID} www-data &&\
useradd -l -u ${USER_ID} -g www-data www-data &&\
来源:https://stackoverflow.com/questions/56980608/why-is-a-folder-created-by-workdir-owned-by-root-instead-of-user