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

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

## はじめに

この記事は「[NTTテクノクロス Advent Calendar 2018](https://qiita.com/advent-calendar/2018/ntt-tx)」の18日目の記事です。

執筆は[NTTテクノクロス](https://www.ntt-tx.co.jp/products/osscloud/?utm_source=qiita&utm_medium=ac2018&utm_campaign=18)の花上([NaotoHanaue](https://qiita.com/NaotoHanaue))です。
普段は、Dockerをメインとしたコンテナ技術やそのオーケストレーションツールであるKubernetes関連の技術検証をしてます。

所属しているチームでは、主にOpenStackなどIaaS関連の技術検証や構築支援を行なっており、その獲得した技術を生かし「[OSSクラウド基盤トータルサービス](https://www.ntt-tx.co.jp/products/osscloud/?utm_source=qiita&utm_medium=ac2018&utm_campaign=18)」を提供しておりまして、昨今はOpenStackなどのIaaS基盤に限らず関連性の深いコンテナ技術やPaaS関連に進出していますが、そのコンテナ技術関連を担当している一人が私だったりします。

今回は、タイトルにも記載している通り「Dockerのストレージドライバ」について触れたいと思います。
具体的には、Dockerで利用できるストレージドライバは数種類ありますが、その中でも比較的利用されている
overlayfs(overlay, overlay2)やdevicemapperがどのようにDockerのデータ(Dockerイメージやコンテナのデータ領域として)を管理しているかをまとめてみました。[^1]
[^1]: 残念ながらdevicemapperは18.09以降のfuture releaseから削除されるようです。「[docker engine recommended storage drivers](https://docs.docker.com/storage/storagedriver/select-storage-driver/#docker-engine---community)」

## Dockerのストレージドライバ

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

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

また、各ストレージドライバがどのような利用シーンに適合するかは、「[Suitability for your workload](https://docs.docker.com/storage/storagedriver/select-storage-driver/#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なレイヤが生成され
そこにコンテナ起動後の変更差分が格納されていきます。

![Imagelayer.png](https://qiita-image-store.s3.amazonaws.com/0/318088/e639f72d-f9ec-70cc-c011-11f04c5eb06f.png)

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

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

![devicemapperlayers.png](https://qiita-image-store.s3.amazonaws.com/0/318088/a0db3ed5-5a93-c3f7-2f44-c66b88b100b1.png)

## 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](https://qiita.com/advent-calendar/2018/ntt-tx)」は、[rkmrHonjo](https://qiita.com/rkmrHonjo)さんです。
Advent Calendar 2018も後半戦ですが、最後までお付き合いお願いします。😆