【連載】世界一わかりみが深いコンテナ & Docker入門 〜 その6:Dockerのファイルシステムってどうなってるの? 〜

コンテナ・Docker
◆ Live配信スケジュール ◆
サイオステクノロジーでは、Microsoft MVPの武井による「わかりみの深いシリーズ」など、定期的なLive配信を行っています。
⇒ 詳細スケジュールはこちらから
⇒ 見逃してしまった方はYoutubeチャンネルをご覧ください
【4/18開催】VSCode Dev Containersで楽々開発環境構築祭り〜Python/Reactなどなど〜
Visual Studio Codeの拡張機能であるDev Containersを使ってReactとかPythonとかSpring Bootとかの開発環境をラクチンで構築する方法を紹介するイベントです。
https://tech-lab.connpass.com/event/311864/

こんにちは、サイオステクノロジー武井です。いよいよ佳境に入ってきた連載「世界一わかりみが深いコンテナ & Docker入門 」ですが、今回はDockerのファイルシステムです。

全7回シリーズでお届けする予定で、今回は第6回目となります。

  1. その1:コンテナってなに?
  2. その2:Dockerってなに?
  3. その3:Dockerfileってなに?
  4. その4:docker-composeってなに?
  5. その5:Dockerのネットワークってどうなってるの?
  6. 今回はこちら → その6:Dockerのファイルシステムってどうなってるの?
  7. その7:実践!!Dockerでアプリケーション開発!!(執筆中)

Dockerのファイルシステム

Dockerは、その1:コンテナってなに?で紹介したDockerリポジトリにたくさんのDockerイメージを格納しています。その容量を節約するために、ちょっと特殊なファイルシステムを採用しています。それは、「OverlayFS(Overlay Filesystem)」というもので、ちょうど画像や写真を編集するソフト「Photoshop」のレイヤーのようなイメージです。OverlayFSのおかげで劇的に容量を効率化出来ているDockerリポジトリですが、まず、そのOverlayFSの仕組みからご説明したいと思います。(他にも色々なファイルシステムがありますが、本ブログでは一番メジャーと思われるOverlayFSについてのみ説明します)

OverlayFSとは?

OverlayFSについてお話します。

OverlayFSのイメージ

OverlayFSとは、端的にいうと、レイヤーを重ね合わせて結合してできるファイルシステムです。ナンノコッチャという感じですが、まず以下の図を見てください。

layer01〜layer03までの3つのディレクトリがあります。OverlayFSではこれら3つのレイヤーを結合してmergeディレクトリのような見せ方をすることが出来ます。

では、それぞれのレイヤーに同じファイル名のファイルがあった場合はどうなるでしょうか?

上図のようにlayer02とlayer03のディレクトリに同じファイル名のファイルBがあった場合、上のレイヤーのほうが見えることとなります。

これはOverlayFSの超簡単なイメージであり、実際はもうちょっと複雑な動きをします。次にそれを説明したいと思います。

OverlayFSの実際の動き

ここではOverlayFSの実際の動きをご説明するとともに、実際にコマンド叩いて実践したいと思います。

まず、その前にちょっと説明させてください。OverlayFSには以下の4つのレイヤーの概念があります。

lowerdir
重ね合わせるレイヤーのベースとなるディレクトリです。先程のイメージ図で記載した「layer01〜layer03ディレクトリ」に相当します。OverlayFSの仕組み上、このディレクトリのファイルに対して変更がされることはないので、基本的にこのディレクトリは読み込み専用でOKです。
upperdir

mergeddirに対して変更をかけたファイルが保存されるディレクトリです。

workdir
内部的に利用される作業用ディレクトリです。
mergeddir
lowerdirとupperdirを結合したディレクトリです。ファイルに対して追加・変更・削除などの操作を行うディレクトリでもあります。

 

説明だけではわかりにくいと思いますし、私も最初この説明だけでは全くわかりませんでした。なので、実践してみたいと思います。以下のような構成をもとに、実際にOverlayFSを構築します。upperdirはここでは使いませんし、一旦その存在を忘れてもらってOKです。OverlayFSはややこしいので、順を追って説明していきます。

lowerdirに相当する2つのディレクトリ「lower01」「lower02」、upperdirに相当するディレクトリ「upper」、mergeddirに相当するディレクトリ「merged」 を作成します。

