LoginSignup
25
25

Docker の Volume バックアップ(Docker v19.03, macOS/Windows10)

Last updated at Posted at 2019-12-09

docker volume create で作成したボリュームをローカルにバックアップしたい。

しかし、Mac や Win10 には docker volume inspect で表示される Mountpoint に該当ボリュームがないのです。

Docker Engine のバージョンによっても挙動が違うらしく、簡単にバックアップ&復元できないかmac docker volume バックアップ」で Qiita 記事をググっても記事の内容がバラバラで困ったので、自分のググラビリティとして。

TL; DR (今北産業)

実行中のコンテナの、特定のファイルをローカルにコピーしたいだけなら、docker cp コマンドを使うのが楽です。

docker cp <コンテナ名 or コンテナID>:<コンテナ内のパス> <出力先のパス>
$ docker cp \
    lancache-dns-1:/opt/cache-domains/cache_domains.json \
    ./cache_domains.json

Successfully copied 4.61kB to /root/lancache/cache_domains.json

逆に、複数ディレクトリやボリュームをバックアップしたいだけなら、Alpine Linux などの軽量コンテナに、バックアップ元とバックアップ先の2つのボリュームを一旦マウントし tar などでアーカイブするのがらくです。

docker run でバックアップする

以下は、docker volume create data-hoge で作成し、利用されていたボリュームを、ローカルの ./backup にバックアップしたい場合のコマンドです。

