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

212 lines
9.2 KiB

# Lesson 3: Image layers
1. Change directory into `lesson03`.
1. Build the `hello-world` image with the tag `v1`:
$ docker build . -t hello-world:v1
1. 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...
1. 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.
1. 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
1. 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
1. 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
1. 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?
1. 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?
1. 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
1. Notice that we did not login to the container to do an `ls`, rather, we used
the `exec` command.
1. 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.
1. Back to layers.
1. 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...
1. 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.)
1. 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
1. 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...
1. 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).
1. 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.
1. **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.
1. 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.
1. 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...
1. Notice that the `ADD` line has changed its image id.
1. 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.
1. 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)
1. 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.