-1

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="org@example.com"
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?

hagrawal7777
  • 14,103
  • 5
  • 40
  • 70
  • 3
    I think it would be 3 layers plus how many layers are in `ubuntu:18.04` – erik258 May 08 '23 at 15:00
  • FROM will only create one ("container") layer, but if your Docker engine doesn't have the base image, it will have to pull and build it first. https://jessicagreben.medium.com/digging-into-docker-layers-c22f948ed612 https://stackoverflow.com/a/67927907/2125723 – ryanwebjackson May 08 '23 at 16:04
  • @ryanwebjackson, FROM certainly doesn't create container layer. Container layer will be created at runtime. – hagrawal7777 May 08 '23 at 18:17
  • @hagrawal7777 was I able to resolve your doubts? – Paolo May 10 '23 at 16:14
  • Hi @Paolo, you were really helpful in understanding this, thank you. I have already done +1 but I cannot accept the answer right now as I am looking for a step-by-step proof (for myself as well as for future visitors) how anyone can checks the layers. I will try if I can put a answer, otherwise I will accept your answer. – hagrawal7777 May 11 '23 at 05:02

4 Answers4

1

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.

Paolo
  • 21,270
  • 6
  • 38
  • 69
  • @hagrawal7777 not sure if you saw my comments, but the layer `"sha256:b7e0fa7bfe7f9796f1268cca2e65a8bfb1e010277652cee9a9c9d077a83db3c4"` is the layer of the base image, it is not generated by the `FROM` statement – Paolo May 09 '23 at 06:50
1

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

Developer
  • 1,297
  • 1
  • 4
  • 15
  • **Comments have been [moved to chat](https://chat.stackoverflow.com/rooms/253564/discussion-on-answer-by-developer-how-many-layers-will-get-created-especially-b); please do not continue the discussion here.** Before posting a comment below this one, please review the [purposes of comments](/help/privileges/comment). Comments that do not request clarification or suggest improvements usually belong as an [answer](/help/how-to-answer), on [meta], or in [chat]. Comments continuing discussion may be removed. – Samuel Liew May 09 '23 at 13:55
0

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

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
BMitch
  • 231,797
  • 42
  • 475
  • 450