期待する動きとしては、lower01ディレクトリにあるhoge.txt(中身はhogeと書いてある)と、lower02ディレクトリにあるfuga.txt(中身はfugaと書いてある)の両方のファイルがmergedディレクトリに表示されるというものです。upperは、今回の説明では使いませんし、ややこしいので気にしないでください。

 

では早速実践してみましょう!!OverlayFSを使うためには、毎度おなじみmountコマンドを使います。書式は以下のとおりです。

mount -t overlay [一意の識別名] -o lowerdir=[lowerdirに指定するディレクトリ],upperdir=[upperdirに指定するディレクトリ],workdir=[workdirに指定するディレクトリ] [mergeddirに指定するディレクトリ]

上図の構成を実現するためのコマンドは以下のとおりです。

# mkdir merged
# mkdir upper
# mkdir lower01 lower02
# echo "hoge" > lower01/hoge.txt
# echo "fuga" > lower02/fuga.txt
# mkdir work
# mount -t overlay overlay -o lowerdir=lower02:lower01,upperdir=upper,workdir=work merged

lowerdirに複数のディレクトリを指定する場合は、コロンで区切り、左側に指定するほうが上になります。なので今回の場合は、lower02:lower01と指定します。

では、本当に期待通りの動作になっているか見てみましょう。

# ls merged
fuga.txt  hoge.txt
# cat merged/hoge.txt 
hoge
# cat merged/fuga.txt 
fuga

キタ━━━━(゚∀゚)━━━━!!

期待通りですね。

では、次にlower02ディレクトリにhoge2という内容のhoge.txtを追加してみます。lower02のほうがlower01より上のレイヤーなので、mergedディレクトリのhoge.txtの中身はhoge2になるはずです。図にすると以下のような構成ですね。

 

では試してみませう(๑•̀ㅂ•́)و✧ lowerdirは基本読み取り専用を前提としているので、lowerdir内のファイルを変更する場合、再マウントが必要になります。

# umount overlay
# echo "hoge2" > lower02/hoge.txt
# mount -t overlay overlay -o lowerdir=lower02:lower01,upperdir=upper,workdir=work merged
# ls merged
fuga.txt  hoge.txt
# cat merged/hoge.txt 
hoge2

キタ━━━━(゚∀゚)━━━━!!

期待通りですね。

ファイルを追加するときの動き

ここからは、ファイルの追加・削除・更新のときのそれぞれのユースケースにて、OverlayFSがどのような動きをするのか解説したいと思います。

では、先程ご説明した以下の構成のOverlayFSによって作られたファイルシステムにファイルを追加したいと思います。追加するファイル名「piyo.txt」、その中身はpiyoという文字列のファイルになります。

 

ここで初めてupperdirの出番なのです。OverlayFSにファイルを追加・更新・削除などの変更処理を加える場合、OverlayFSの仕様により、それらは必ずupperdirに反映されます。

ユーザーに見えるのはmergeddirであり、ユーザーはこのmergedディレクトリにファイルを追加するオペレーションをするわけですが、OverlayFS的にはpiyo.txtはupperディレクトリに追加されます。そして、upperディレクトリに書き込まれたファイルはlower01ディレクトリやlower02ディレクトリに書き込まれたファイルと同じように、mergedディレクトリに見えるようになります。図にすると以下のような感じです。

 

つまり、OverlayFSはlowerdirとupperdirを重ね合わせたもの(mergrddir)が、ユーザーに見えるディレクトリになるわけです。

では、実際にやってみたいと思います。

最初はlower01ディレクトリにhoge.txt、lower02ディレクトリにfuga.txtがある状態です。まず、この状態を以下のように作ります。「OverlayFSの実際の動き」でご紹介したことと同じことをしているだけですが。

# mkdir merged
# mkdir upper
# mkdir lower01 lower02
# echo "hoge" > lower01/hoge.txt
# echo "fuga" > lower02/fuga.txt
# mkdir work
# mount -t overlay overlay -o lowerdir=lower02:lower01,upperdir=upper,workdir=work merged

 

ここでmergeddirにpiyo.txtを追加してみましょう。確かにmergeddirディレクトリにファイルが追加されていることがわかります。

