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.
212 lines
9.2 KiB
212 lines
9.2 KiB
2 years ago
|
# 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.
|