You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
programming-basics-2022/10_docker/lesson03/README.md

9.2 KiB

Lesson 3: Image layers

  1. Change directory into lesson03.

  2. Build the hello-world image with the tag v1:

     $ docker build . -t hello-world:v1
    
  3. Each Docker image consists of a sequence of file system layers with the later layers overwriting earlier ones. Each layer corresponds to an instruction in the Dockerfile. Let's look at the layers. (Note: in this and subsequent displays I leave off the CREATED column to save space).

     $ docker history hello-world:v1
     IMAGE          CREATED BY
     f38d648975d5   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/roo...
     09b895731b10   /bin/sh -c chmod a+x /root/run.sh
     07d951eff6a0   /bin/sh -c #(nop) ADD file:ae2a94e6e79a95786â¦...
     34fd6d6fbb15   /bin/sh -c #(nop)  LABEL maintainer=adamhl@s...
     589ac6f94be4   /bin/sh -c #(nop)  CMD ["bash"]
     <missing>      /bin/sh -c #(nop) ADD file:422aca8901ae3d869...
    
     $ docker history debian:buster-slim
     IMAGE          CREATED BY
     589ac6f94be4   /bin/sh -c #(nop)  CMD ["bash"]
     <missing>      /bin/sh -c #(nop) ADD file:422aca8901ae3d869...
    
  4. Notice that the hello-world:v1 image's first two layers are the same as debian:buster-slim's layers. This reflects the fact that hello-world:v1 starts FROM the debian:buster-slim image.

  5. The subsequent layers of the hello-world:v1 image each correspond to the commands in the Dockerfile. Here is the Dockerfile again as a reminder:

     # Dockerfile
     FROM debian:buster-slim
     LABEL maintainer="adamhl@stanford.edu"
    
     ADD run.sh /root/run.sh
     RUN chmod a+x /root/run.sh
     CMD /root/run.sh
    
  6. When you create a running Docker container using docker run the Docker system loads your image with each layer being read-only with a final, new layer being added. This last layer is writable. Let's try it.

     $ docker run --rm --name=fuzzle busybox /bin/sh -c "sleep 10000" &
     $ docker exec -ti fuzzle /bin/sh (we "login" to the running container)
     / # cd /root; ls
     ~ # date > date.txt; ls -l
     -rw-r--r--    1 root     root             0 Jan 16 03:56 date.txt
     ~ # cat /root/date.txt
     Sat Jan 16 03:57:15 UTC 2021
     ~ # exit
    
  7. The container is still running. Let's make sure the file we created is still there.

     $ docker exec -ti fuzzle /bin/sh
     ~ # cat /root/date.txt
     Sat Jan 16 03:57:15 UTC 2021
     ~ # exit
    
  8. However, once the container exits, that final writable layer is thrown away. It does not persist. Let's see.

     $ docker rm fuzzle
     # You should get an error here. Why?
    
  9. To stop a running container from the outside use the docker kill command.

     $ docker kill fuzzle
     $ docker rm fuzzle
     # This last command returns an error. Why?
    
  10. To see that the file /root/date.txt is really gone, let's start the container again and look.

     $ docker run --rm --name=fuzzle busybox /bin/sh -c "sleep 10000" &
     $ docker exec fuzzle ls -l /root
    
  11. Notice that we did not login to the container to do an ls, rather, we used the exec command.

  12. To reiterate, changes made to the containers top writable layer do not persist. If you want the container to make persistent changes to files another mechanism is needed such as mounting an external volume or writing to some other persistent data store; more on that in a later lesson.

  13. Back to layers.

  14. When we looked at the history of the hello-world:v1 image we saw this:

     $ docker history hello-world:v1
     IMAGE          CREATED BY
     f38d648975d5   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/roo...
     09b895731b10   /bin/sh -c chmod a+x /root/run.sh
     07d951eff6a0   /bin/sh -c #(nop) ADD file:ae2a94e6e79a95786â¦...
     34fd6d6fbb15   /bin/sh -c #(nop)  LABEL maintainer=adamhl@s...
     589ac6f94be4   /bin/sh -c #(nop)  CMD ["bash"]
     <missing>      /bin/sh -c #(nop) ADD file:422aca8901ae3d869...
    
  15. Under the IMAGE column are hex strings. Those correspond to the SHA256 hash of the new layer's content. (More precisely, the SHA256 hash of the layers configuration object.)

  16. Think of these hashes (roughly) corresponding to git commit hashes. The layers and their hashes are useful for they tell Docker when a layer has changed. We use a copy of Dockerfile with one small change:

     # Dockerfile-v2
     FROM debian:buster-slim
     LABEL maintainer="adamhl@stanford.edu"
    
     ADD run.sh /root/run.sh
     RUN chmod a+rx /root/run.sh  # This is the changed line.
     CMD /root/run.sh
    
  17. Build the image and look at its history:

     $ docker build . -f Dockerfile-v2 -t hello-world:v2
     $ docker history hello-world:v2
     IMAGE          CREATED BY
     0dd6cfe9bb51   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/roo...
     39085b76a64f   /bin/sh -c chmod a+rx /root/run.sh
     07d951eff6a0   /bin/sh -c #(nop) ADD file:ae2a94e6e79a95786...
     34fd6d6fbb15   /bin/sh -c #(nop)  LABEL maintainer=adamhl@s...
     589ac6f94be4   /bin/sh -c #(nop)  CMD ["bash"]
     <missing>      /bin/sh -c #(nop) ADD file:422aca8901ae3d869...
    
     $ docker history hello-world:v1
     IMAGE          CREATED BY
     f38d648975d5   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/roo...
     09b895731b10   /bin/sh -c chmod a+x /root/run.sh
     07d951eff6a0   /bin/sh -c #(nop) ADD file:ae2a94e6e79a95786â¦...
     34fd6d6fbb15   /bin/sh -c #(nop)  LABEL maintainer=adamhl@s...
     589ac6f94be4   /bin/sh -c #(nop)  CMD ["bash"]
     <missing>      /bin/sh -c #(nop) ADD file:422aca8901ae3d869...
    
  18. Note that the layers have the same image ID except for the layers starting with the one we changed. The top layers have different id's since even though they are the same Dockerfile command they derive from the layer that changed (again, think of a git commit).

  19. This has the pleasant result that if the first N Dockerfile commands do not change but the N+1'st command does change, then docker build will use the cached layers for the first N layers and only rebuild from layer N+1 onwards. This means rebuilds can be quite fast.

  20. Important. The image changes whenever one or more of the Dockerfile ADD, COPY, or RUN command lines change. Even adding whitespace to a line that has no real effect can trigger a new layer.

  21. What does it mean for a Dockerfile line to change? Either the line in the Dockerfile itself changes (as we saw above) or, for commands that add files from the local environment, the file being added changes. For example, if we added a comment line to run.sh this counts as a change to the ADD run.sh /root/run.sh line and would trigger a layer rebuild from that layer forward.

  22. Edit the file run.sh by adding a comment and then rebuild:

     #!/bin/sh
     # An extra comment
     echo "Hello, world."
     exit 0
    
     $ docker build . -t hello-world:v3
     $ docker history hello-world:v1
     IMAGE          CREATED BY
     2131b85a91d5   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/roo...
     30a489d1e5b0   /bin/sh -c chmod a+x /root/run.sh
     94667dcd1388   /bin/sh -c #(nop) ADD file:ae2a94e6e79a95786...
     ebca792f7949   /bin/sh -c #(nop)  LABEL maintainer=adamhl@s...
     589ac6f94be4   /bin/sh -c #(nop)  CMD ["bash"]
     <missing>      /bin/sh -c #(nop) ADD file:422aca8901ae3d869...
     $ docker history hello-world:v3
     IMAGE          CREATED BY
     acc54a5d1b49   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/roo...
     82b9cde2c517   /bin/sh -c chmod a+x /root/run.sh
     2a19083a38b0   /bin/sh -c #(nop) ADD file:997845d6d2fdc9492...
     ebca792f7949   /bin/sh -c #(nop)  LABEL maintainer=adamhl@s...
     589ac6f94be4   /bin/sh -c #(nop)  CMD ["bash"]
     <missing>      /bin/sh -c #(nop) ADD file:422aca8901ae3d869...
    
  23. Notice that the ADD line has changed its image id.

  24. A gotcha: your Dockerfile installs a package downloaded from an external repository (like Debian). You build the image. A little while later the package is updated in the Debian repository. You rebuild the container thinking the rebuild will catch this change. But is does NOT. The docker build only notices changes to the Dockerfile itself, not to sources external to the build environment.

  25. A way around this gotcha. If you want to be sure that every time you build your container it rebuilds each layer regardless of any changes to the Dockerfile use the --no-cache option. This has the advantage of reliability but the disadvantage that it can take significantly more time.

     $ docker build . -t hello-world:v3            (this will be fast)
     $ docker build . --no-cache -t hello-world:v3 (this will slower)
    
  26. Another advantage of Docker caching layers is that different images built off the same layer help save space. Recall that images layers are loaded read-only, so if two different running containers are built off the same layer, both containers can point to the same physical file. This is why spawning a second container running on the same image as the first can be so quick: the base layer is already loaded into memory so all that has to be done is add the final writable layer that is distinct for each container.