6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Dockerのレイヤーについて調べてみる

Last updated at Posted at 2024-12-08

はじめに

Dockerfileをコードレビューしてもらう機会があり、その際に、「apk add等のコマンドでパッケージをインストールするときはなるべく1行で書きましょう。」という指摘を頂きました。理由としてはそのほうがレイヤーを少なくすることができ、最終的なイメージを小さく保つことができる。ということですが、なぜレイヤーが増えると、実行しているコマンドが同じでもイメージが大きくなるのかをイメージするために調査してみます。

そもそもレイヤーって?

dockerイメージは一つのファイルから形成されるのではなく、複数のファイルの層が積み重なってできています。
その1層分のことをレイヤーといいます。
具体的には、dockerFile等で命令を一つ実行する毎にそのアウトプットがレイヤーとして積み重なっていきます。
このような構造を取ることにより、他の人が作成したイメージに独自の命令を追加し、レイヤーを重ねて行くことにより、オリジナルのイメージを作成することができる仕組みになっています。

例えば下記のDockerFileから作成されるDockerImageは下記のイメージになります。

Dockerfile
FROM alpine:latest
RUN apk add vim
RUN apk add curl

青 シンプル お知らせ メモ インスタグラム投稿.png

実際に調査してみる。

まずは実行

調査対象としたDockerfileは下記の2つです。
単純にalpineのコンテナに、vimとcurlをインストールしてみます。
これらのインストールを1行で行うパターンと2行で行うパターンを比較してみます。

  • インストールを1行でするversion(以下、one_liner)
Dockerfile
FROM alpine:latest
RUN apk update
RUN apk add vim && apk add curl
  • インストールを2行でするversion(以下、two_liners)
Dockerfile
FROM alpine:latest
RUN apk update
RUN apk add vim
RUN apk add curl

これをそれぞれビルドします。

shell
docker build . -t test:one_liner
shell
docker build . -t test:two_liners

できたイメージがこちら

shell
docker images                                                                                                                     
REPOSITORY                                                               TAG               IMAGE ID       CREATED          SIZE
test                                                                     two_liners        xxxxxxxxxxxx   2 hours ago      45.6MB
test                                                                     one_liner         xxxxxxxxxxxx   2 hours ago      45.5MB

確かにtwo_linersのほうが少し大きい...?

イメージの中身を見てみる

イメージを取り出すコマンドは公式のリファレンスにあったのでそちらを使用します。

shell
# image展開用のディレクトリを作成
mkdir one_liner

# imageをセーブ(imageはtarでアーカイブされたものが出力される)
docker image save test:one_liner -o one_liner/image.tar

# ディレクトリを移動
cd one_liner

# tarコマンドで展開
tar -zxf image.tar

# 展開後の一覧を表示
ls                                                                                                                                ─╯
blobs         image.tar     index.json    manifest.json oci-layout    repositories

展開後のファイルを色々見ていると、どうやら、blobs/sha256配下に実際のレイヤーが格納されているのと、manifest.jsonにレイヤー関連の情報が記載されているみたいでした。

shell
cat manifest.json | jq                                                                                                            ─╯
[
  {
    "Config": "blobs/sha256/6c8dbfcfbca671b0b3b9334a41ecebd5201c9666d90af316ccf40532d8b22aba",
    "RepoTags": [
      "test:one_liner"
    ],
    "Layers": [
      "blobs/sha256/977340364f395522c48194db0cf2a81b7643d1bd1378a4c16dc848095a39de7d",
      "blobs/sha256/3c7a6a496478c1d53ad62f997dda2bc0e55440f35c1bca960ad1af48c4edd630",
      "blobs/sha256/be4fa7e81edbbbf5ed6ccc2648e77f4cf69e3dcf82a9dc5c7514d59676437f4b"
    ],
    "LayerSources": {
      "sha256:3c7a6a496478c1d53ad62f997dda2bc0e55440f35c1bca960ad1af48c4edd630": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 2472960,
        "digest": "sha256:3c7a6a496478c1d53ad62f997dda2bc0e55440f35c1bca960ad1af48c4edd630"
      },
      "sha256:977340364f395522c48194db0cf2a81b7643d1bd1378a4c16dc848095a39de7d": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 8464384,
        "digest": "sha256:977340364f395522c48194db0cf2a81b7643d1bd1378a4c16dc848095a39de7d"
      },
      "sha256:be4fa7e81edbbbf5ed6ccc2648e77f4cf69e3dcf82a9dc5c7514d59676437f4b": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 36279808,
        "digest": "sha256:be4fa7e81edbbbf5ed6ccc2648e77f4cf69e3dcf82a9dc5c7514d59676437f4b"
      }
    }
  }
]

