Dropboxのバックアップと簡易世代管理


Dropboxには、アプリケーションの設定ファイルなどのデータサイズは大きくないけど各PCに同期したいファイルを主に保存しています。このファイル群には非常に大事なものが含まれており、万が一にも誤操作などで消えてしまうと復旧に多大な時間を費やしてしまう事が懸念されることから、Dropboxのファイルを簡易世代管理付きでバックアップすることにしました。

Dropbox APIを使ってLambdaで~とも考えましたが、やりたいことに対して無駄に時間を消費してしまいそうだったので、シンプルにshで実装しました。

## 条件と対応

### 費用

* なるべく安く
* 1バックアップ1GB程度
* 週次で全バックアップ

S3のオレゴンリージョンにストレージクラスSTANDARD_IAで保存。
費用試算: 180日 / 1週間 * 1GB * `$0.0125` = `$0.32`/月
[料金 - Amazon S3 | AWS](https://aws.amazon.com/jp/s3/pricing/)

### バックアップ期間

* バックアップ後180日で削除

S3のLifecycleを設定。

### 通知

* バックアップした事をメール通知

S3のEventsでバケットへのバックアップ追加をSNS経由でメール通知。

### 複数端末同期

* 複数のMac PCから実行
    * どのPCを起動していてもバックアップ処理が実行されるように
    * なるべく無駄にバックアップしないように

日次で、各PC間で実行日時を少しずらしながらバックアップスクリプトを実行して、実行時にDropbox上のファイルに最終実行日時を追記しておく。スクリプトは最終実行日時から一週間以上経過していればバックアップするように実装。

## イメージ

![Screen Shot 2016-04-30 at 12.04.07 PM.png](https://qiita-image-store.s3.amazonaws.com/0/5774/4b1c916e-49e7-7997-4754-a111c8cc1b02.png "Screen Shot 2016-04-30 at 12.04.07 PM.png")


## 実行環境

* OS X EL Capitan Version 10.11.3

## 事前準備

依存ライブラリをインストール。

```
$ pip install awscli
$ brew install coreutils
```

## AWSの設定

オレゴンにS3バケットを作成。

```
$ aws s3api create-bucket --bucket my-dropbox-backup-xxx --region us-west-2 --create-bucket-configuration LocationConstraint=us-west-2
```

保存後180日後に削除されるように、バケット全体にLifecycleを設定する。

* [Amazon S3でオブジェクトの有効期限を設定できるようになりました ｜ Developers.IO](http://dev.classmethod.jp/cloud/amazon-s3-object-expiration/)

バケットにバックアップが追加されたらSNSでメール通知するように設定する。

* [AWS新機能 Amazon S3 Event Notificationsを使ってみた #reinvent ｜ Developers.IO](http://dev.classmethod.jp/cloud/amazon-s3-event-notifications/)

## スクリプトの配置

各PCで実行できるようにDropboxに配置する。

*$HOME/Dropbox/bin/dropbox-backup:*

```sh
#!/bin/sh

# --設定
S3_PATH=s3://my-dropbox-backup-xxx
ZIP_PASSWORD=xxx
BACKUP_SECOND=604800 # １週間に１度
DROPBOX_PATH=$HOME/Dropbox
APP_DATA_PATH=$DROPBOX_PATH/.$(basename $0)
DATA_FILE=$APP_DATA_PATH/data
# 設定--

export PATH="/usr/local/bin:/usr/local/opt/coreutils/libexec/gnubin:$PATH"
export TZ='Asia/Tokyo'
DATE_FORMAT='%Y-%m-%dT%T+09:00'

mkdir -p $APP_DATA_PATH

main() {
  # 初回実行時はダミーで最終実行日時を保存
  if [[ ! -f $DATA_FILE ]]; then
    echo "1970-01-01T09:00:00+09:00" > $DATA_FILE
  fi

  # 現在日時と最終実行日時
  local now=$(date +"$DATE_FORMAT")
  local before=$(cat $DATA_FILE)

  # 差分秒数を算出
  local now_unix=$(date +%s -d "$now")
  local before_unix=$(date +%s -d "$before")
  local n=$(expr $now_unix - $before_unix)

  # 設定した秒数以上経過していたらS3にバックアップ
  if [[ $n -ge $BACKUP_SECOND ]]; then
    cd $DROPBOX_PATH
    zip -r --password $ZIP_PASSWORD - . 2>/dev/null \
      | aws s3 cp --storage-class STANDARD_IA - $S3_PATH/$(date +"%y%m%d_%H%M%S").zip
    [[ $? -eq 0 ]] && echo $now > $DATA_FILE
  else
    echo "前回実行日時: $before, 次回実行までの秒数: $(expr $BACKUP_SECOND - $n)"
  fi
}

main
```

実行権限を付与。

```
$ chmod +x $HOME/Dropbox/bin/dropbox-backup
```

## 定期実行の設定

launchdで日次実行を設定。LaunchControlから設定すれば簡単。

* [launchd管理ツールのLaunchControlが非常に便利 - Qiita](http://qiita.com/nori3tsu/items/39cf1ca8ca779f9d074f)

*$HOME/Library/LaunchAgents/local.dropbox-backup.plist:*

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>local.dropbox-backup</string>
        <key>Program</key>
        <string>/Users/Nori/Dropbox/bin/dropbox-backup</string>
        <key>StandardErrorPath</key>
        <string>/tmp/local.dropbox-backup.stderr</string>
        <key>StandardOutPath</key>
        <string>/tmp/local.dropbox-backup.stdout</string>
        <key>StartCalendarInterval</key>
        <dict>
                <key>Hour</key>
                <integer>12</integer>
                <key>Minute</key>
                <integer>0</integer>
        </dict>
</dict>
</plist>
```