Alpine Linux の軽量イメージからコンテナを作成し、各々をマウントしてアーカイブしています。

  1. バックアップしたいボリューム(-v data-hoge:/data
  2. バックアップ先のディレクトリ(-v "$(pwd)/backup":/backup
docker run --rm \
    -v data-hoge:/data \
    -v "$(pwd)/backup":/backup \
    alpine \
    tar cvf /backup/backup.tar /data

復元は、上記の逆の手順でアーカイブとボリュームをマウントして、ボリューム内に解凍します。

docker compose run でバックアップする(バックアップ・リストア用 docker-compose.yaml

ボリュームをバックアップする専用のディレクトリを作成し、バックアップ & リストア用の YAML ファイルを作成しておくと便利です。

特に、docker system prune などで、イメージ・コンテナ・ボリュームなどを定期的に prune prune する場合に重宝します。

バックアップ
# バックアップの実行(カレントの ./backup/backup.tar.gz に sample-data ボリュームがアーカイブされる)
docker compose run --rm backup
リストア
# リストア先のボリュームを作成
docker volume create sample-data
# リストアの実行(カレントにある ./backup/backup.tar.gz をリストア先に展開する)
docker compose run --rm restore
docker-compose.yml
# 下記 sample-data を適宜バックアップしたいボリューム名に変更する
version: "3.9"

volumes:
  sample-data:
    external: true

services:
  backup:
    image: alpine
    volumes:
      - sample-data:/fromA
      - ./backup:/toB
    working_dir: /fromA
    entrypoint: [ "tar",  "czvf", "/toB/backup.tar.gz", "."]

  restore:
    image: alpine
    volumes:
      - ./backup/backup.tar.gz:/fromA/backup.tar.gz
      - sample-data:/toB
    working_dir: /fromA
    entrypoint: [ "tar",  "xzvf", "/fromA/backup.tar.gz", "-C", "/toB"]

TS; DR (データの永続化を完全に理解した気になるコマケーこと)

「データ消えちゃうんだよね」「永続化っ 👋 バシッ」

基本的に Docker のコンテナが削除されると、コンテナ内のデータは消えます。当然ですが。

そこで、コンテナ内で作成・更新されたデータを残すことを「データの永続化」と言います。

Docker コンテナのデータ永続化には、いくつか種類があります。

  1. Dockerfile と同じ階層のディレクトリをマウントして、永続化したいデータをそこに置く
  2. Docker ボリュームを作成&マウントして、永続化したいデータをそこに置く

上記1のローカル・ディレクトリをマウントするのが簡単なので、一般的です。

しかし「複数の異なるコンテナで同じデータを共有したい」場合に問題が発生します。例えば、複数コンテナで同じディレクトリをマウントしたい場合などです。

この上記1の問題は、コンテナごとに Dockerfile を分けていた場合です。各々のディレクトリが異なるため、共有したいディレクトリの階層が異なることになり、マウントできません。

dataディレクトリをマウントしたい
$ tree
.
├── docker-compose.yml <-- ./data と ./sample1/data はコンテナと
│                          同じ階層以下にあるのでマウントできる。
├── data <---------------- sample1, sample2 のコンテナからは階層
│   │                      が異なるため相対パスではマウントできない。
│   ├── common1.json
│   ├── data2.json
│   └── data3.json
├── sample1
│   ├── data <------------ sample1 のコンテナはマウントできる。
│   │   │                  sample2 のコンテナからは相対パスではマウ
│   │   │                  ントできない。
│   │   ├── common1.json
│   │   ├── data2.json
│   │   └── data3.json
│   └── Dockerfile
└── sample2 <------------- (´・ω・`)ショボーン
    └── Dockerfile

マウントできないと言いましたが、絶対パスで指定すればマウントはできます。しかし、パスは環境によって変わるものなので相対パスで指定したいところですが、できないのが問題です。

実は、絶対パスでも Docker Desktop の設定で親ディレクトリのパスを通していないとマウントできません。[Preferences] -> [Resources] -> [File sharing] に登録されているディレクトリ以下のパスのみが Docker から参照・マウントできます。「なんで /Users ディレクトリがあるの」と気持ち悪いからと親ディレクトリの登録を削除しちゃうなど、意外に見落としがちな設定箇所です。

これは Windows や macOS の Docker(Docker Desktop)の場合のセキュリティ上の制限です。具体的には Dockerfile のある階層以下のディレクトリしか相対パスでマウントできないように制限されているからです。

つまり、./data は使えても ../data は使えないと言うことです。

そのような場合、3 つの方法があります。

  1. あきらめて絶対パスでマウントする。
  2. 上階層に docker-compose を置いてマウントする。
  3. Docker ボリュームを作成&マウントする。

この、最後の 3 目の方法が本記事の本題です。

$ docker volume create コマンドで仮想ボリュームを作成し、それを各々のコンテナにマウントする方法です。

特に、この方法だとホスト側のディスク・フォーマットを気にしなくてもいいというメリットがあります。

問題は、良くも悪くも「仮想ボリューム」であることです。

なぜなら、この仮想ボリューム内のデータをローカルにバックアップするのが Windows や macOS の場合、いささか面倒だからです。詳しくは下記「Mac や Windows の Docker ボリュームは特殊」参照。

Docker ボリュームのバックアップ

まずは、具体的な手順から。

いささか冗長的ですが、「完全に理解した」気分になれるように丁寧に説明したいと思います。

作成済み(バックアップしたい)Dockerボリュームの確認
$ docker volume ls
DRIVER              VOLUME NAME
local               data-hoge

dockerls コマンド(docker volume ls)を使うと、作成済みの仮想ボリュームが一覧で確認できます。上記の場合、data-hoge がすでに作成済みとなります。

ボリュームがない場合の新規作成
$ docker volume ls
DRIVER              VOLUME NAME

$ docker volume create data-hoge
data-hoge

$ docker volume ls
DRIVER              VOLUME NAME
local               data-hoge

任意の仮想ボリュームを作成したい場合は create コマンド(docker volume create)を使います。上記は data-hoge を新たに作成しています。

それでは、とあるコンテナに data-hoge がマウントされて、中にデータが保存されていると仮定します。この中身をローカルの ~/my_backup/backup にバックアップしたいと思います。

バックアップ
$ # バックアップの作業ディレクトリの作成&移動
$ cd mkdir ~/my_backup && cd "$_"

$ # バックアップ開始(/data を /backup にアーカイブする)
$ docker run --rm \
    -v data-hoge:/data \
    -v $(pwd)/backup:/backup \
    alpine \
    tar cvf /backup/backup.tar /data
tar: removing leading '/' from member names
data/
data/hello.txt
...

上記は、作業ディレクトリ ~/my_backup に移動後、まっさらな Alpine linux のコンテナを立ち上げて 2 つのボリュームをマウントしています。

具体的には data-hoge の仮想ボリュームをコンテナの /data に、ローカルの ./backup ディレクトリをボリュームとしてコンテナの /backup にマウントしています。

あとは tar コマンドでコンテナの /backup/backup.tar/data ディレクトリの中身をアーカイブ(固めて 1 つに)しています。

これにより、ローカルの ~/my_backup/backup/ ディレクトリに backup.tar が作成されます。

バックアップからリカバリしたい場合は、アーカイブする変わりに -xvf でアーカイブを展開します。(圧縮→解凍みたいなものです)

アーカイブの解凍と確認
$ # 作業ディレクトリに移動
$ cd ~/my_backup

$ # 作成されたバックアップの確認
$ ls
backup

$ # ディレクトリ構造の確認(要treeコマンド。brew install tree でインストール可能)
$ tree
.
└── backup
    └── backup.tar

1 directory, 1 file

$ # アーカイブの解凍
$ tar -xvf ./backup/backup.tar
x data/
x data/hello.txt
...

$ # 解凍ファイルの確認
$ ls
backup	data

$ # ディレクトリ構造の確認
$ tree
.
├── backup
│   └── backup.tar
└── data
    ├── ...
    └── hello.txt

Mac や Windows の Docker ボリュームは特殊

docker volume create --name data-hoge とボリュームを作成した場合、inspect コマンドでボリューム情報を表示すると以下のようになります。

ボリューム情報の表示
$ docker volume inspect data-hoge
[
    {
        "CreatedAt": "2019-12-09T04:28:16Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/data-hoge/_data",
        "Name": "data-hoge",
        "Options": {},
        "Scope": "local"
    }
]

しかし、上記の Mountpoint のパスにデータがあるかと思いきや、ありません。

macOSでMountpointをlsしてみる
$ ls /var/lib/docker/volumes/data-hoge/_data
ls: /var/lib/docker/volumes/data-hoge/_data: No such file or directory

$ # そもそも /var/lib/docker がない。docker いっちゃった?
$ ls /var/lib/
postfix
Win10+WindowsTerminal(Preview)でMountpointをlsしてみる
> docker volume ls

DRIVER              VOLUME NAME

local               data-hoge

> docker volume inspect sample

[

    {

        "CreatedAt": "2020-03-19T14:09:56Z",

        "Driver": "local",

        "Labels": {},

        "Mountpoint": "/var/lib/docker/volumes/data-hoge/_data",

        "Name": "sample",

        "Options": {},

        "Scope": "local"

    }

]


PS > ls /var/lib/docker/volumes/data-hoge/_data
ls : パス 'C:\var\lib\docker\volumes\data-hoge\_data
' が存在しないため検出できません。
発生場所  行:1 文字:1
+ ls /var/lib/docker/volumes/data-hoge/_data

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : ObjectNotFound: (C:\var\lib\docker\volumes\data-hoge\_data:String) [Get-ChildItem], ItemNot  
   FoundException

    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand



PS > # やっぱり /var/lib/ は存在しない。docker いっちゃってる。
PS > ls /var/lib/
ls : パス 'C:\var\lib\
' が存在しないため検出できません。
発生場所  行:1 文字:1
+ ls /var/lib/

+ ~~~~~~~~~~~~

    + CategoryInfo          : ObjectNotFound: (C:\var\lib\:String) [Get-ChildItem], ItemNotFoundException


    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemComman

これは Mac や Windows10 の Docker Desktop の場合、Docker は VM(仮想マシン) 上で動いているためです。

つまり docker volumes create で作成されたボリュームも仮想マシンのイメージの中に作成されるということなので、ローカルには直接作成されません。

以下は macOS の場合ですが、作成された仮想マシンのイメージは以下のディレクトリに設置されます。(Docker v19.03.5 現在)

Dockerの仮想マシン・イメージの保存先
~/Library/Containers/com.docker.docker/Data/vms/0/

問題は、このディレクトリから該当するイメージと、そこからデータを引き出す方法がわかりづらいこと。

どのファイルを選べばいいのかわからない(treeコマンドはbrewで別途インストール済み)
$ tree ~/Library/Containers/com.docker.docker/Data/vms/0/
/Users/admin/Library/Containers/com.docker.docker/Data/vms/0/
├── 00000002.000005f4
├── 00000002.00001000
├── 00000002.00001001
├── 00000002.00001002
├── 00000002.0000f3a4
├── 00000002.0000f3a5
├── 00000003.000005f5
├── 00000003.00000948
├── Docker.raw
├── config.iso
├── connect
├── data
├── guest.000005f5 -> 00000003.000005f5
├── guest.00000948 -> 00000003.00000948
├── hyperkit.json
├── hyperkit.pid
├── lifecycle-server.sock
├── log
├── nic1.uuid
└── tty -> /dev/ttys000

2 directories, 18 files

screentty で仮想マシンに接続する方法もあるようなのですが、確認ができるというだけでローカルにデータを保存するには煩雑な作業が必要そうです。

$ # 仮想マシンの tty に接続
$ screen ~/Library/Containers/com.docker.docker/Data/vms/0/tty
cd
docker-desktop:~# # 仮想マシン上で Mountpoint を確認
docker-desktop:~# ls /var/lib/docker/volumes/data-qithub/_data
hello.txt ...

docker-desktop:~# # Ctrl+a -> Crtl+k で exit
Really kill this window [y/n] y
[screen is terminating]

これらのファイルを下手に触って、うっかりイメージを壊したりしそうで危険な臭いがします。また、docker volume archive <ボリューム名> のようなコマンドもないようです。(´・ω・`)

$ docker volume --help

Usage:	docker volume COMMAND

Manage volumes

Commands:
  create      Create a volume
  inspect     Display detailed information on one or more volumes
  ls          List volumes
  prune       Remove all unused local volumes
  rm          Remove one or more volumes

Run 'docker volume COMMAND --help' for more information on a command.

そこで、確実で安全なバックアップ方法がないか探したところ、公式に書いてありました。ダミーのコンテナに、ボリュームとローカルのディレクトリをマウントして tar アーカイブする方法です。(訳は、筆者訳)

For example, create a new container named dbstore:
【訳】 例えば、まず "dbstrore" というコンテナを新規に作成します。

$ docker run -v /dbdata --name dbstore ubuntu /bin/bash

Then in the next command, we:

  • Launch a new container and mount the volume from the dbstore container
  • Mount a local host directory as /backup
  • Pass a command that tars the contents of the dbdata volume to a backup.tar file inside our /backup directory.

【訳】 次のコマンドで、以下を行いましょう。

  • 別の新しいコンテナを起動し、先の dbstore コンテナからボリュームをマウントする。
  • ローカルのディレクトリを /backup としてマウントする。
  • tar コマンドで、dbdata ボリュームの内容をアーカイブして、/backup ディレクトリ内の backup.tar ファイルに格納する。
既存のコンテナのボリュームを新規コンテナにマウントしてアーカイブする方法
$ docker run --rm \
    --volumes-from dbstore \
    -v "$(pwd)":/backup \
    ubuntu tar cvf /backup/backup.tar /dbdata

Backup, restore, or migrate data volumes @ docs.docker.com より)

確かにシンプルで力強い方法です。なにより直感的(覚えやすい)ですな。

ただ、ubuntu コンテナは 78MB 程度とは言え、筆者は alpine の軽量コンテナ(6 MB)が個人的に好みです。

バックアップとリストア

公式にある上記の方法だと ubuntu コンテナであるだけでなく処理後に不要になったダミー・コンテナが残ってしまいます。ボリュームが生きている(docker volume ls で表示される)のであれば、以下のようにバックアップするのが簡単だと思います。

バックアップ方法
$ # バックアップしたいボリューム名がdbstoreの場合
$ docker run --rm \
    -v dbstore:/dbstore \
    -v "$(pwd)/backup":/backup \
    alpine \
    tar cvf /backup/backup.tar /dbstore

ちなみに、リカバリーは逆の手順で、ボリューム内に tar ファイルを解凍します。

リカバリー方法
$ # リカバリー用の空のボリューム作成 (dbstore2)
$ docker volume create --name dbstore2

$ # リカバリー(Ubuntu イメージにリカバリする場合)
$ docker run --rm \
    -v dbstore2:/dbdata \
    -v "$(pwd)/backup.tar":/backup/backup.tar \
    ubuntu \
    bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1"

なお、上記の $(pwd) の箇所ですが、macOS や Windows はスペースの入ったパスが多いため、"$(pwd)" とダブルクォートで囲うクセを付けておくと良いと思います。

参考文献

25
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
25