RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

Dockerイメージの中身を理解する

はじめに

こんにちは、開発課に所属している新卒 1 年目のke-suke0215です。 Dockerのイメージについて「なんとなく業務で使っているけど、いまいちどうなっているのかわかっていない」という状態だったので、今回はDockerイメージの中身がどのように構成されているのかについて調べてみました。

私のようなDockerをなんとなく使っている方が仕組みを理解する手助けになれば幸いです。

Dockerイメージの構成要素

そもそもDockerイメージとは

Dockerイメージは、アプリケーションの実行に必要な情報をまとめたパッケージです。これらのイメージはコンテナ実行の土台となっています。ポータビリティの高さやホストOSから環境を分離できるなどの利点があります。

Dockerイメージの中身を見る

ここでは以下のDockerfileをビルドしたDockerイメージの中身を見ていきます。

FROM ubuntu:latest

最新のubuntuのイメージをベースにするだけの簡単なものです。 中身を見る手順は以下になります。 各コマンドの説明は省略させていただきます。

1. イメージをビルドする

今回はmy-ubuntuという名前のイメージでタグは1.0としてビルドします。

docker build -t my-ubuntu:1.0 .

2. イメージをtarファイルとして保存する

docker saveコマンドを使ってtarファイルにします。

docker save -o my-ubuntu-1.0.tar my-ubuntu:1.0

3. tarファイルを展開する

保存したtarファイルを展開して中身を見れるようにします。

tar xf my-ubuntu-1.0.tar

これでdockerイメージの中身がどのようになっているのか確認できるようになりました。 どのようなものがあるのか確認してみます。

$ ls

0cf20f556e5f1e2fd508acc18bc53e95974c37b6c7304b6cdcbd5bb1bb52df40
e8b8228e36aef7aaaacedf7b10514683933b62424e35956c02e5659aefbcf3bd.json
manifest.json
my-ubuntu-1.0.tar
repositories

my-ubuntu-1.0.tarは展開元のファイルなので、それ以外について詳しく見ていきます。

ハッシュ名のディレクト

0cf20f556e5~~~です。これはイメージを構成する上で核となっているレイヤーと呼ばれるものです。レイヤーについての詳しい説明は後述します。各レイヤーはユニークなハッシュ値によって識別されています。各ディレクトリにはそのレイヤーの内容と関連するメタデータが格納されます。

ハッシュ名のJSONファイル

e8b8228e36a~~~.jsonです。中身は以下のようになっています。

{
  "architecture": "amd64",
  "config": {
    "Env": [
      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    ],
    "Cmd": [
      "/bin/bash"
    ],
    "Labels": {
      "org.opencontainers.image.ref.name": "ubuntu",
      "org.opencontainers.image.version": "22.04"
    },
    "OnBuild": null
  },
  "created": "2023-06-28T08:37:42.319109064Z",
  "history": [
    {
      "created": "2023-06-28T08:37:40.107416121Z",
      "created_by": "/bin/sh -c #(nop)  ARG RELEASE",
      "empty_layer": true
    },
    {
      "created": "2023-06-28T08:37:40.172787047Z",
      "created_by": "/bin/sh -c #(nop)  ARG LAUNCHPAD_BUILD_ARCH",
      "empty_layer": true
    },
    {
      "created": "2023-06-28T08:37:40.235648065Z",
      "created_by": "/bin/sh -c #(nop)  LABEL org.opencontainers.image.ref.name=ubuntu",
      "empty_layer": true
    },
    {
      "created": "2023-06-28T08:37:40.292202878Z",
      "created_by": "/bin/sh -c #(nop)  LABEL org.opencontainers.image.version=22.04",
      "empty_layer": true
    },
    {
      "created": "2023-06-28T08:37:42.055763636Z",
      "created_by": "/bin/sh -c #(nop) ADD file:140fb5108b4a2861b5718ad03b4a5174bba03589ea8d4c053e6a0b282f439ff3 in / "
    },
    {
      "created": "2023-06-28T08:37:42.319109064Z",
      "created_by": "/bin/sh -c #(nop)  CMD [\"/bin/bash\"]",
      "empty_layer": true
    }
  ],
  "moby.buildkit.buildinfo.v1": "eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJhdHRycyI6eyJmaWxlbmFtZSI6IkRvY2tlcmZpbGUifSwic291cmNlcyI6W3sidHlwZSI6ImRvY2tlci1pbWFnZSIsInJlZiI6ImRvY2tlci5pby9saWJyYXJ5L3VidW50dTpsYXRlc3QiLCJwaW4iOiJzaGEyNTY6MGJjZWQ0N2ZmZmEzMzYxYWZhOTgxODU0ZmNhYmNkNDU3N2NkNDNjZWJiYjgwOGNlYTJiMWYzM2EzZGQ3ZjUwOCJ9XX0=",
  "os": "linux",
  "rootfs": {
    "type": "layers",
    "diff_ids": [
      "sha256:59c56aee1fb4dbaeb334aef06088b49902105d1ea0c15a9e5a2a9ce560fa4c5d"
    ]
  }
}

