開発とプログラミング

Dockerのvolumesからデータを救出する~コンテナが起動できなくなったときの話

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の扱い方

 

-開発とプログラミング