moby: CMD doesn't survive docker run, docker commit

When an image that has a CMD is run, and the resulting container committed as a new image, the new image has no CMD.

As a counterpoint, when an image that has a CMD is used as the FROM line in a Dockerfile, the new image DOES have a CMD. (Perhaps subject to bug(s) vaguely described in #3762).

I think it should be default, or at least possible, for the resulting image to retain the CMD / config.Cmd.

Reproduce

Here’s a minimal reproduction:

#!/bin/bash
set -e

# Create image with explicit CMD in Dockerfile
cat > Dockerfile <<END
FROM ubuntu:13.10
CMD ["true"]
END
docker build --rm --tag cmdtest:dockerfile . > /dev/null 2>&1

# Run and commit a container as a new image.
rm -f container_id
docker run --cidfile=container_id cmdtest:dockerfile
docker commit $(cat container_id) cmdtest:dockerrun > /dev/null

# Output: Inspect CMD
set -x
docker inspect --format='{{.config.Cmd}}' cmdtest:dockerfile
docker inspect --format='{{.config.Cmd}}' cmdtest:dockerrun
docker run cmdtest:dockerrun

Output:

+ docker inspect '--format={{.config.Cmd}}' cmdtest:dockerfile
[true]
+ docker inspect '--format={{.config.Cmd}}' cmdtest:dockerrun
<no value>
+ docker run cmdtest:dockerrun
2014/02/26 23:52:00 Error: create: No command specified

How it works in Dockerfile

The Dockerfile CMD line does this in buildfile.go (error handling trimmed):

func (b *buildFile) CmdCmd(args string) error {
    cmd := b.buildCmdFromJson(args)
    b.config.Cmd = cmd
    _ := b.commit("", b.config.Cmd, fmt.Sprintf("CMD %v", cmd))
}

I can’t find a docker command to manipulate an image config to reproduce b.config.Cmd = cmd outside of a Dockerfile. Nothing else inside Docker seems to write to config.Cmd.

Use-case

My use-case is a build pipeline which takes a developer-friendly image (codebase is mounted in as a volume), and builds a deployable image by adding and building the codebase using something roughly like this:

docker run \
  --cidfile="build.cid"
  --volume=/codebase:/mnt/codebase \
  IMAGE_NAME \
  bash -c "cp -a /mnt/codebase /codebase && cd /codebase && make"

docker commit $(cat build.cid) release

I’d like the release image to retain the CMD from the original image.

Version

$ docker -v
Docker version 0.8.1, build a1598d1

$ docker info
Containers: 25
Images: 68
Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Dirs: 118

$ uname -a
Linux ubuntu-13 3.11.0-17-generic #31-Ubuntu SMP Mon Feb 3 21:52:43 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

About this issue

  • Original URL
  • State: closed
  • Created 10 years ago
  • Reactions: 2
  • Comments: 22 (7 by maintainers)

Most upvoted comments

I see no mention of a --run option to docker commit in the docs or built-in help.

I managed to work around this problem by doing this instead:

  1. start the app container (gitlab/gitlab-ce in my case) with no options other than --detach (no ports, no volumes, no /bin/bash command, etc)
  2. connect to the running container via docker exec -it <container id> /bin/bash
  3. make whatever changes I need (installing another gem in my case)
  4. exit
  5. use docker commit

Ta da! The original config is preserved (including CMD, which is what screwed me the first time I did this).

I understand your perspective, and I think you are right. What is then missing is a way to commit the image’s filesystem without touching to the configuration. For instance, it does seems that there are no way to do an “apt-get update && apt-get upgrade” on an image that would have set its ENTRYPOINT in only one commit. I have to: 1- save the configuration 2- use “docker run --entrypoint /bin/bash IMAGENAME -c ‘apt-get update && apt-get upgrade’” to remove any other entrypoint the image would have 3- commit the container (which commits the config and the filesystem) 4- reset the configuration with saved values by doing another commit.

The “apt-get update && apt-get upgrade” is an example. I have great use of running just a command in a docker and saving the result. The update is then easily shippable because very small and quickly built and you don’t need to recalculate and send huge images. It’s meant like adding a new “RUN” live to manufacture a docker image.

What I understand is that I should probably move this concern to another place. Thanks !.