Does the FROM command create a layer and how to check number of layers?

703 views Asked by At

I am reading the official Docker docs, and it is unclear to me how many layers will be created for the below dockerfile.

# syntax=docker/dockerfile:1
FROM ubuntu:18.04
LABEL org.opencontainers.image.authors="[email protected]"
COPY . /app
RUN make /app
RUN rm -r $HOME/.cache
CMD python /app/app.py

As per my understanding, four layers will be created - first from FROM command, then COPY command, and then two layers from each RUN command.

I have another question: how many layers does the FROM command create? Does it create only one layer, or can it create more than one layer?

4

There are 4 answers

1
Paolo On

The key concept is:

Commands that modify the filesystem create a layer.

so in your example, the COPY command and the RUN commands will create one layer each (so 3 in total).

These layers are added to whatever layers the base image has.


$ docker inspect ubuntu:18.04 | jq '.[].RootFS.Layers'
[
   "sha256:b7e0fa7bfe7f9796f1268cca2e65a8bfb1e010277652cee9a9c9d077a83db3c4"
]

given:

FROM ubuntu:18.04
RUN echo hello
RUN echo world
ADD foo .

then:

$ docker build -t foo . 2>/dev/null && docker inspect foo | jq '.[].RootFS.Layers'
[
  "sha256:b7e0fa7bfe7f9796f1268cca2e65a8bfb1e010277652cee9a9c9d077a83db3c4",
  "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
  "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
  "sha256:b422bf2b9916188c7df60fc54a82b9496b0d4c228b0f602e1e0f028a3acdeced"
]

so you end up with whatever layers you had in the base image, plus the new layers. FROM in itself does not create a layer.

1
Developer On

It will create 4 layers. According to the docker documentation:-

enter image description here

enter image description here

  1. The FROM command will create a new Layer.
  2. The COPY command add files so it will update the filesystem. So, it will create a new layer.
  3. The first RUN command builds your application and writes the result to a new layer.
  4. Lastly the second RUN command remove the directory. So, it will create a new layer.

Some are saying that, FROM doesn't create a layer. So, for them, I am sharing documentation part. Please Look:-

enter image description here

0
AudioBubble On

Below is the quote from official documentation. Going by the official documentation it is clear that there will be four layers. Also, the answer from other member proves that there will be four layers.

This Dockerfile contains four commands. Commands that modify the filesystem create a layer. The FROM statement starts out by creating a layer from the ubuntu:18.04 image. The LABEL command only modifies the image’s metadata, and does not produce a new layer. The COPY command adds some files from your Docker client’s current directory. The first RUN command builds your application using the make command, and writes the result to a new layer. The second RUN command removes a cache directory, and writes the result to a new layer. Finally, the CMD instruction specifies what command to run within the container, which only modifies the image’s metadata, which does not produce an image layer.

0
BMitch On

The layers are inherited from the base image, whatever that image contains will be the starting point for your new image.

Those saying that your example will result in 4 layers are correct because ubuntu only has one layer to start with, not because the FROM line is repackaged into a single layer.

$ docker inspect ubuntu:latest --format '{{json .RootFS.Layers}}' | jq .
[
  "sha256:b8a36d10656ac19ddb96ef3107f76820663717708fc37ce929925c36d1b1d157"
]

Lets take a better example, nginx contains several layers:

$ cat df.layers
FROM nginx:latest
RUN echo "hello" >/hello.txt
CMD [ "ls", "-l", "/" ]

$ docker build -t test-layers -f df.layers .
[+] Building 0.9s (6/6) FINISHED
 => [internal] load build definition from df.layers                                                0.0s
 => => transferring dockerfile: 107B                                                               0.0s
 => [internal] load .dockerignore                                                                  0.0s
 => => transferring context: 49B                                                                   0.0s
 => [internal] load metadata for docker.io/library/nginx:latest                                    0.0s
 => [1/2] FROM docker.io/library/nginx:latest                                                      0.1s
 => [2/2] RUN echo "hello" >/hello.txt                                                             0.7s
 => exporting to image                                                                             0.0s
 => => exporting layers                                                                            0.0s
 => => writing image sha256:3bc381601cdb33e4159ac4ee7e3e5724dbd47b01e37c24f2aac0dc1fbd40131e       0.0s
 => => naming to docker.io/library/test-layers                                                     0.0s

