Dockerのストレージドライバを理解する


Dockerのストレージドライバを理解する

はじめに

この記事は「NTTテクノクロス Advent Calendar 2018」の18日目の記事です。

執筆はNTTテクノクロスの花上(NaotoHanaue)です。
普段は、Dockerをメインとしたコンテナ技術やそのオーケストレーションツールであるKubernetes関連の技術検証をしてます。

所属しているチームでは、主にOpenStackなどIaaS関連の技術検証や構築支援を行なっており、その獲得した技術を生かし「OSSクラウド基盤トータルサービス」を提供しておりまして、昨今はOpenStackなどのIaaS基盤に限らず関連性の深いコンテナ技術やPaaS関連に進出していますが、そのコンテナ技術関連を担当している一人が私だったりします。

今回は、タイトルにも記載している通り「Dockerのストレージドライバ」について触れたいと思います。
具体的には、Dockerで利用できるストレージドライバは数種類ありますが、その中でも比較的利用されている
overlayfs(overlay, overlay2)やdevicemapperがどのようにDockerのデータ(Dockerイメージやコンテナのデータ領域として)を管理しているかをまとめてみました。1

Dockerのストレージドライバ

Dockerでストレージドライバとして利用できるものは以下です。

ストレージドライバ サポートファイルシステム
overlay2, overlay xfs, ext4
devicemapper direct-lvm(ループバックデバイスの利用も可能(none production ready))
aufs xfs, ext4
btrfs btrfs
zfs zfs
vfs その他ファイルシステム

また、各ストレージドライバがどのような利用シーンに適合するかは、「Suitability for your workload」としてDockerのオフィシャルサイトに定義されています。

  • overlay2, aufs, and overlay all operate at the file level rather than the block level. This uses memory more efficiently, but the container’s writable layer may grow quite large in write-heavy workloads.
  • Block-level storage drivers such as devicemapper, btrfs, and zfs perform better for write-heavy workloads (though not as well as Docker volumes).
  • For lots of small writes or containers with many layers or deep filesystems, overlay may perform better than overlay2, but consumes more inodes, which can lead to inode exhaustion.
  • btrfs and zfs require a lot of memory.
  • zfs is a good choice for high-density workloads such as PaaS.

Dockerインストール時にデフォルトで適用されるストレージドライバはoverlay2であり、
基本的にLinuxディストリビューションで推奨されるストレージドライバもoverlay2となっております。

利用しているストレージドライバはdocker infoコマンドにて確認可能です。

$ sudo docker info
(snip)
Storage Driver: overlay2
 Backing Filesystem: xfs
 Supports d_type: true
 Native Overlay Diff: true
(snip)

Dockerが利用するストレージの概念

Dockerイメージはread-onlyなレイヤを束ねたものであり、
各レイヤが積み重なり、unionファイルシステムとして1つの統合された形になります。

具体的には、nginxのDockerイメージをDocker Hubから入手すると以下のように複数のハッシュ値を持つ
データをダウンロードしたことがわかります。

$ sudo docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
a5a6f2f73cd8: Pull complete
1ba02017c4b2: Pull complete
33b176c904de: Pull complete
Digest: sha256:5d32f60db294b5deb55d078cd4feb410ad88e6fe77500c87d3970eca97f54dba
Status: Downloaded newer image for nginx:latest

また、docker historyコマンドを利用することでDockerイメージの履歴を確認することができます。
以下出力内でサイズを持つレイヤが実際にダウンロードされたレイヤであり、Dockerイメージを構成するレイヤとなります。

<missing>と表示される部分はかつてのDockerでは全て情報が出力されていましたが、現在表示されない箇所があります。

# docker history nginx
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
568c4670fa80        2 weeks ago         /bin/sh -c #(nop)  CMD ["nginx" "-g" "daem...   0B
<missing>           2 weeks ago         /bin/sh -c #(nop)  STOPSIGNAL [SIGTERM]         0B
<missing>           2 weeks ago         /bin/sh -c #(nop)  EXPOSE 80/tcp                0B
<missing>           2 weeks ago         /bin/sh -c ln -sf /dev/stdout /var/log/ngi...   22B
<missing>           2 weeks ago         /bin/sh -c set -x  && apt-get update  && a...   53.8MB
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV NJS_VERSION=1.15.7....   0B
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV NGINX_VERSION=1.15....   0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  LABEL maintainer=NGINX ...   0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           4 weeks ago         /bin/sh -c #(nop) ADD file:dab9baf938799c5...   55.3MB

Dockerイメージからコンテナを起動する場合は、このread onlyな各レイヤに対してWritableなレイヤが生成され
そこにコンテナ起動後の変更差分が格納されていきます。

devicemapper

devicemapperとは

devicemapperは、Linux上で多くのボリューム管理技術の基盤となるカーネルベースのフレームワークで以下のような特徴があります。

  • シンプロビジョニングでありスナップショット機能を利用したイメージとコンテナの管理
  • devicemapperドライバは、Docker専用のデバイスを使用し、ファイルレベルではなくブロックレベルで動作
  • オペレーティングシステム(OS)レベルでファイルシステムを使用するよりも優れたパフォーマンスを発揮

devicemapperで管理するデータ領域

Dockerは、デフォルト「/var/lib/docker」をデータ領域として利用します。
devicemapperを利用した時のディレクトリ構造は以下のようになります。

今回は、プロダクションとして推奨されているdirect-lvmではなくループバックデバイスを利用して調査しています。