# echo piyo > merged/piyo.txt
# ls merged
fuga.txt  hoge.txt  piyo.txt

 

でも、実際に追加したファイルはuperdirディレクトリにあります。

# ls upperdir
piyo.txt

 

ということで、追加したファイルはすべてupperdirの方に反映されることがわかりました。

ファイルを更新するときの動き

次にファイルを更新するときの動きを説明します。「ファイルを追加するときの動き」でご紹介した以下の構成のfuga.txtというファイルの内容をfugaからfuga2に変更してみます。

 

ユーザーがmergedディレクトリ内のfuga.txtをhogeからhoge2に変更するオペレーションをすると、ファイルシステム内部の動きは、まずlower02のfuga.txtがupperディレクトリにコピーされます。

 

次に、uperディレクトリ内のfuga.txtの内容がfugaからfuga2に変更されます。

 

そして、このfuga.txtというファイル名のファイルは、lower02ディレクトリとupperディレクトリの両方に存在してます。OverlayFSの仕様では、より上位の層のレイヤーのファイルがユーザーに見えることとなるので、mergedディレクトリには、upperディレクトリにあるファイルが見えることとなります。つまり以下のような状態です。

 

では実践してみましょう。「ファイルを追加するときの動き」の状態で、mergedディレクトリ内のhoge.txtの内容をhogeからhoge2に変更してみます。

# echo fuga2 > merged/fuga.txt 
# cat merged/fuga.txt 
fuga2

 

mergedディレクトリ内のhoge.txtの内容はhoge2になっているわけですが、upperディレクトリのhoge.txtも合わせてhoge2になっていることがわかります。

# cat upper/fuga.txt 
fuga2

 

つまり、mergedディレクトリに加えた変更は、一旦lower01ディレクトリもしくはlower02ディレクトリのファイルをupperディレクトリにコピーし、それからその内容を変更します。この仕組をコピー・オン・ライトといいます。

ファイルを削除するときの動き

次にファイルを削除するときの動きを見てみます。「ファイルを更新するときの動き」でご紹介した以下の構成から、hoge.txtを削除してみます。

 

hoge.txtを削除すると下図のようになります。upperディレクトリに何やら新しいファイルが出来て、mergeddirディレクトリにはhoge.txtは見えなくなりました。

upperディレクトリに出来たファイルは「ホワイトアウトファイル」と呼ばれるもので、ファイルが削除されたことを表すものです。OverlayFSでは、lowerdirの層にあるディレクトリには変更を加えることはしません。そういう仕様だからです(この仕様のありがたみは、このあとの説明でご説明します)。では、「ファイルが削除された」ということを表現する方法が難しいわけですが、そこでOverlayFSでは、先程のホワイトアウトファイルと呼ばれるものを、削除対象と同名のファイル名でupperディレクトリに置くことで、ファイルシステム的に削除されたことにしてしまうわけです。つまりmergeddirからは見えなくなるということになります。

では、この「ホワイトアウトファイル」というファイルの実態についてですが、これは「キャラクタデバイスファイル」と呼ばれるものです。ここでは、本筋から離れるので多くを語りませんが、Linuxのファイルの種類には「ファイル」「ディレクトリ」「シンボリックリンク」などのほかに「デバイスファイル」というものがあります。Linuxはハードディスクなどの物理的なデバイスや画面への出力など何でもファイルとして表現する特徴があります。ハードディスクなら/dev/sdaみたいなのがありますし、画面になにか文字を出力したい場合は、/dev/stdoutに書き出したりします。ホワイトアウトファイルもこのデバイスファイルの一種です。デバイスファイルには「キャラクタデバイスファイル」と「ブロックデバイスファイル」があり、前者は1文字単位で、後者はある程度まとまった単位で通信します。ここでは、それほどキャラクタデバイスファイルやブロックデバイスファイルについては、知らなくてもいいかもしれません。とにかくキャラクタデバイスファイルは、OverlayFSではファイルが削除されたことを表すマーカーみたいなものなのです。

 

では実践してみましょう。

# rm merged/hoge.txt 
# ls merged
fuga.txt  piyo.txt

確かに削除されてますね。

 

では、upperディレクトリを見てみましょう。