Next, lets compare the resulting layers in each of those images:

$ docker inspect nginx:latest --format '{{json .RootFS.Layers}}' | jq .
[
  "sha256:8553b91047dad45bedc292812586f1621e0a464a09a7a7c2ce6ac5f8ba2535d7",
  "sha256:a29cc9587af6488ae0cbb962ecbe023d347908cc62ca5d715af06e54ccaa9e36",
  "sha256:6bc8ae8fb3cf0909b3d9c2e74f6cabe16e6a2322c52cec76fbaecaef47006f1d",
  "sha256:5684be535bf11cb9ad1a57b51085f36d84ae8361eabc2b4c2ba9a83e8b084b20",
  "sha256:93ee76f39c974e4f819e632149c002d6f509aadc5995ec6523a96b337751c8ed",
  "sha256:1040838fe30e6f26d31bde96c514f47ee4bf727b3f1c3c7b045ea3891c1c2150"
]

$ docker inspect test-layers:latest --format '{{json .RootFS.Layers}}' | jq .
[
  "sha256:8553b91047dad45bedc292812586f1621e0a464a09a7a7c2ce6ac5f8ba2535d7",
  "sha256:a29cc9587af6488ae0cbb962ecbe023d347908cc62ca5d715af06e54ccaa9e36",
  "sha256:6bc8ae8fb3cf0909b3d9c2e74f6cabe16e6a2322c52cec76fbaecaef47006f1d",
  "sha256:5684be535bf11cb9ad1a57b51085f36d84ae8361eabc2b4c2ba9a83e8b084b20",
  "sha256:93ee76f39c974e4f819e632149c002d6f509aadc5995ec6523a96b337751c8ed",
  "sha256:1040838fe30e6f26d31bde96c514f47ee4bf727b3f1c3c7b045ea3891c1c2150",
  "sha256:6994e46eed98d24824300283a52d7e6c905936c688366c51a77ab27c2f7b80e4"
]

The first 6 layers are identical, then the RUN in the Dockerfile added a single new layer. These layers are shared since they are part of a content addressable store, it's a hash of the tar+gz of the filesystem changes. They don't get repacked into a single layer which would result in more space to store and bandwidth to distribute.

You can also see the steps in the history of the image, not every step creates a layer, and docker shows these in reverse chronological order:

$ docker history test-layers
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
3bc381601cdb   2 minutes ago   CMD ["ls" "-l" "/"]                             0B        buildkit.dockerfile.v0
<missing>      2 minutes ago   RUN /bin/sh -c echo "hello" >/hello.txt # bu…   6B        buildkit.dockerfile.v0
<missing>      5 days ago      /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B
<missing>      5 days ago      /bin/sh -c #(nop)  STOPSIGNAL SIGQUIT           0B
<missing>      5 days ago      /bin/sh -c #(nop)  EXPOSE 80                    0B
<missing>      5 days ago      /bin/sh -c #(nop)  ENTRYPOINT ["/docker-entr…   0B
<missing>      5 days ago      /bin/sh -c #(nop) COPY file:e57eef017a414ca7…   4.62kB
<missing>      5 days ago      /bin/sh -c #(nop) COPY file:abbcbf84dc17ee44…   1.27kB
<missing>      5 days ago      /bin/sh -c #(nop) COPY file:5c18272734349488…   2.12kB
<missing>      5 days ago      /bin/sh -c #(nop) COPY file:7b307b62e82255f0…   1.62kB
<missing>      5 days ago      /bin/sh -c set -x     && addgroup --system -…   61.6MB
<missing>      5 days ago      /bin/sh -c #(nop)  ENV PKG_RELEASE=1~bullseye   0B
<missing>      5 days ago      /bin/sh -c #(nop)  ENV NJS_VERSION=0.7.11       0B
<missing>      5 days ago      /bin/sh -c #(nop)  ENV NGINX_VERSION=1.23.4     0B
<missing>      5 days ago      /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…   0B
<missing>      5 days ago      /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>      5 days ago      /bin/sh -c #(nop) ADD file:a2378c1b12e95db69…   80.5MB