確かにone_linerはDockerfileを3行で書いているのでLayersの要素数も3つになっています。そのため3層になっているのがわかります。

一応実際のファイルサイズがmanifest.jsonと一致しているかも確認。

shell
ls -l blobs/sha256                                                                                        ─╯
total 92272
-rw-r--r--@ 1 mame  staff       870 12  8 21:52 077e400a38f167478bf9b43083641094238e96118e7e9abd3cf148300e3bf892
-rw-r--r--@ 1 mame  staff       401 12  8 21:52 1b383c5862d3a41436494b30f284c480904acd3476718431f738360bc35d50a8
-rw-r--r--@ 1 mame  staff   2472960 12  8 21:52 3c7a6a496478c1d53ad62f997dda2bc0e55440f35c1bca960ad1af48c4edd630
-rw-r--r--@ 1 mame  staff       708  1  1  1970 6c6130efc494de845d6254dc7f4df3ff092b1d391766f88e69ffd1ab17026690
-rw-r--r--@ 1 mame  staff      1046 12  8 21:52 6c8dbfcfbca671b0b3b9334a41ecebd5201c9666d90af316ccf40532d8b22aba
-rw-r--r--@ 1 mame  staff       477 12  8 21:52 8695a2373fe88ff4cb060da25e3dce2bdc83fde38c5978a1d000c5bb603d8c7f
-rw-r--r--@ 1 mame  staff   8464384 12  8 21:52 977340364f395522c48194db0cf2a81b7643d1bd1378a4c16dc848095a39de7d
-rw-r--r--@ 1 mame  staff  36279808 12  8 21:52 be4fa7e81edbbbf5ed6ccc2648e77f4cf69e3dcf82a9dc5c7514d59676437f4b

こちらもmanifest.jsonののsizeと値が一致しているのを確認できました。manifest.jsonを見るといろいろわかりそうですね。

同様にtwo_linersのmanifest.jsonを見てみます。

shell
cat manifest.json | jq                                                                      
[
  {
    "Config": "blobs/sha256/6542b0cf7df81f5be54a454f9868324d6e60275fceaf9b9b8aa1027ffda895cb",
    "RepoTags": [
      "test:two_liners"
    ],
    "Layers": [
      "blobs/sha256/977340364f395522c48194db0cf2a81b7643d1bd1378a4c16dc848095a39de7d",
      "blobs/sha256/3c7a6a496478c1d53ad62f997dda2bc0e55440f35c1bca960ad1af48c4edd630",
      "blobs/sha256/744626c64fed99ee7dcf6e2cda66e1cc8c635d5b7b0aaeda579a86afea1fd603",
      "blobs/sha256/6f52a0874715aaaddd490e8b774db30145ff83043db79319fc37d9cc09e74754"
    ],
    "LayerSources": {
      "sha256:3c7a6a496478c1d53ad62f997dda2bc0e55440f35c1bca960ad1af48c4edd630": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 2472960,
        "digest": "sha256:3c7a6a496478c1d53ad62f997dda2bc0e55440f35c1bca960ad1af48c4edd630"
      },
      "sha256:6f52a0874715aaaddd490e8b774db30145ff83043db79319fc37d9cc09e74754": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 5219840,
        "digest": "sha256:6f52a0874715aaaddd490e8b774db30145ff83043db79319fc37d9cc09e74754"
      },
      "sha256:744626c64fed99ee7dcf6e2cda66e1cc8c635d5b7b0aaeda579a86afea1fd603": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 31184384,
        "digest": "sha256:744626c64fed99ee7dcf6e2cda66e1cc8c635d5b7b0aaeda579a86afea1fd603"
      },
      "sha256:977340364f395522c48194db0cf2a81b7643d1bd1378a4c16dc848095a39de7d": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 8464384,
        "digest": "sha256:977340364f395522c48194db0cf2a81b7643d1bd1378a4c16dc848095a39de7d"
      }
    }
  }
]

