dockervisual-studio-codedocker-composevscode-devcontainerdevcontainer

vscode devcontainers - why are two images built?


im using a remote container as my dev environment. so essentially in my project i have a Dockerfile, docker-compose.yml and my devcontainer.json that vscode uses to build, start and launch into the container with my code mounted. works great so far.
The thing is, on the linux host, if do a docker ps, i see my single container, but if look at the docker images, two are created, and the last one is the one started.

my docker-compose looks like this:


  app:
    container_name: my_app 
    build:
      context: ..
      dockerfile: .devcontainer/Dockerfile
    volumes:
      - ../..:/workspaces:cached

and my .devcontainer.json looks like this:

    "name": "my_app_service",
    "dockerComposeFile": "docker-compose.yml",
    "service": "app",
    "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
    "customizations": { 
        "vscode": {
            "extensions": ["ms-python.python"]
        },
        "settings": {
            "terminal.integrated.shell.*": "/bin/bash"
        }
    }     

docker ps shows a single container running with:

container name: my_app
image: vsc-my_proj-64ad9a6809f39eaa523af6a319f22b90b2d1358fe3a8aebc93ac1dfaebf5f22a-uid 

docker images shows two images created, with names:

REPOSITORY: my_app_devcontainer-app 
REPOSITORY: vsc-my_proj-64ad9a6809f39eaa523af6a319f22b90b2d1358fe3a8aebc93ac1dfaebf5f22a-uid

(note in the above 'my_proj' part of the image name comes from my the vscode project folder name i believe.) i can delete the first image. but i want to understand why this is happening? doesnt seem like it should - its almost like the build is happening twice. Also, is there a way to specify an image name using docker-compose, or in vscode using the devcontainer.json (i know how to do it from docker-build command line arg).

Thanks for any help in figuring this out


Solution

  • I was wondering about the same thing, so I did the following:

    Steps

    1. I printed out the image history, only printing out the column that shows the actual docker file statements. See https://docs.docker.com/config/formatting/ and https://docs.docker.com/engine/reference/commandline/history/
    docker image history PUTIMAGE_HASH_HERE_FOR_IMAGE_NOT_ENDING_IN_UID --no-trunc --format "table {{.CreatedBy}}" > image_wo_uid.txt
    
    docker image history PUTIMAGE_HASH_HERE_FOR_IMAGE_WITH_UID_AT_END --no-trunc --format "table {{.CreatedBy}}" > image_uid.txt
    
    1. Then diff the two: diff image_wo_uid.txt image_uid.txt

    2. That gives you something like (note: reverse chronological order):

    1a2,9
    > USER vscode
    > ARG IMAGE_USER
    > RUN |3 REMOTE_USER=vscode NEW_UID=1000 NEW_GID=1000 /bin/sh -c eval $(sed -n "s/${REMOTE_USER}:[^:]*:\([^:]*\):\([^:]*\):[^:]*:\([^:]*\).*/OLD_UID=\1;OLD_GID=\2;HOME_FOLDER=\3/p" /etc/passwd);  eval $(sed -n "s/\([^:]*\):[^:]*:${NEW_UID}:.*/EXISTING_USER=\1/p" /etc/passwd);  eval $(sed -n "s/\([^:]*\):[^:]*:${NEW_GID}:.*/EXISTING_GROUP=\1/p" /etc/group);  if [ -z "$OLD_UID" ]; then   echo "Remote user not found in /etc/passwd ($REMOTE_USER).";  elif [ "$OLD_UID" = "$NEW_UID" -a "$OLD_GID" = "$NEW_GID" ]; then   echo "UIDs and GIDs are the same ($NEW_UID:$NEW_GID).";  elif [ "$OLD_UID" != "$NEW_UID" -a -n "$EXISTING_USER" ]; then   echo "User with UID exists ($EXISTING_USER=$NEW_UID).";  elif [ "$OLD_GID" != "$NEW_GID" -a -n "$EXISTING_GROUP" ]; then   echo "Group with GID exists ($EXISTING_GROUP=$NEW_GID).";  else   echo "Updating UID:GID from $OLD_UID:$OLD_GID to $NEW_UID:$NEW_GID.";   sed -i -e "s/\(${REMOTE_USER}:[^:]*:\)[^:]*:[^:]*/\1${NEW_UID}:${NEW_GID}/" /etc/passwd;   if [ "$OLD_GID" != "$NEW_GID" ]; then    sed -i -e "s/\([^:]*:[^:]*:\)${OLD_GID}:/\1${NEW_GID}:/" /etc/group;   fi;   chown -R $NEW_UID:$NEW_GID $HOME_FOLDER;  fi; # buildkit
    > SHELL [/bin/sh -c]
    > ARG NEW_GID
    > ARG NEW_UID
    > ARG REMOTE_USER
    > USER root
    

    Conclusion

    Notes