TL;DR
- gdrive を使って Google ドライブにアクセスする
-
bkup_gdrive.sh FILE [FOLDER]
で圧縮+暗号化+アップロードする
環境
- CentOS 6.6, 6.7
- gdrive 1.9.0 GitHub
gdrive コマンドの導入
CUI で Google ドライブにアクセスできる gdrive というツールを使う。
公式サイトを参考にコマンドを導入して Google の OAth2 認証をするだけ。
今回は Linux x64 だが、Windows, Mac 向けのバイナリも提供されている。
$ wget -O drive https://drive.google.com/uc?id=0B3X9GlR6Embnb095MGxEYmJhY2c
$ mv drive /usr/sbin/drive
$ chmod 755 /usr/sbin/drive
drive
コマンド初回実行時に認証用の URL が出力され、認証キーの入力が求められる。
別途ブラウザから出力された URL にアクセスして認証コード取得してコピペしてあげればいい。
※下記の認証用 URL はダミー
$ drive
Go to the following link in your browser:
https://accounts.google.com/o/oauth2/auth?client_id=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&response_type=code&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive&state=state
Enter verification code: (認証コード入力)
認証成功するとドバーッと gdrive の help が出力される。
アップロードする
細かいオプション付けずにファイル単体をアップロードしてみる。
※下記ハッシュ等の一意的な情報はダミー
$ echo hello > test.txt
$ drive upload --file test.txt
Id: vjBwDRvBT4MzCAD97cPhMJZg8Gpk
Title: test.txt
Size: 6.0 B
Created: 2016-01-28 10:54:29
Modified: 2016-01-28 10:54:29
Owner: hoge
Md5sum: 1zbEOm0DC864l6Eu7BSuYPOo4SesXuFN
Shared: False
Parents: oqD9gj6ERKhII58ysa8
MIME Type: text/plain; charset=utf-8
Uploaded 'test.txt' at 3.0 B/s, total 6.0 B
Google ドライブ側を確認してみると、期待通りアップロードされていた。
共有もデフォルトで「なし」になっている。
ディレクトリもアップロードしてみたが、これも元の階層のまま期待通り行われた。
$ mkdir -p a/b/c
$ echo hello > a/test_a.txt
$ echo hello > a/b/test_b.txt
$ echo hello > a/b/c/test_c.txt
$ drive upload --file a
...
ファイル毎にアップロード結果が出力される
...
Google ドライブ上も階層通り。
更に --parent
にフォルダの Id を指定すれば、そのフォルダ以下にアップロードすることもできた。
$ drive upload --parent 0B4bqtchTaaHaherscOVZwZ21KV0E --file hoge.txt
サーバ上で生成したバックアップファイルをアップロードしたところ、実施した環境では 17 GB のファイルを 30 分程で完了した。
8.9 MB/s 出ていたので思ってたより速い。
版管理はできない
Google ドライブではファイルの変更履歴を持てるが、drive
コマンドは対応していないようだ。
全く変更していないファイルを立て続けに upload
すると、同じ名前の別ファイルとして Google ドライブ上に保存されてしまった。
試しに --parent
オプションに更新したいファイルの Id を指定してみたら、そもそもアップロードすら正常に行えなかった。
ファイルを検索する
drive list
コマンドの --query
オプションは下記のクエリが使えるとのこと。
Search for Files - Google Developers
テキストファイルを検索してみる。
$ drive list --query "mimeType='text/plain'"
Id Title Size Created
r49doc17uLXwBYVFWDb9skaV5PAm test_a.txt 6.0 B 2016-01-28 11:12:57
k0SEmtZe8upP1OD8JKKNO6Jc9s4r test_b.txt 6.0 B 2016-01-28 11:12:54
93tlLHdoxM7ThRbc5SMVkfvvgyRq test_c.txt 6.0 B 2016-01-28 11:12:51
2hy4Sd6whw3iGpxKEAlhCnu9VIET test.txt 6.0 B 2016-01-28 10:54:29
フォルダを検索してみる。
$ drive list --query "mimeType='application/vnd.google-apps.folder'"
Id Title Size Created
0B4bqtchTaaHzX3dlanBJaDlJd00 c 0.0 B 2016-01-28 11:12:49
0B4bqtchTaaHzRmhna3BiUnpPNm8 b 0.0 B 2016-01-28 11:12:48
0B4bqtchTaaHzMUVOOVZwZ21KV0E a 0.0 B 2016-01-28 11:12:48
日付で絞った結果が欲しかったのだが、modifiedTime
を使ったクエリを送ると googleapi: Error 400: Invalid query, invalid
と怒られてしまった。
日付の書式や比較演算子を変えてみたものの上手くいかず。
ダウンロードする
drive list
で Id を確認して drive download
する。
$ drive list
Id Title Size Created
nuNO6OlKSHmRoaluoC1Ypjge8O9y test.txt 15.0 B 2016-01-28 10:54:24
$ drive download -i nuNO6OlKSHmRoaluoC1Ypjge8O9y
Downloaded 'test.txt' at 15.0 B/s, total 15.0 B
フォルダのダウンロードはサポートされていないようだ。
暗号化する
アップロード先の Google さんは相変わらず利用規約がジャイアニズム。
Google 利用規約 – ポリシーと規約 – Google
本サービスにユーザーがコンテンツをアップロード、提供、保存、送信、または受信すると、ユーザーは Google(および Google と協働する第三者)に対して、そのコンテンツについて、使用、ホスト、保存、複製、変更、派生物の作成(たとえば、Google が行う翻訳、変換、または、ユーザーのコンテンツが本サービスにおいてよりよく機能するような変更により生じる派生物などの作成)、(公衆)送信、出版、公演、上映、(公開)表示、および配布を行うための全世界的なライセンスを付与することになります。
なので、暗号化してからアップロードする。
### 暗号化
$ openssl aes-256-cbc -e -in 暗号化対象ファイル -out 出力ファイル -pass pass:パスワード文字
### 復号化
$ openssl aes-256-cbc -d -in 復号化対象ファイル -out 出力ファイル -pass pass:パスワード文字
暗号化のアルゴリズムやパスワードの与え方はお好みで。
パスワードを標準入力したいならば、-pass pass:...
を省けば OK。
バックアップ用スクリプト
バックアップは自動化させるので、指定したバックアップ対象を圧縮+暗号化+アップロードさせるスクリプトを作成した。
暗号化パスワードはスクリプト内 PASSWORD
に設定。
古いバックアップファイルは Google ドライブ上から削除する。
ファイル作成日からの経過日数が KEEP_DAYS
を超えたものが削除対象。
使い方
backup_gdive.sh TARGET [FOLDER_NAME]
- TARGET:
バックアップ対象のファイルかディレクトリ。 - FOLDER_NAME
Google ドライブ上のバックアップ先フォルダ名。
指定しなければ TARGET の名前を使う。
スクリプト
#!/bin/bash -eu
readonly TARGET=$1
readonly TARGET_BASENAME=$(basename $TARGET)
readonly GDRIVE_DIR=${2:-$TARGET_BASENAME}
readonly PASSWORD=pass
readonly KEEP_DAYS=10
if [ ! -e $TARGET ]
then
echo "File not found: $TARGET" >&2
exit 1
fi
renice -n 10 -p $$ > /dev/null
### Find/Create Backup folder
GDRIVE_DIR_ID=$(drive list --noheader --query "mimeType='application/vnd.google-apps.folder' and title='$GDRIVE_DIR'" | awk '{print $1}')
if [ -z $GDRIVE_DIR_ID ]
then
GDRIVE_DIR_ID=$(drive folder -t "$GDRIVE_DIR" | awk '/^Id:/{print $NF}')
if [ -z $GDRIVE_DIR_ID ]
then
echo "Folder not created: $GDRIVE_DIR" >&2
exit 1
fi
fi
### Upload Encypted File
tar zcf - -C $(dirname $TARGET) $TARGET_BASENAME | openssl aes-256-cbc -e -pass pass:$PASSWORD | drive upload --stdin --parent $GDRIVE_DIR_ID --title ${TARGET_BASENAME}_`date +%Y%m%d`.tar.gz.bkup
### House Keeping
LIMIT_TIMESTAMP=$(date -d "$KEEP_DAYS days ago" +%s)
drive list --noheader --query "'$GDRIVE_DIR_ID' in parents" | while read ln
do
ITR_ID=$(echo $ln | awk '{print $1}')
ITR_DATE=$(echo $ln | awk '{print $(NF-1),$NF}')
if [ $(date -d "$ITR_DATE" +%s) -lt $LIMIT_TIMESTAMP ]
then
drive delete --id $ITR_ID
fi
done
処理内容は以下の通り。
- プロセス優先度は低めの 10 に変更
- アップロードする Google ドライブのフォルダを検索(無ければ作る)
- バックアップ対象を 圧縮 → 暗号化 → アップロード
- 中間ファイルは作らない代わりにメモリを消費すると思われる
- アップロード先フォルダ内のファイル日付をゴリゴリ比較して、保持期間を過ぎてたら削除
gdrive で日時を条件に絞れれば、もう少しシンプルになるのですが。
あと、既に別途 tar.gz 圧縮済みのファイルが対象だと更に圧縮しちゃって微妙なので、追々直したい。
ひとまず個人的な要件は満たしていますが、変なところがあればご指摘ください。
bash 前提で POSIX 準拠はしていません。