4

So,

enter image description here

Each Docker image references a list of read-only layers that represent filesystem differences. Layers are stacked on top of each other to form a base for a container’s root filesystem.

and,

enter image description here

Because each container has its own thin writable container layer, and all changes are stored in this container layer, this means that multiple containers can share access to the same underlying image and yet have their own data state.

and also,

Layers of a Docker image are essentially just files generated from running some command. You can view the contents of each layer on the Docker host at /var/lib/docker/aufs/diff.

Now, questions,

  • Say I build my docker images layer by layer. A < B < C < D, etc.
  • Now if I update my docker image A, would the rest of docker images B, C, D see the changes as well, provided that the changes are not touched by them when building them? E.g., adding /etc/apt/sources.list.d/somethingnew that was never there before.
  • If I had built another set of docker images layer by layer. A < X < Y < Z, then the above changes will be reflected in X, Y, Z as well, right?
  • Now if however, the future changes to A, is made to the same file that will be changed when building B, C, D, then what would happen? For e.g., let's make it simple that docker images B, C, D each only add pkgB, pkgC, and pkgD in its layer. If I add a pkgA to A after B, C, D are build, what would happen? -- I guess there should be one single version of truth as what packages are in, for a single system, so what would it be for this case?
  • What if I'm only upgrading the packages in A? This should be OK right? Would the rest of docker images see the changes as well?
xpt
  • 20,363
  • 37
  • 127
  • 216

1 Answers1

5

Overall, each image contains its parent image, either as embedded bytes or as a "hard" reference to an image in your local cache if it is already there.

By "parent" I mean the FROM: someimage instruction in your Dockerfile.

I also wrote "hard" since the reference is actually a sha246 digest of the parent image. If any bit of the parent changes, the digest will be different.

There are three main cases here:

  1. You start with a clear cache (docker image ls -a shows nothing). If you docker pull ... some image from a public registry, it will have its parent embedded. A docker ps -a should show only one image.

  2. If however you already have the parent image in your cache, the docker pull ... won't download the parent again. In that case, the pulled image has a reference to the parent in your cache.

  3. If you build locally from a clear cache, docker will download the parent image and produce a child image with a reference to the parent.

It's still the same result at the end. If you replace the parent image with a newer version, the digest won't be the same.

Docker won't let you delete an image if another image references it. When you push your image to a registry, the parent is embedded (I'm skimping over caching behaviour on the registry side here). I think that you can also embed the parent by using docker export and docker import but I haven't tried it. For instance, docker export B, then delete A and B from your docker cache and docker import B should show only one image.

You can get the actual parent relationship using

docker image inspect <image-id> | grep -E "Id|Parent"

combine that with

docker image ls -a --digests

To inspect the relationships.

Some more info.

When you build an image, the following steps occur:

  1. The build context is sent to the host with the docker daemon. This is basically all the files in the directory that your Dockerfile is in. That's why it is important to make use of .dockerignore to only send files that are COPY'd in your Dockerfile.
  2. The docker daemon creates a temporary container using the FROM instruction in the Dockerfile. This is the imported image, including its own imported images. Then every instruction in the Dockefile is executed inside that container. When a persistent change is made in the temporary container (like COPY'ing), it then saves its state. This effectively adds a layer to the final image.
  3. Once finished executing the DOCKERFILE instructions, the temporary container is destroyed. You are left with the final image.

You can see all the layers in the image using docker history <image-id>

Note that this offers a handy way to debug a Dockerfile. You should see an id for the layers that correspond to persistent instructions in your Dockerfile. You can create a new container from any layer using docker run --rm -it <id next to layer> sh and manually execute the Dockerfile instructions that follow.

Bernard
  • 16,149
  • 12
  • 63
  • 66
  • thanks Alkaline. However, if "_each image contains a full copy of all the images it imports_", then the base image, e.g., `A`, can be freely deleted afterward then, right? But how come I'm getting an error: "_unable to delete xxx (cannot be forced) - image has dependent child images_"? and how to tell such dependent child images BTW?_thx – xpt Mar 15 '19 at 03:07
  • This requires more details. I've added it to the answer above. – Bernard Mar 15 '19 at 06:09
  • thanks for your detailed explanation! I did some further research afterwards (see OP). "_Because each container has its own thin writable container layer, and all changes are stored in this container layer, this means that multiple containers can share access to the same underlying image and yet have their own data state._" This statement is closer to my original thinking as multiple containers can ***share*** the _same_ underlying image, yet from your "_each image contains a full copy of all the images it imports_" sounds like they are not shared. Would you list you info source please? thx. – xpt Mar 15 '19 at 15:30
  • An image is not a shared library. It's a read-only template that is used to create a container. Once an image is built, it is immutable. I think that your confusion is with image *tag names*. You can create a new image that has the same tag (eg "A") as an existing image but if it differs by anything, it will have a different digest than the previous image. Derived images use *digests* to refer to their parent images, not tag names. The tag name is only used at build time in the Dockerfile. This can actually lead to [errors](https://renovatebot.com/blog/docker-mutable-tags). – Bernard Mar 17 '19 at 02:16
  • A my company, we use commit numbers in the tag name to avoid the issue. See https://stackoverflow.com/questions/39433480/dockerfile-versioning-best-practice/39433993#39433993. – Bernard Mar 17 '19 at 02:16
  • Also, check this old but very clear post on how image and layers are related. https://windsock.io/explaining-docker-image-ids/ – Bernard Mar 17 '19 at 02:17