# tree -L 1 /var/lib/docker
/var/lib/docker
├── builder
├── containers
├── devicemapper
├── image
├── network
├── plugins
├── swarm
├── tmp
├── trust
└── volumes

その中でDockerイメージやコンテナとして新たに生成されたデータに関連する情報は主に以下に格納されます。

  • containers
    • 作成したコンテナのログ(jsonファイル)やhosts,、resolv.confやコンテナの各種設定等
  • devicemapper
    • devicemapperをループバックデバイスで利用する場合はそのデバイス
    • Dockerイメージやコンテナが利用するレイヤの実データ(binary)
    • 各レイヤのデバイスの場所を示すメタデータ等
  • images
    • Dockerイメージを構成する各レイヤ情報
    • 各レイヤ間の親子関係等

上記devicemapperやimagesディレクトリ内の詳細は以下です。

領域 説明
/var/lib/docker/devicemapper/devicemapper devicemapperとしてdocker領域の実体を含むデータやメタデータ
devicemapperが利用するデバイスであり、binaryファイル
docker-:-*****-poolとしてOSが把握(lsblkで確認可能)
/var/lib/docker/devicemapper/metadata Dockerイメージやコンテナのレイヤがdeivcemapperのデバイス上どこに存在すかを示すメタデータ
/var/lib/docker/devicemapper/mnt コンテナレイヤのデータ
対象コンテナの/配下がそのまま存在
docker exec -it <コンテナ名> bash等でコンテナにアクセスした時などに見えるファイルシステムと同等
/var/lib/docker/image/devicemapper/imagedb/content/sha256 dockerイメージの情報
docker inspectでDockerイメージを調べた時の出力とほぼ同等な情報が格納
/var/lib/docker/image/devicemapper/layerdb/sha256 各レイヤの情報
どのレイヤを親にもつかやローカルのDockerで管理するためのcache-idを持つ
/var/lib/docker/image/devicemapper/mounts/ Dockerコンテナが起動した際に、その起動中コンテナのファイルシステムを示すレイヤ情報

Dockerイメージの取得からコンテナとして起動した際の/var/lib/docker配下の動きや依存関係を図解してみます。

overlay2

overlay2とは

Dockerは、OverlayFSのためにoverlayとoverlay2という、
2つのストレージドライバを提供しておりoverlay2には以下のような特徴があります。

  • Dockerで利用されるデフォルトストレージドライバ
  • devicemapperとは異なりデバイスレベルではなくファイルレベルで管理
  • メモリを効率的に利用可能

overlay2で管理するデータ領域

overlay2で管理するデータ領域は基本的にdevicemapperと変わりません。
異なる点としては、/var/lib/docker/devicemapperに相当する/var/lib/docker/overlay2におけるデータがデバイスレベルではなくファイルレベルであるため、レイヤの差分が非常にわかりやすくなっています。
以下は、devicemapperの時と同様にnginxのDockerイメージをPullした際の/var/lib/docker/overlay2です。

# tree -L 3 /var/lib/docker/overlay2
/var/lib/docker/overlay2
├── 0dbe83996901ffa50c3f91e7796dbf6f4a4ebbc2691e9219082c239398bc65a9
│   ├── diff
│   │   └── var
│   ├── link
│   ├── lower
│   ├── merged
│   └── work
├── 2bee66b7f6fa7932f70dfcccb6eba8f4cc72673d4d19f9843f478ca4ab1a4d9e
│   ├── diff
│   │   ├── etc
│   │   ├── lib
│   │   ├── tmp
│   │   ├── usr
│   │   └── var
│   ├── link
│   ├── lower
│   ├── merged
│   └── work
├── 9c8baa24b80e10cdac3a43dcd5b1afe9360313c3cd72abe0b2d3756755118ab7
│   ├── diff
│   │   ├── bin
│   │   ├── boot
│   │   ├── dev
│   │   ├── etc
│   │   ├── home
│   │   ├── lib
│   │   ├── lib64
│   │   ├── media
│   │   ├── mnt
│   │   ├── opt
│   │   ├── proc
│   │   ├── root
│   │   ├── run
│   │   ├── sbin
│   │   ├── srv
│   │   ├── sys
│   │   ├── tmp
│   │   ├── usr
│   │   └── var
│   └── link
├── backingFsBlockDev
└── l
    ├── 73Y7EXFJYV3URBOSADKL56LH37 -> ../2bee66b7f6fa7932f70dfcccb6eba8f4cc72673d4d19f9843f478ca4ab1a4d9e/diff
    ├── FH2LGX4HNJEHDAC3VZ23MZR32R -> ../0dbe83996901ffa50c3f91e7796dbf6f4a4ebbc2691e9219082c239398bc65a9/diff
    └── NPYYXCFL72SXAXHU4RSYTT25KU -> ../9c8baa24b80e10cdac3a43dcd5b1afe9360313c3cd72abe0b2d3756755118ab7/diff

39 directories, 6 files

終わりに

簡単ではありますが、Dockerが利用するイメージやコンテナのデータが/var/lib/docker配下でどのように保持されているかをまとめてみました。
少し泥臭いですがDockerイメージからコンテナがうまく起動しない時などのトラブルシューティングなどに役立てて頂けたら幸いです。

明日の「NTTテクノクロス Advent Calendar 2018」は、rkmrHonjoさんです。
Advent Calendar 2018も後半戦ですが、最後までお付き合いお願いします。😆


  1. 残念ながらdevicemapperは18.09以降のfuture releaseから削除されるようです。「docker engine recommended storage drivers」