Dockerコンテナ内にあるファイルのバックアップ方法


記事の概要

Dockerコンテナ内にあるデータのライフサイクル(寿命)は、コンテナのライフサイクルと共にあります。
通常、Dockerボリュームなどを使い、データのライフサイクルコンテナのライフサイクルと分離します。

ただし、Dockerボリューム内に蓄積されたデータも何らかの障害で失われる可能性過去の状態に戻したい場合があります。
Dockerボリュームのライフサイクルとは別に、バックアップを取得して第3のライフサイクルとして管理しておくと安心です。

この記事では、OSのディレクトリ上へDockerボリューム内にあるファイルをバックアップする方法について説明します。
またDropbox APIを使って、取得したバックアップファイルをDropboxへ保管する方法についても解説します。

前提条件

本手順の検証は以下の環境で行いました。

OS:CentOS7
Docker:ver19.03.13

また、Dropbox APIを使用するため、Dropboxアカウントが必要になります。

Dockerボリューム内にあるファイルをバックアップする

以下のdocker runコマンドを実行することで、Dockerボリューム内にあるファイルをOS上のディレクトリにバックアップできます。

docker run \
--rm \
--volumes-from [バックアップ対象のコンテナ名] \
-v [バックアップ先となるOSのファイルパス]:/backup \
busybox cp [コンテナ内のバックアップ対象のファイルパス] /backup/[バックアップファイル名]

--rm
 このオプションはコンテナの終了時に、コンテナが使用しているファイルシステムを削除します。
 バックアップ用のコンテナなので、用が済んだら後片付けを行います。

--volumes-from [バックアップ対象のコンテナ名]
 このオプションではバックアップ対象のコンテナにマウントされているDockerボリュームを、バックアップ用のコンテナ(ここではbusybox)にマウントします。
 例えば、appコンテナの/var/www/app/dataにDockerボリュームをマウントしている場合、busyboxコンテナの/var/www/app/dataにDockerボリュームがマウントされます。

-v [バックアップ先となるOSのファイルパス]:/backup
 このオプションではバックアップ先となるOSのファイルパスを、バックアップ用のコンテナの/backup(ここではbusybox)にマウントします。
 例えば、OSのファイルパスとして/home/hogehoge/app/dataを指定すると、/home/hogehoge/app/databusyboxコンテナの/backupにマウントされます。

busybox cp [コンテナ内のバックアップ対象のファイルパス] /backup/[バックアップファイル名]
 このコマンドは、バックアップしたいファイルを、OS上のディレクトリにコピーするために実行します。
 例えば、コンテナ内のファイルパスとして/var/www/app/data/db.sqlite3を指定し、バックアップファイル名としてdb.sqlite3を指定すれば、/backup/db.sqlite3(/backupにはOS上のディレクトリがマウントされている)へ/var/www/app/data/db.sqlite3がコピーされます。

DropboxへAPIを使ってファイルをアップロードする

Dropboxは無料でも使用可能なクラウドストレージです。
また、同一のファイル名は30日間だけ遡って、復元することもできます(つまり30日バージョン管理として使えます)。

バージョン履歴はストレージ容量にカウントされないため、容量節約にもなります。

バージョン履歴はストレージ容量にカウントされますか?
いいえ、バージョン履歴はストレージ容量には含まれません。

バージョン履歴の概要

ここでは上記の特徴から、ファイル名を同一のものにして、バックアップのたびに上書きするようにします。

Dropbox App consoleへログイン

以下のURLへアクセスします。
https://www.dropbox.com/login?cont=https%3A%2F%2Fwww.dropbox.com%2Fdevelopers%2Fapps%3F_tk%3Dpilot_lp%26_ad%3Dtopbar4%26_camp%3Dmyapps

appを作成

画面右上にあるCreate appを選択します。

  • 1. Choose an APIScoped access
  • 2. Choose the type of access you needApp folder(このappのためのフォルダを作り、そこだけにアクセスさせます。)
  • 3. Name your app:好きなappの名前を入力します。

Create appボタンを選択して作成します。

appのパーミッションを設定

作成されたappを選択後、Permissionsタブを開きます。

アップロードに必要なfiles.content.writeにチェックを入れます。
ここでは、files.content.readにもチェックを入れていますが、不要であれば、入れなくて大丈夫です。

画面下部にあるSubmitボタンを押します。

アクセストークンの発行

Settingsタブに戻り、アクセストークンを作成します。

Access token expirationShort-livedにすると短い間だけ有効になります。
No expirationは無期限です。

どちらかを選択した後で、Generated access tokenGenerateボタンを選択します。
以下のような文字列が生成されるため、コピーして保管します(ページ遷移すると消えるため)。

