volumesを使うとデータを永続化できる
Dockerコンテナでデータを永続化させるのに、Volumesという仕組みがあります。
永続化とは、コンテナ内で作成したデータをそのコンテナやイメージを削除しても残すようにする仕組みのこと。
Volumesはコンテナごとに独立したマウントポイントを作る以外にも、他のコンテナとも共有させることもできます。
さらに、ホストOSから隔離して永続化したデータ保存先を確保できるため、ホスト側の影響を受けにくいなどのメリットがあります。
ちなみに、データ永続化方法としてはvolumesの他に、bind mountsなどがあります。
docker runコマンドの-vオプションなどで馴染みのあると思います。
簡単に違いをまとめておくと、bind mountsの方が手軽に使えますが、ホスト側のディレクトリ構造に依存するデメリットがあります。
本記事はvolumesの特徴について説明していますが、bind mountsの使い方は扱いません。
volumesとbind mountsを比較するとっても分かりやすい記事があるので、興味のある人はそちらもどうぞ。
簡単なvolumesの導入方法
Dockerコマンドからのvolumes使用
公式ドキュメントの例をみていきます。
事前にコマンドでvolumesを作成しておきます。
docker volume create myvol2
以下はvolumes "myvol2"をマウントしてnginxイメージを起動させるコマンド。
docker run -d \
--name devtest \
--mount source=myvol2,target=/app \
nginx:latest
これでコンテナ内の/app/にmyvol2というvolumesがマウントされる。
作成されたvolumesを確認するには以下のコマンドを実行します。
docker volum ls
参考:Use volumes | Docker Documentation
Docker composeからのvolumes使用
docker-compose.ymlでの"volumes"の定義方法には2種類あります。
- サービス内で定義(bind mounts) →サービスのみで専有
- トップレベルで定義(本記事で扱うvolumes) →サービス間で共有させることができる
Docker composeでは(volumesもbind mountsも)- volumesキーで記述するので、違いが分かりにくいですね。
実際に公式ドキュメントの例を参考にしてみましょう。
version: "3.8"
services:
web:
image: nginx:alpine
volumes:
- type: volume #これが今回メインテーマの volumes
source: mydata
target: /data
volume:
nocopy: true
- type: bind #これが定番のbind mounts
source: ./static
target: /opt/app/static
db:
image: postgres:latest
volumes:
- "/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock"
- "dbdata:/var/lib/postgresql/data"
volumes:
mydata:
dbdata:
ホストOS上のディレクトリをbind mountsするときは、上の例でいうと source: ./static のように指定します。
それに対してvolumesの場合は、ymlファイルのトップレベルで定義された(上の例では mydataとdbdata)volumesを指定します(source: mydata)。
トップレベルで定義しているvolumesはどのサービスからもアクセスすることができます。上の例でいうと、mydataとdbdataの2つのvolumesはどちらも、webとdb両方のコンテナでマウントできます。
参考:Compose file version 3 reference | Docker Documentation
volumes作成後のメンテナンス
Volumesの一覧を確認したいときは
docker volume ls
でみられます。
また、あるVolumeのメタ情報や実体のパスは以下のコマンドで調べられます。
docker volume inspect NAME
volumesからデータを救い出す
ここからが本題です。
とある事情でmysqlを動かすコンテナがマウントしていたvolumesからデータを救出したときの手順です。
環境は以下の通り
- Windows 10 Pro 1903
- Docker Desktop community (Docker for Windows) 2.2.0.3
事の背景
MySQLのデータ保存先(デフォルト:/var/lib/mysql)をDockerホストのどこかに保存することでデータを永続化させたい。かなりメジャー(というかほぼ全件?)な要求ですね。
永続化を実現するために、ホスト側のパスをそのまま書いてマウントするbind mountsが一般的です。
volumes:
- ./mysql_data:/var/lib/mysql
しかし!
この方法ではDocker for Windows ではパーミッションエラーとなって実行できません。
(直でWindows側にアクセスするため何らかの抵抗勢力に合うのでしょう)
そこで、docker-composeファイルではトップレベルのvolumesを定義し、それをマウントするようにして運用していました。
…そんな折、突然MySQLのコンテナが起動できなくなったため、トップレベルで定義しているvolumeからデータファイルを救出する必要が生じました。Windows(ホスト)からでは、今回bind mountsしているわけではないのでデータファイルが直接見れないですし、volumesの中身は直接見れません。
さてどうやってvolumesの中身を救出しましょう?
volumesからデータをhostへ救出する
そこで簡易的にvolumeをマウントするだけのコンテナを作成し、ファイルを取り出すことにしました。
あるボリュームをただマウントして中身を見るだけなら、
docker run -d --name VOLUME-NAME -v myvol:/mount_target busybox
VOLUME-NAME:任意のコンテナ名
-v:ボリュームをマウントするオプション
"myvol"は対象のボリューム名(docker volume lsで調べる)
/mount_target にマウントされる
busybox は超軽量のLinuxイメージ
マウントしたコンテナからホストOS(ローカル)へファイルをコピーします。
docker cp コマンドを使いましょう。
docker cp CONTAINER-ID:/mount_target/source_file ./local_backup
コンテナ→ホストOS、コンテナ←ホストOSの双方向のコピーができるスグレモノです。
これで、volumesからのデータ救出ができました。
補足
docker-compose.ymlでvolumesを変更した後に、コンテナ側からデータ(Windows側からは見える)が見えないときはHyper-Vマネージャからvmを削除し、Dockerデーモンを再起動します。
それでもだめなら Windows ごと再起動しよう。
また、Docker for Windows でホスト側のパスを絶対パスで指定するのにちょっと癖があるので注意です。
参考>>Docker for WindowsをWSLから使う時のVolumeの扱い方