# ls upper
fuga.txt  hoge.txt  piyo.txt
[root@localhost test]# ls -la upper/hoge.txt 
c---------. 1 root root 0, 0 Aug 11 09:16 upper/hoge.txt

あれ?何やら新しいhoge.txtという新しいファイルが出来ていますね。しかも、先頭の一文字が「c」となっています。これがキャラクタデバイスファイルです(ディレクトリだとd、シンボリックリンクだとlとかになっています)。

 

もちろん下位のレイヤー(lower01ディレクトリ、lower02ディレクトリ)には何ら変化はありません。

# ls lower01
hoge.txt
# ls lower02
fuga.txt

 

OverlayFSでのファイルの追加・更新・削除を実施してみました。いかがでしたでしょうか?なんとなく雰囲気は掴んで頂けたかと思います。次は、OverlayFSがどのようにDockerに応用されているかを見てみます。

DockerでOverlayFSを試してみよう!!

では、このOverlayFSをDockerでどのように使われているか体感してみましょう!!

コンテナを作ってOverlayFSの動きを確認する

そのために、以下のDockerイメージを作成します。

  • ベースイメージはCentOS7
  • hogeと書かれたテキストファイルを/root/hoge.txtに配置
  • fugaと書かれたテキストファイルを/root/fuga.txtに配置

以下のDockerfileを作成します。

FROM centos:centos7

ADD hoge.txt /root/hoge.txt

ADD fuga.txt /root/fuga.txt

 

そして以下のコマンドを実行してDockerのイメージを作成します。

# echo "hoge" > /root/hoge.txt
# echo "fuga" > /root/fuga.txt
# docker build -t testapp01 .

 

先ほど作成したイメージでコンテナを起動します。

# docker run -d -it testapp01 /bin/bash

 

mountコマンドでファイルシステムのマウント状況を確認します。

# mount | grep overlay
overlay on /var/lib/docker/overlay2/1ed14519861b8d785d3265512917f70274b3f11192f184e0f025728cad34317d/merged type overlay (rw,relatime,seclabel,lowerdir=/var/lib/docker/overlay2/l/677WTGQVA7IBOMGXUH7Z4DEXLS:/var/lib/docker/overlay2/l/NBS6NXIEXE65SYVIZ7D3HSREZY:/var/lib/docker/overlay2/l/BFQS6YGZG6YK5HU5PPE3SLJ4NE:/var/lib/docker/overlay2/l/5CXN3KDXZ4VBENORX7ZLOHUAQU,upperdir=/var/lib/dockeroverlay2/1ed14519861b8d785d3265512917f70274b3f11192f184e0f025728cad34317d/diff,workdir=/var/lib/docker/overlay2/1ed14519861b8d785d3265512917f70274b3f11192f184e0f025728cad34317d/work)

 

lowdir、upperdirなどに色々何やら指定されていますね。これを先程の図に置き換えると以下のようになります。(図が小さいのでクリックして拡大してみてください)

 

先程のmountコマンドの結果にて、lowerdirオプションでコロン(:)区切りで指定した部分が、OverlayFSのlowerdirの層に該当します。upperdirオプションはOverlayFSのupperdir層に該当します。でもまだ、このコンテナには何も変更を加えていないので何もありません。「overlya on…」の直後に指定されているディレクトリは、OverlayFSのmergeddir層に該当します。

そして、上図から見てわかるように、Dockerfileに記載した1行が、lowerdirの1層に該当します。そしてその順番はmountコマンドのlowerdirオプションで指定されている順番の前にある方がより新しい物となっています。

lowerdirの一番下の層はCentOSのバイナリやライブラリが格納されています。真ん中の層はhoge.txtがある層、一番上はfuga.txtがある層ですね。

つまりDockerfileに記載されている一行ごとが、OverlayFSの1層に該当するのです。ただし、LBAELコマンドなどファイルシステムに影響のないコマンドは、OverlayFSの層は作成されません。

新しくファイルを作ってみる

ここでさらにOverlayFSのコンテナでの動きを確認するために、先程作成したコンテナに新しいファイルを作成してみましょう。

# docker exec -it trusting_lalande /bin/bash
# echo "piyo" > /root/piyo.txt
# exit
# cat /var/lib/docker/overlay2/1ed14519861b8d785d3265512917f70274b3f11192f184e0f025728cad34317d/diff/root/piyo.txt 
piyo

 