sl.A9KVBrdIBMTHy1tyM6vUi3KMKHrh_LFJhX-ygxkgzprNyF6q19hdPksEjgzQ_7YSMcBzha7xruUug1RjvbAlzuinAyAfeguvOqj1j5HlLPR0MAzntJTgsS_JZnhogehogehoge

Dropbox API Explorerでアップロード検証

以下のURLに接続してDropbox API Explorerを開きます。
https://dropbox.github.io/dropbox-api-v2-explorer/#files_upload

File to uploadにテスト用のファイルを選択します。
path/test.txtのように/ファイル名(アップロード先のファイル名)を結合したものです。
mode(optional)overwriteを選択します。

Submit Callボタンを押して正常にアップロードされることを確認します。
実際にDropboxの画面を開くと、アプリフォルダが作成されていて、前の手順で作成したapp名のフォルダが作られています。

アプリ
 = app名(pathで指定するときのルートフォルダ、つまり/)
  = hogehoge.txt

curlで実行できるようにする。

Dropbox API Explorer内でShow Codeボタンを押すと、実際に実行するコードを参照できます。

以下は例です。

curl -X POST https://content.dropboxapi.com/2/files/upload \
 --header 'Authorization: Bearer ZTZ-m7zw48MAAAAAAAAAAZ5Ps8hDrpTPI37W3Ocfgc1LmB3Hu0hogehogehoge' \
 --header 'Content-Type: application/octet-stream' \
 --header 'Dropbox-API-Arg: {"path": "/test.txt", "mode":{".tag":"overwrite"}}' \
 --data-binary @'hogehoge.txt'

自分の環境でShow Codeを実行した後で、下2行を変更します。

 --header 'Dropbox-API-Arg: {"path": "Dropboxのファイルパス(ファイル名を入れる)", "mode":{".tag":"overwrite"}}' \
 --data-binary @'Dropboxへアップロードするクライアントのファイルパス'

例えば、Dropboxのアプリ - test(testはapp名)配下にOS上の/home/hoge/hogehoge.txtdropbox_hogehoge.txtという名前でアップロードするには、

 --header 'Dropbox-API-Arg: {"path": "/dropbox_hogehoge.txt", "mode":{".tag":"overwrite"}}' \
 --data-binary @'/home/hoge/hogehoge.txt'

とします。

実行できる形になったら、OS上から実行して正常にアップロードされるかを確認します。

cronを使って定期実行させる

このままでも手動で実行は可能ですが、cronを使って毎日実行する例を記載します。

非rootユーザー

dockerコマンドを実行できるユーザー(dockerグループに所属)で実行しています(rootではない)。

crontab -eで編集画面へ移動し、以下を書き込みます。

SHELL=/bin/bash

00 18 * * * docker run --rm --volumes-from container -v /home/hogehoge/backup/data:/backup busybox cp /backup_file_in_container/fugafuga /backup/backup_`date +\%Y\%m\%d`
00 19 * * * upload_to_dropbox.sh
00 20 * * * rm /home/hogehoge/backup/data/backup_`date --date '7 days ago' +\%Y\%m\%d`

18時に実行しているのは、コンテナ内にあるファイルをバックアップするコマンドです。
ここでは日付をファイル名に付けています。

19時はDropboxへファイルをアップロードするスクリプトを実行します。
先程のcurlコマンドをスクリプトファイルに入れただけです。

upload_to_dropbox.sh
file_name="/home/hogehoge/backup/data/backup_`date +%Y%m%d`"

curl -X POST https://content.dropboxapi.com/2/files/upload \
 --header 'Authorization: Bearer ZTZ-m7zw48MAAAAAAAAAAZ5Ps8hDrpTPI37W3Ocfgc1LmB3Hu0hogehogehoge' \
 --header 'Content-Type: application/octet-stream' \
 --header 'Dropbox-API-Arg: {"path": "/test.txt", "mode":{".tag":"overwrite"}}' \
 --data-binary @${file_name}

20時に、OS上のバックアップファイルを削除します。
念の為、OS上にも7日分のバックアップファイルを保持しておきたいので、7日前のものを削除します。

rootユーザー

人にもよりますが、私が運用している環境はコンテナからバックアップしたファイルの所有者がrootになっています。
このままだと、root以外のユーザーでファイルを消せないので、所有者を変更します。

rootユーザーでcrontab -eを実行し、以下を書き込みます。

30 18 * * * chown hogehoge:hogehoge -R /home/hogehoge/backup/data/

最後にDropboxへのアップロードが正常に行われたかどうかの通知をSlackで受け取ります。

後日執筆予定。

参考

Backup a container
Dockerコンテナのバックアップ方法について記載あり

Dropbox API v2
APIドキュメント

APIを利用する場合は上記ドキュメントと合わせてExploreも使用すると楽になる。
Dropbox API Explorer