こちらも、Layersの要素数を見ると4つになっているので4層になっていることがわかります。

中身を見てみる

いろいろ調べていると、manifest.jsonのLayersの順番にDockerfileで記載したコマンドによって作成されたレイヤーが対応しているみたいでした。
そのため、one_linerのLayersの3行目のファイルを展開すれば、vimとcurlのファイルが確認できるはず。

shell:/one_liner
# 展開用にディレクトリを作成
mkdir vim_curl

# 上記で作ったディレクトリに展開
tar -zxf blobs/sha256/be4fa7e81edbbbf5ed6ccc2648e77f4cf69e3dcf82a9dc5c7514d59676437f4b -C vim_curl

# vim_curlディレクトリの中身を確認
ls vim_curl                                                                  
etc lib usr

# 更に中身を確認
ls vim_curl/usr/bin/ 
curl  ex    rview rvim  view  vim   xxd

確かに、vimとcurlがインストールされていることがわかりました。そのため調査対象は間違っていなさそうです。

サイズを比較してみる

ここまでの調査からone_liner3行目のbe4fa7e81edbbbf5ed6ccc2648e77f4cf69e3dcf82a9dc5c7514d59676437f4bとtwo_linersの3行目と4行目である744626c64fed99ee7dcf6e2cda66e1cc8c635d5b7b0aaeda579a86afea1fd603,6f52a0874715aaaddd490e8b774db30145ff83043db79319fc37d9cc09e74754の合計値との比較がサイズの比較になりそうです。

  • 計算
    (31184384 + 5219840) - 36279808 = 124416(byte)
    124416/1024 = 121.5(kb)

なので121.5kb差となりました。MB単位で表示したときに0.1MB差だったこととも結果はあっていそうです。

考察

思った程差がでないなと思ってちょっとがっかりしていましたが、公式ドキュメントのベストプラクティスのページを読んでいるとこんな記載がありました。

Docker の古いバージョンでは、性能を確保するために、イメージ・レイヤ数の最小化が重要でした。以下の機能は、この制限を減らすために追加されたものです。
RUN 、 COPY 、 ADD 命令のみレイヤを作成します。他の命令では、一時的な中間イメージ(temporary intermediate images)を作成し、構築時の容量は増えません。

上記の記載から、docker側の性能改善によって昔ほどレイヤー数を無理に減らさなくても良いということなのかもしれないのかなと解釈しました。
そのため、単純にモジュールを追加していくだけの処理であれば、そこまで容量に差がでないのかもしれませんね。
とはいえ、途中でファイルの削除等を挟む場合には、今でも1行で書いたほうがサイズを減らせるのではないかとも思いました。

まとめ

今回の調査によって、1行で書くことにより劇的にサイズを少なくするという期待する結果を得ることはできませんでしたが、dockerのレイヤーがどのようなものなのかを具体的に見ることができ、dockerに対する理解が深まった気がしました。
また、今回はパッケージを2つだけインストールした場合で検証しましたが、更に多くのパッケージをインストールする場合等に効果を発揮するのかもしれません。
余裕があったらまた検証にチャレンジしてみたいと思います!

6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?