skip to Main Content

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

Answers


  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.

    Login or Signup to reply.
  2. 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

    Login or Signup to reply.
  3. 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.

    Login or Signup to reply.
  4. 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
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search