Best practices for writing Dockerfiles によると、 FROM/COPY/RUN/CMD の 4 種類の操作ごとにレイヤーが作成されるらしい。 COPY/RUN/CMD は 1 レイヤ増えるだけなのは分かるが、FROM 部分は 1 レイヤだけ増えるんだろうか。それとも親のコンテナイメージのレイヤー分だけ増えるんだろうか。
Each instruction creates one layer:
FROM creates a layer from the ubuntu:18.04 Docker image.
COPY adds files from your Docker client’s current directory.
RUN builds your application with make.
CMD specifies what command to run within the container.
結論: 親のコンテナイメージのレイヤー分だけ増える。まあそうだよね。
検証内容
次のような Dockerfile からイメージを作成し、pull したときにダウンロードされるレイヤー数を確認する。
FROM nginx:alpine RUN touch app.txt
イメージをビルドする。
# docker build -t my-nginx -f Dockerfile . Sending build context to Docker daemon 2.048kB Step 1/2 : FROM nginx:alpine alpine: Pulling from library/nginx 540db60ca938: Pull complete 0ae30075c5da: Pull complete 9da81141e74e: Pull complete b2e41dd2ded0: Pull complete 7f40e809fb2d: Pull complete 758848c48411: Pull complete Digest: sha256:0f8595aa040ec107821e0409a1dd3f7a5e989501d5c8d5b5ca1f955f33ac81a0 Status: Downloaded newer image for nginx:alpine ---> a6eb2a334a9f Step 2/2 : RUN touch app.txt ---> Running in a2908d15addd Removing intermediate container a2908d15addd ---> 56d6c8e6e54c Successfully built 56d6c8e6e54c Successfully tagged my-nginx:latest # docker images REPOSITORY TAG IMAGE ID CREATED SIZE my-nginx latest 56d6c8e6e54c About a minute ago 22.6MB nginx alpine a6eb2a334a9f 11 days ago 22.6MB
Dockerhub に push する。
# docker tag 56d6c8e6e54c kimullaa/my-nginx:latest # docker push kimullaa/my-nginx:latest The push refers to repository [docker.io/kimullaa/my-nginx] ed8e996ae7b6: Pushed 058eb06e0efd: Mounted from library/nginx 2f2df3ae0cad: Mounted from library/nginx 2b60f0243825: Mounted from library/nginx 96131b349b16: Mounted from library/nginx a42a23cd7b07: Mounted from library/nginx b2d5eeeaba3a: Mounted from library/nginx latest: digest: sha256:5fe47980b1c632aa4536500a529eb7e2f41869b29b9140098ef975e18fc758ba size: 1774
ローカルのイメージを削除し、push したイメージを pull する。このとき、RUN を 1 こしか書いてないのに複数レイヤーがガウンロードされることから、FROM で指定した親のコンテナイメージのレイヤー分だけ増えてる様子がわかる。
# docker rmi -f 56d6c8e6e54c Untagged: kimullaa/my-nginx:latest Untagged: kimullaa/my-nginx@sha256:5fe47980b1c632aa4536500a529eb7e2f41869b29b9140098ef975e18fc758ba Untagged: my-nginx:latest Deleted: sha256:56d6c8e6e54c3d58e819f5b50e09990b2df139f20286938361ece7c441d24db7 Deleted: sha256:882a91f1f0cffc7159344c9205cbcdc3de679af92d56a3b50dd3c16ec5ee686c # docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx alpine a6eb2a334a9f 11 days ago 22.6MB # docker pull kimullaa/my-nginx:latest latest: Pulling from kimullaa/my-nginx 540db60ca938: Already exists 0ae30075c5da: Already exists 9da81141e74e: Already exists b2e41dd2ded0: Already exists 7f40e809fb2d: Already exists 758848c48411: Already exists 76c667677d7e: Pull complete Digest: sha256:5fe47980b1c632aa4536500a529eb7e2f41869b29b9140098ef975e18fc758ba Status: Downloaded newer image for kimullaa/my-nginx:latest docker.io/kimullaa/my-nginx:latest
ついでにコンテナ実行して overlayfs の様子をみても、lowerdir に 7 レイヤ(FROM で指定した nginx が 6 レイヤ。RUN が 1 レイヤ)ある様子がわかる。
# docker run -d kimullaa/my-nginx f46c13e0298f048f1712a738c3f7283c284fd5ffb0541e64d1972262b3837b4f # docker exec -it f46c13e0298f048f1712a738c3f7283c284fd5ffb0541e64d1972262b3837b4f mount | grep overlay overlay on / type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/2G4GXEZAC6MBNYE5OKZR6IYDW5:/var/lib/docker/overlay2/l/O5GXX6WYSTHI5QLCH5GJ3OVD6E:/var/lib/docker/overlay2/l/ZWBFPQ7L4YSW3PVT7M5SUZG4ZS:/var/lib/docker/overlay2/l/VIATNKZE266STVGHR2CDTXOJVW:/var/lib/docker/overlay2/l/I6EPLY4J6TFHDFTELI2V4FSGI3:/var/lib/docker/overlay2/l/375Y7S4FQFOF5GEAG3ZFSZNTNM:/var/lib/docker/overlay2/l/ASNXD5OCHKNRG6XFYDPZWO7D3S:/var/lib/docker/overlay2/l/YXWBCZCQNRMR6VF2JXB4FAI6VR,upperdir=/var/lib/docker/overlay2/aa2d0884239fe4d6a91915e60a71de605e1c0db95e2cd2bc7fcbd44665144c16/diff,workdir=/var/lib/docker/overlay2/aa2d0884239fe4d6a91915e60a71de605e1c0db95e2cd2bc7fcbd44665144c16/work,xino=off)
蛇足
ちなみに以前の記事で実行時に重ねられるレイヤーの上限があることを確認した。
ということで、あまり大量のレイヤーから出来ているイメージを FROM に指定するのは良くなさげ。まあでも、最近は Multi-Stage Build があるから問題にならないか。
ちなみにじゃあ親イメージの親イメージの…と再帰的にさかのぼっていくと最終的にどうなるの?というところだけど、今回は scratch イメージっぽい(というかディストリビューションのイメージが FROM に指定されている場合はほぼ scratch っぽい)。 nginx:alpine から親の alpine イメージにさかのぼると、FROM が scratch になっている。
FROM scratch ADD alpine-minirootfs-3.13.5-x86_64.tar.gz / CMD ["/bin/sh"]
引用元: alpine の Dockerfile
で、このイメージはレイヤーが 1つ。え、FROM があるから scratch ぶんだけ 1 増えるんじゃないのという感じだけど、Dockerhub によると docker1.5.0 からは 何もしない虚無イメージらしい。
This image is most useful in the context of building base images (such as debian and busybox) or super minimal images (that contain only a single binary and whatever it requires, such as hello-world).
As of Docker 1.5.0 (specifically, docker/docker#8827), FROM scratch is a no-op in the Dockerfile, and will not create an extra layer in your image (so a previously 2-layer image will be a 1-layer image instead).
引用元: Dockerhub scratch