upperdirの層に新しくpiyo.txtというファイルが追加されていました。つまり以下のようになっています。

 

先程、「OverlayFSとは?」の「ファイルを追加するときの動き」で説明した動きと全く同じものとなっていることがわかると思います。

【補足】必ず追加されるレイヤー

先程の図では触れていませんでしたが、lowerdirオプションで指定されている一番最初のレイヤー(つまりlowerdirの一番上のレイヤー)には、どのコンテナも必ず以下のようなディレクトリ・ファイル構成を含みます。

dev
├── console
├── pts
└── shm
.dockerenv
etc
├── hostname
├── hosts
├── mtab
└── resolv.conf

どうやらこれは、Dockerが必ず勝手に追加するレイヤーのようです。例えば、.dockerenvがあることによって、これがコンテナかどうかを識別したりなど、そういうシステム的な用途に使うレイヤーのようです。

Dockerリポジトリにイメージをpushしてみよう!!

ここからいよいよ核心に迫ってきます。「なぜOverlayFSを用いると、Dockerイメージの容量を劇的に効率化できるのか?」がわかるまで、あともうちょっとです。そのためには、Dockerリポジトリにイメージをpushして、その仕組を説明する必要があります。

まずは簡単なイメージをpushしてみる

Docker Hubの中身を見ることはできませんので、Dockerのプライベートリポジトリを作ってみます。これは、その名の通り、自分の好きな環境に自分だけのマイDockerリポジトリを作成できるものです。このプライベートリポジトリはDocker Hubと同じ動きをします。

では、プライベートリポジトリを作成します。

# docker run -d -p 5000:5000 registry:2.7.1

これだけです。Docker Hubからregistryという名前のイメージがDockerのプライベートリポジトリで、これpullして、localhostの5000番のポートでアクセスできるようにしています。

では、先程作成したイメージをpushしてみましょう。その前にまず準備があります。まず以下の書式のコマンドでDockerイメージにタグ打ちします。

docker tag [イメージ名] [Dockerリポジトリのホスト名]:[Dockerリポジトリのポート番号]/[Dockerリポジトリ内でのイメージ名]:[Dockerリポジトリ内でのタグ名]

この書式に準じて、以下のコマンドを発行します。

# docker tag testapp01 localhost:5000/testapp01:1.0.0

ホスト名localhost、ポート番号5000のDockerリポジトリに対して、ホストPC内のtestapp01というDockerイメージをイメージ名tespapp01、タグ1.0.0でpushするための準備になります。

docker imagesコマンドを発行すると、このタグうちされたイメージが確認できます。

# docker images
REPOSITORY                 TAG                 IMAGE ID            CREATED             SIZE
testapp01                  latest              2a676d1a10be        3 hours ago         203MB
localhost:5000/testapp01   1.0.0               2a676d1a10be        3 hours ago         203MB
centos                     centos7             7e6257c9f8d8        2 weeks ago         203MB
registry                   2.7.1               2d4f4b5309b1        2 months ago        26.2MB

 

これでpushする準備は整いました。ではpushします。

# docker push localhost:5000/testapp01
The push refers to repository [localhost:5000/testapp01]
7795a963c7fd: Pushed 
293e2fda7682: Pushed 
613be09ab3c0: Pushed 
1.0.0: digest: sha256:cae2b6d2048363477707e2efeebb392cf77769e2fc4aab15e4692c76f9c2b399 size: 943

 

これでpushは完了しました。これらの情報はプライベートリポジトリ内の/var/lib/registry/docker/registry/v2ディレクトリ内に格納されています。しかし、結構複雑な構成になっています。

大きく分けますと、pushした各イメージのメタ情報が格納されているrepositoriesというディレクトリ、メタ情報から参照され実際のコンテンツが格納されるblobsというディレクトリから構成されます。

では、先程pushしたイメージがどのように格納されているか紐解いていきます。

まず、メタ情報を見てみます。先の程の図の通り、pushしたtespapp01というイメージ名で、タグが1.0.0のイメージは、/var/lib/registry/docker/registry/v2/repositories/testapp01/_manifests/tags/1.0.0/current/link というファイルに保存されています。この中身を見てみましょう。

