Docker イメージのレイヤーの概念とファイルの整理

Docker イメージのレイヤーの理解を深めるために、Dockerホストのコンソール上で牛にMooと言わせるだけのコンテナを動かし、そのコンテナを例にとってDockerイメージのレイヤーの状態を整理してみました。

いまいちDockerイメージのレイヤーの概念がよく分からないという方に参考になれば幸いです。 (※各ファイルのディレクトリの構成は、現在ストレージドライバの標準になっているoverlay2を使った場合の例です。)

サンプルのDockerコンテナで実現すること

牛に"Moo"と言わせるだけです。

f:id:moritamorie:20201218031718p:plain

Dockerfileを作る

Dockerホストの任意のディレクトリ(ここで は cowsay としました)に、以下のようなDockerfileを作ってみます。

FROM debian:buster

RUN apt-get update && apt-get install -y cowsay
  • debian:busterのイメージをDocker hubから取得
  • 牛を表示するアプリケーションcowsay をインストール

というシンプルなDockerfileです。

Dockerfileをビルドし、イメージを作る

Dockerfileがあるディレクトリ内に移動し、作ったDockerfileをビルドし、イメージを作ります。

$ cd cowsay
$ docker build -t test/cowsay .
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM debian:buster
 ---> 6d6b00c22231
Step 2/2 : RUN apt-get update && apt-get install -y cowsay
 ---> Using cache
 ---> 5b2abe87f6d2
Successfully built 5b2abe87f6d2
Successfully tagged test/cowsay:latest

まずは docker images を実行して、test/cowsayのDockerイメージが出来ていることを確認します。

$ docker images test/cowsay:latest

REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
test/cowsay   latest    20961704943e   16 seconds ago   179MB

次に docker historyを実行してイメージの状態を確認してみます。2つのイメージレイヤーが出来ていることを確認できました。

$docker history test/cowsay:latest

IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
5b2abe87f6d2   27 seconds ago   /bin/sh -c apt-get update && apt-get install…   64.5MB    
6d6b00c22231   6 days ago       /bin/sh -c #(nop)  CMD ["bash"]                 0B        
<missing>      6 days ago       /bin/sh -c #(nop) ADD file:6014cd9d7466825f8…   114MB  

この2つのイメージレイヤーはそれぞれDockerホストの異なるディレクトリにファイルの差分が保存されます。

  • 上の方のイメージレイヤー(IMAGE: 5b2abe87f6d2)は、牛が表示されるアプリケーションcowsayをインストールした後のレイヤー
  • 下の方のイメージレイヤ(IMAGE: 6d6b00c22231)は、ベースイメージdebian:busterをPULLした後のレイヤー

です。

f:id:moritamorie:20201218105911p:plain

それぞれのレイヤーの差分がDockerホスト上のどのディレクトリは保存されるかは、コンテナが出来た後docker container inspect というコマンド( 後述)を実行して確認できます。

Dockerホスト上のディレクトリを参照すると、各レイヤーはそれぞれ差分の情報しかもっておらず

  • 上の方のイメージレイヤー(IMAGE: 5b2abe87f6d2)には、アプリケーションcowsayがあるが、PULLしたベースイメージ debian:busterのファイルはない
  • 下の方のイメージレイヤ(IMAGE: 6d6b00c22231)は、ベースイメージdebian:busterのファイルはあるがcowsay はない

ということがわかります。

この例はシンプルなDockerfileなのでレイヤーが少ないですが、Dockerfileが大きくなるほど、レイヤーは多くなり、複数のレイヤーの差分を重ねることで1つのDockerイメージができます。

コンテナを触ってみる

bashで入る① (cowsayがあるイメージからコンテナを作る)

イメージからコンテナを作り、 bash で入ってみます。この際、docker historyの上の方のイメージ(5b2abe87f6d2: cowsayインストール後にできたイメージ)を指定してみます。

$ docker run -it 5b2abe87f6d2 /bin/bash

root@b2b28d1d223f:/# ls /usr/games/cowsay
/usr/games/cowsay

docker historyの上の方のイメージ(5b2abe87f6d2)を指定すると、下のイメージの差分も入っていることを確認できます。( bash コマンドは、ベースイメージdebian:busterの中にあります。)

コンテナに入る際、以下のように名前でイメージを指定しても結果は同じです。

$ docker run -it test/cowsay /bin/bash

bashで入る② (cowsayがないイメージからコンテナを作る)

次に、docker historyの下の方のイメージ(6d6b00c22231: cowsayインストール前のイメージ)を指定してみます。

$ docker run -it 6d6b00c22231 /bin/bash

root@4f9452a93ebd:/# ls /usr/games/cowsay
ls: cannot access '/usr/games/cowsay': No such file or directory

これは cowsay インストール前なので、 cowsay の実行ファイルがないことが確認できます

Dockerホストから cowsay を実行してみます

$ docker run 6d6b00c22231 /usr/games/cowsay "Moo"
 _____
< Moo >
 -----
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

cowsay インストール前のイメージを指定するとエラーが返ります。

$ docker run 6d6b00c22231 /usr/games/cowsay "Moo"
docker: Error response from daemon: OCI runtime create failed: container_linux.go:370: starting container process caused: exec: "/usr/games/cowsay": stat /usr/games/cowsay: no such file or directory: unknown.
ERRO[0000] error waiting for container: context canceled

Dockerホスト上のディレクトリの場所の確認方法

docker container inspect にコンテナ名を指定すると関連するファイルの場所を確認できます。

$ docker container ls -l
CONTAINER ID   IMAGE         COMMAND                  CREATED       STATUS                   PORTS     NAMES
c23fee232a48   test/cowsay   "/usr/games/cowsay M…"   8 hours ago   Exited (0) 8 hours ago             awesome_banach

ドキュメントを参照すると、イメージレイヤのファイルはLowerDirに保存されるようです。

$ $ docker container inspect awesome_banach -f "{{json .GraphDriver.Data}}" | jq .

{
  "LowerDir": "/var/lib/docker/overlay2/4e1a74ac2ac471b3484c196250a7e1be8f36eb8dc64ade4fcc4bdec1e4a95010-init/diff:/var/lib/docker/overlay2/d867fc751ea99450c01330ebdac487c1f12db47f35eeab6c628683a3f762310c/diff:/var/lib/docker/overlay2/6431aa52a32ab412806f379a4830b6cc059c3fb5f9380c8d5b8df298ae005e5b/diff",
  "MergedDir": "/var/lib/docker/overlay2/4e1a74ac2ac471b3484c196250a7e1be8f36eb8dc64ade4fcc4bdec1e4a95010/merged",
  "UpperDir": "/var/lib/docker/overlay2/4e1a74ac2ac471b3484c196250a7e1be8f36eb8dc64ade4fcc4bdec1e4a95010/diff",
  "WorkDir": "/var/lib/docker/overlay2/4e1a74ac2ac471b3484c196250a7e1be8f36eb8dc64ade4fcc4bdec1e4a95010/work"
}

参考資料