このjsonファイルにはイメージのメタデータなどが記述されています。また、Dockerfileの記述の中でレイヤーにならないものの情報も入っています。レイヤーにならない記述はCMD,ENTRYPOINT,ENVなどがあります。

また、イメージの情報を出力すると以下のようになります。

docker image ls
REPOSITORY          TAG       IMAGE ID       CREATED        SIZE
my-ubuntu           1.0       e8b8228e36ae   1 days ago     77.8MB

IMAGE IDがこのファイル名と一致していることがわかります。なのでハッシュ名のJSONファイルはdockerイメージのidにもなっているのです。

manifest.json

このファイルの中身は以下の通りです。

[
  {
    "Config": "e8b8228e36aef7aaaacedf7b10514683933b62424e35956c02e5659aefbcf3bd.json",
    "RepoTags": [
      "my-ubuntu:1.0"
    ],
    "Layers": [
      "0cf20f556e5f1e2fd508acc18bc53e95974c37b6c7304b6cdcbd5bb1bb52df40/layer.tar"
    ]
  }
]

manifest.jsonはDockerイメージの中核となるファイルの一つです。 各記述はそれぞれ以下を指しています。

  • Config:ハッシュ名のJSONファイル
  • RepoTags:イメージ名とタグ
  • Layers:使用しているレイヤー

このことからmanifest.jsonはこのdockerイメージの設計図のような役割を果たしていると言えそうです。

repositories

repositoriesはの中身は以下のようになっています。

{
  "my-ubuntu": {
    "1.0": "0cf20f556e5f1e2fd508acc18bc53e95974c37b6c7304b6cdcbd5bb1bb52df40"
  }
}

イメージ名とタグとをハッシュにマッピングすることで、具体的なイメージのバージョンや名前を特定する役割を果たします。

レイヤーについて

さきほど少し触れましたが、dockerイメージはいくつかの層(レイヤー)で成り立っています。基本的にDockerfileの記述ごとに1つまたは複数のレイヤーが作成されます。(ハッシュ名のJSONファイルの部分でも触れたように、一部レイヤーにならない記述もあります。)

レイヤー構成の何がいいかというと、差分のみのレイヤーを作成することでディスクスペースを節約できることです。具体的に見ていきましょう。

2つのDockerfileからレイヤー構造を理解する

まずFROMRUNの記述がある下のようなDockerfile(Dockerfile①とする)からイメージを作成してみます。

FROM ubuntu:latest
RUN apt-get update

すると以下のレイヤーが作成されます。

  • FROM ubuntu:latestに基づいて生成されたレイヤー(レイヤーA)
  • RUN apt-get updateに基づいて生成されたレイヤー(レイヤーB)

次に下のようなFROMCOPYの記述があるDockerfile(Dockerfile②とする)からイメージを作成します。

FROM ubuntu:latest
COPY sample.txt /tmp

このとき、生成されるのは以下のレイヤーです。

  • COPY sample.txt /tmpに基づいて生成されたレイヤー(レイヤーC)

Dockerfile①と同じ記述であるFROM ubuntu:latestのレイヤーは作成されず、すでにあるレイヤーAを使ってイメージを構成します。なのでDockerfile②から生成されるイメージはレイヤーAとレイヤーCからできています。

不足しているレイヤーのみを作成してイメージを生成するため、無駄なレイヤーを増やさずディスク容量を節約できるのです。

まとめ

Dockerイメージの内部構成について見てきました。興味がある方の参考になれば幸いです。最後までお読みいただきありがとうございました。

参考文献

Copyright © RAKUS Co., Ltd. All rights reserved.