# cat /var/lib/registry/docker/registry/v2/repositories/testapp01/_manifests/tags/1.0.0/current/link 
sha256:cae2b6d2048363477707e2efeebb392cf77769e2fc4aab15e4692c76f9c2b399

 

この値はblobsディレクトリにあるコンテンツを指しています。blobsの下には、コンテンツを一定の規則でsha256でハッシュ化したディレクトリの下に保存されています。この例では、/var/lib/registry/docker/registry/v2/blobs/sha256/ca/cae2b6d2048363477707e2efeebb392cf77769e2fc4aab15e4692c76f9c2b399/dataに保存されていることとなります。

では、このメタ情報の中身を見てみましょう。

{
   "schemaVersion": 2,
   "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
   "config": {
      "mediaType": "application/vnd.docker.container.image.v1+json",
      "size": 3264,
      "digest": "sha256:2a676d1a10be3f73ba4c869690c70dd362e8fc59910827e82e801f50c9444cd0"
   },
   "layers": [
      {
         "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
         "size": 75863188,
         "digest": "sha256:75f829a71a1c5277a7abf55495ac8d16759691d980bf1d931795e5eb68a294c0"
      },
      {
         "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
         "size": 141,
         "digest": "sha256:e1d238d7466d5d8ee1c493d55edfd05b793f2f5e24694eaa427bfab49c1e3fe1"
      },
      {
         "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
         "size": 142,
         "digest": "sha256:ecd34d62868de6dd6bbf39056edd8d2aa5b5b2ec75d8e16e8de3e0e603a2385e"
      }
   ]
}

 

メタ情報がJOSN形式で色々書かれていますが、大事なのはlayersというフィールドです。ここには、配列の形式で、このイメージを構成するレイヤーの情報が格納されています。3つのレイヤーから成り立っていることがわかります。

そして、この順番も重要で、layersフィールドの中に定義されている配列は、上にある方が一番下のレイヤーということになります。このあたりは、後ほどもっと詳細にご説明します。

digestの値は、コンテンツを一定の規則でハッシュ化したもので、blobsディレクトリのディレクトリ名のもととなっています。

では、このメタ情報をもとにblobsディレクトリの中身を実際に見てみることにしましょう。

まず、digestが「75f829a71a1c5277a7abf…」のコンテンツを見てみましょう。これは、先程ご説明したようにblobsディレクトリ配下のディレクトリ名と同じなので、このレイヤーのコンテンツは、/var/lib/registry/docker/registry/v2/blobs/sha256/75/75f829a71a1c5277a7abf55495ac8d16759691d980bf1d931795e5eb68a294c0/dataであることがわかります。

レイヤーのコンテンツは、tarで丸められgunzipで圧縮されていますので、解凍しますと以下のようなファイルが出てきます。

# tar xzvf data
# ls
anaconda-post.log  bin  data  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

 

これはCentOS7のイメージですね。今度は、その一つ下の配列にあり、digestが「e1d238d7466d5d8ee1c493d…」のコンテンツを見てみましょう。このコンテンツは、/var/lib/registry/docker/registry/v2/blobs/sha256/e1/e1d238d7466d5d8ee1c493d55edfd05b793f2f5e24694eaa427bfab49c1e3fe1/dataに格納されています。こちらも同様にtarで丸められgunzipで圧縮されていますので、解凍します。

# tar xzvf data
# ls root
hoge.txt

 

おお!!これは、hoge.txtを追加したレイヤーですね。ここまでやるとわかるかとは思いますが、blobs配下の各ディレクトリは、Dockerfile内で定義した各コマンドが生成したレイヤーは以下の図のように格納されています。

 

先程のイメージをちょっと変更したものをpushしてみる

さて、ここで先程のイメージをちょっと変更したイメージを作って、プライベートリポジトリにpushしてみます。以下のDockerfileを作成してtesapp02というイメージを作ります。testapp01との差分は、hello.txtが追加されたということだけです。

FROM centos:centos7

ADD hoge.txt /root/hoge.txt

ADD fuga.txt /root/fuga.txt

ADD hello.txt /root/hello.txt

 

ビルドしてtestapp02というイメージを作ります。

# echo "hello" > hello.txt
# docker build -t testapp02 .

 

先ほどと同じ要領でタグ打ちして、プライベートリポジトリにpushします。

