Docker の Volume がよくわからないから調べた


前提

  • 本記事執筆時の Docker 最新バージョン: 19.03
  • 書いている人は、業務で本格的に docker を使ったりしておらず、個人開発で適当に使っていたが、よくわからず使っている部分もあったので改めて調べてるレベル。

悩み

  • データを永続化したい場合に、-vオプションや、Dockerfile のVOLUMEや、Compose ファイルのvolumesを使ったりしていたが、違いをよくわかっていない。それぞれ記法もいくつかあるようだがよくわかっていない。
  • コンテナ消した時に勝手に破棄される場合もあってよくわからないので、ホスト側のディレクトリをコンテナ内のディレクトリにマッピングするやつを使いがち。ホスト側のディレクトリが見えているので安心しちゃう。
  • Data Volume コンテナがいいぞ、と言われたので使っているが、なんでいいのかわかってない。
  • とにかくよくわからないまま使っている。

この記事の概要

  • 公式ドキュメントに全部書いてあるので詳しくはこちらへ。この記事は理解のポイントだけおさえてあります。
  • Docker のストレージにはボリュームとバインドマウントと tmpfs の3種類がある。
  • --volumeというオプションでボリュームもバインドマウントも指定できるので、オプション名と機能の名前がかぶっててややこしいから混乱する。
  • -vオプションじゃなく--mountオプションを使おう。
  • Data Volume コンテナは、そういう機能があるわけではなくデザインパターン。
  • 名前付きボリュームを使おう。

Docker のストレージの考え方

コンテナ内で発生したデータは同じコンテナ内のどこかに書き出されるが、コンテナを破棄すると消えてしまいます。データだけはコンテナが消えても保存しておきたかったり、別コンテナで使いたいというニーズに対して、Docker はコンテナ外にデータ保存領域をつくる機能を提供しています。

具体的には3種類。(厳密にいうと、Windows で名前付きパイプっていうのもある)

  • ボリューム
    Docker の管理下でストレージ領域を確保する。Linux なら /var/lib/docker/volumes/以下。
    名前付きボリュームと匿名ボリュームがあり、名前付きの場合は Docker ホスト内で名前解決できるのでアクセスしやすい。匿名ボリュームは適当にハッシュ値が振られる。
    他のプロセスからはさわれないので安全。基本はこれを使うのがよい。
  • バインドマウント
    ホスト側のディレクトリをコンテナ内のディレクトリと共有する。
  • tmpfs
    メモリ上にストレージ領域を確保する。名前の通り一時的な領域となる。用途としては、機密性の高い情報を一時的にマウントする場合などに使う。

-vオプションも、VOLUMEvolumesで指定するものに関しても、基本的には上記のどれかを扱っています。普通に使う分にはボリュームかバインドマウントになるので、以下はそのふたつをメインに説明します。

-v オプション

自分の場合は-vオプションでいろいろ指定ができるのが混乱の原因のひとつでした。
上記3種類のストレージが理解できていればなんてことないのですが。
sample/image:latest というイメージを run するとして、以下に記法による違いを列挙します。

  • 匿名ボリューム
    $ docker container run -v /hoge sample/image:latest
    ホスト側には、Linux なら/var/lib/docker/volumes/以下に領域が確保され、コンテナ内の/hogeディレクトリと共有される。識別のためにハッシュ値が振られる。同じネットワークからそのハッシュ値でアクセスできる。
  • 名前付きボリューム
    $ docker container run -v name:/hoge sample/image:latest
    匿名ボリュームと同様に、ホスト側には、Linux なら/var/lib/docker/volumes/以下に領域が確保され、コンテナ内の/hogeディレクトリと共有される。nameという名前がついているので、同じネットワーク内から name というホスト名でアクセスできる。
  • バインドマウント
    $ docker container run -v ${PWD}/data:/hoge sample/image:latest
    ホスト側のカレントディレクトリ配下のdataディレクトリと、コンテナ側の/hogeディレクトリが共有される。

--mount オプション

Docker 17.06 から単一のコンテナに対しても--mountオプションが使えるようになっており、公式でも-vの代わりにこちらを使うことが推奨されてます。(Swarm では前から使っていた)
キーバリュー形式で各要素が指定できるので、こちらの方が記法としては明快です。長くなるけど。
こんな感じで指定します。
$ docker container run --mount type=volume, src=name, dst=/hoge sample/image:latest

  • type
    volume, bind, tmpfs を指定
  • src
    名前付きボリュームであれば、その名前。バインドマウントであればホスト側のディレクトリを指定します。匿名ボリュームであれば省略します。
    他にsourceとも書ける。
  • dst
    コンテナ側のディレクトリを指定します。
    他にdistination, targetとも書ける

--rm オプションをつけたとき

$ docker container runする時に、--rmオプションをつけると、匿名ボリュームの場合はコンテナ停止と同時にボリュームも破棄されます。
名前付きボリュームの場合は--rmオプションでコンテナが破棄されても破棄されません。

ただ名前付きボリュームであっても、マウントされたコンテナがない状態では、$docker volume prune などでは破棄されます。

Data Volume コンテナは単なるデザインパターン

Volume を調べているとよく出てくる Data Volume コンテナ。
これは Docker の機能としてそういうものがあるのではなく、ボリュームを扱いやすくするためのデザインパターンです。

ボリュームに対してコンテナを紐付けておくとなにがうれしいのか、というと、ボリュームへのアクセスが抽象化できるので扱いやすくなったり、うっかり prune で破棄してしまったりしなくて済むということじゃないかと思います。(たぶん)

結局どれ使ったらいいの?

基本的には名前付きボリュームを使いましょう。
Docker 領域下にボリュームができるから安全ですし、名前でアクセスできるので扱いやすいです。
バインドマウントは、ホスト側の環境に依存しますし、ボリュームに比べて機能が制限されています。ホスト側からデータを注入したいときや、開発時に更新したソースコードやビルドを即時反映したい場合などの場合はバインドマウントを使うのがよいです。

まとめ

  • Docker のストレージにはボリュームとバインドマウントと tmpfs の3種類がある。
  • -vオプションじゃなく--mountオプションを使おう。
  • 基本は名前付きボリュームを使おう。

参考文献