# docker tag testapp02 localhost:5000/testapp02:1.0.0
# docker push localhost:5000/testapp02

 

さて、プライベートリポジトリの中のファイルを見てみると以下のようになっています。メタ情報が格納されているrepositoriesディレクトリの中に、testapp02というディレクトリが増えています。これは先程pushしたイメージ名のメタ情報になります。

では、先程と同じ要領でtestapp02というイメージのメタ情報を見てみましょう。

{
   "schemaVersion": 2,
   "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
   "config": {
      "mediaType": "application/vnd.docker.container.image.v1+json",
      "size": 3511,
      "digest": "sha256:f6c5ea1064e5aa2f48127eb493a3b76d1dccf86fffbdeb4f62d1dbca68abc463"
   },
   "layers": [
      {
         "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
         "size": 75863188,
         "digest": "sha256:75f829a71a1c5277a7abf55495ac8d16759691d980bf1d931795e5eb68a294c0"
      },
      {
         "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
         "size": 141,
         "digest": "sha256:e1d238d7466d5d8ee1c493d55edfd05b793f2f5e24694eaa427bfab49c1e3fe1"
      },
      {
         "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
         "size": 142,
         "digest": "sha256:ecd34d62868de6dd6bbf39056edd8d2aa5b5b2ec75d8e16e8de3e0e603a2385e"
      },
      {
         "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
         "size": 143,
         "digest": "sha256:b5795aa121e492cac028390d379cc9fdeb654d414a671b9a94674f6d293ffa0f"
      }
   ]
}

 

よーく目を凝らしてみ見ます。layersのフィールドの配列には4つのレイヤーが含まれます。ただしtestapp01のものと比べると、一番最後の配列にあるレイヤー(digestが「b5795aa121e49…」のもの)が増えているのみです。だいたい想像は付きますが、きっとこれは、hello.txtを追加したレイヤーかなと。実際見てみたいと思います。blobsディレクトリを見てみます。

# cd /var/lib/registry/docker/registry/v2/blobs/sha256/b5/b5795aa121e492cac028390d379cc9fdeb654d414a671b9a94674f6d293ffa0f/
# ls
data
# tar xzvf data 
root/
root/hello.txt

やっぱりそうでしたね。

では次にblobsディレクトリを確認してみますと、以下のような構成になっています。

 

つまり、全く異なるイメージであるはずのtestapp01とtestapp02は、同じレイヤーはプライベートリポジトリ上で共有しているのです。

つまり、Dockerは、OverlayFSを利用して、Dockerfile内で発行された各コマンドによって生成されたファイルを「レイヤー」という単位で分けて管理して、同じレイヤーは重複してリポジトリにはアップせず、リポジトリ上で共有することで、容量を節約しているのです。

もしこれが、OverlayFSを利用しないでtestapp01、testapp02をまるごと違うものとして全てリポジトリにpushしたらどうなるでしょうか?CentOSのイメージも含まれるので、その容量はかなり大きくなることが想像できます。

まとめ

いかがでしたでしょうか?DockerのファイルシステムであるOverlayFS、そしてDockerがなぜOverlayFSを使うメリットを説明してみました。随分複雑なことをしているんですね、Docker。泣かせてくれます。でも、苦労してまとめたので、ぜひ見てくれたら幸いです。

アバター画像
About 武井 宜行 269 Articles
Microsoft MVP for Azure🌟「最新の技術を楽しくわかりやすく」をモットーにブログtech-lab.sios.jp)で情報を発信🎤得意分野はAzureによるクラウドネイティブな開発(Javaなど)💻「世界一わかりみの深いクラウドネイティブ on Azure」の動画を配信中📹 https://t.co/OMaJYb3pRN
ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

32人がこの投稿は役に立ったと言っています。


ご覧いただきありがとうございます。
ブログの最新情報はSNSでも発信しております。
ぜひTwitterのフォロー&Facebookページにいいねをお願い致します!



>> 雑誌等の執筆依頼を受付しております。
   ご希望の方はお気軽にお問い合わせください!

1 Comment

  1. この連載を一通り読んで手を動かしてDockerに対する理解がすごい深まりました!
    その7は現在執筆中でしょうか?
    楽しみにしています。

Leave a Reply

Your email address will not be published.


*


質問はこちら 閉じる