Google Driveへのn世代バックアップを自動化する

# 概要
VPS上でサービス開発をしている中、DB等データのバックアップをどうするか？で悩みました。
なるべくお金をかけたくなかったので、`Google Drive`に自動でバックアップデータをアップロード/世代管理する方針にしました。
この記事では、セットアップ方法とスクリプトを記載します。

![backup-flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424303/d516aad1-ca24-dc1c-87ae-20f5ce9fa4c3.png)

# 1. 事前準備
※1 本記事では、以下`Ubuntu`環境でのセットアップ手順を記載しています。
OSによりインストールするバージョン等やコマンドが少し異なる場合がありますがご了承ください。
※2 本文中のコードに対するコメントを `#` で記載していましたが、Qiitaのナビに影響が出たので `//` で置換しております。

```bash
$ uname -a
Linux xxx-xxx-xxx-xxx 5.4.0-45-generic #49-Ubuntu SMP Wed Aug 26 13:38:52 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
```

## 1.1 gdriveをインストール
バックアップを実行するサーバに[gdrive](https://github.com/prasmussen/gdrive)をinstallします。
[gdrive](https://github.com/prasmussen/gdrive)を利用することで、CLI経由でGoogleドライブとやりとりが可能です。
`gdrive version`を実行し、バージョンが表示できればinstallは完了です。

※ OSに適したバージョンをinstallしてください。

```Bash: Ubuntu
// Download
$ curl -JLO https://github.com/prasmussen/gdrive/releases/download/2.1.1/gdrive_2.1.1_linux_386.tar.gz

// 解凍
$ tar xvfz gdrive_2.1.1_linux_386.tar.gz
gdrive

// 権限を付与して実行ディレクトリにコピー
$ sudo chmod +x gdrive
$ sudo cp gdrive /usr/local/bin/

// 動作確認
$ gdrive version
gdrive: 2.1.1
Golang: go1.11.13
OS/Arch: linux/386
```

## 1.2 アクセストークンの取得
Googleドライブと通信するためには、アクセストークンが必要です。

`gdrive list`等を実行すると、アクセストークンが無い場合はブラウザ経由で認証画面が表示されます。
希望のGoogleアカウントを選択し、表示される認証コードをコピペすることでアクセストークンが取得できます。

ファイル一覧が表示できれば、アクセストークンの準備も完了です。

```bash: Ubuntu
// ファイル一覧を取得(アクセストークンが無い場合、認証を求められる)
$ gdrive list
Authentication needed
Go to the following url in your browser:
(Googleアカウントとのリンク用URL)

Enter verification code: xxx

// アクセストークンが有効な場合、ファイル一覧を取得できる
$ gdrive list
Id                                  Name          Type   Size      Created
1_Cut...(省略)...                   hoge.tar.gz   bin    72.7 MB   2022-04-04 00:00:35
1oSUk...(省略)...                   fuga.tar.gz   bin    72.7 MB   2022-04-03 00:00:36
1Mrs-...(省略)...                   piyp.tar.gz   bin    72.7 MB   2022-04-02 00:00:36\
```

# 2. バックアップするスクリプト準備
`バックアップ対象を圧縮` → `Gdriverにアップロード` → `Gdriveにある古いファイルを削除`という感じの流れです。
ファイル名を`shell起動日時`として世代管理しています。
古いファイルを削除する処理は、`現在時刻から60日以上前に作られたファイル`を削除しているため、Googleドライブの容量制限に引っかかることも無さそうです。

```bash: backup.sh
// 現在日時
NOW_DATE=`date +%Y%m%d`
// 世代管理日数(60日) yyyy-mm-dd
DELETE_DAY=`date -d '60 day ago' +%Y-%m-%d`
// 作業ディレクトリ
WORK_DIR="/tmp"

// バックアップファイル名
BACKUP_FILE="backup-${NOW_DATE}.tar.gz"
BACKUP_TARGET_FILE="/home/user/hoge.db"

// Google Drive 保存先ディレクトリID
GDRIVE_DIR_ID=xxx

// バックアップ関数
backup ()
{
    // /tmpディレクトリに移動
    cd $WORK_DIR

    // バックアップ作業ディレクトリ作成
    mkdir backup-tmp
    // DBを作業ディレクトリにdump & 圧縮
    tar cvfz ./backup-tmp/${BACKUP_FILE} ${BACKUP_TARGET_FILE}

    // Google Driveへバックアップ
    /usr/local/bin/gdrive upload -p ${GDRIVE_DIR_ID} ./backup-tmp/${BACKUP_FILE}

    // バックアップ作業ディレクトリ削除
    rm -rf backup-tmp
}

// バックアップ削除(世代管理)関数
remove ()
{
    echo "${DELETE_DAY}より古いバックアップを削除します."
        
    // 指定期間を超過したファイルID一覧を取得(ディレクトリは対象外)
    gdrive_file_ids=$(/usr/local/bin/gdrive list --no-header --query "'${GDRIVE_DIR_ID}' in parents and trashed = false and mimeType != 'application/vnd.google-apps.folder' and createdTime < '${DELETE_DAY}T00:00:00'" | awk '{ print $1 }')

    // ID毎にループ
    for file_id in $gdrive_file_ids; do
        echo $file_id

        // 削除
        /usr/local/bin/gdrive delete ${file_id}
    done
}

// 変数空チェック
if [ -z "${NOW_DATE}" ] || [ -z "${DELETE_DAY}" ] || [ -z "${BACKUP_FILE}" ] || [ -z "${GDRIVE_DIR_ID}" ] ; then
  exit 1
fi

// バックアップ & n世代管理
backup && remove
```

# 3. cron設定
毎日0時に先ほど作成したバックアップのshellを動かすcronを設定すれば完了です。
※ dockerで運用しているmysqlのDBをダンプしてバックアップ的な用途で利用しているため `root` ユーザで実行していますが、`root`ユーザである必要はありません。

```bash: Ubuntu
$ cat /etc/cron.d/cron_backup
// DBバックアップ
// 毎日0時にGoogleドライブに自動バックアップ
0 0 * * * root sh /hoge/shell/backup.sh >> /var/log/cron-backup-db.log 2>&1
```

最終的に、下記のようなDBのバックアップが自動で作られる感じになります～。
![完成image.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424303/741b3349-21fd-5b0b-00b8-0e665e99f16d.png)


# まとめ
Googleドライブに自動でバックアップする手順をまとめました。
`gdrive`のアップロード/ダウンロードの使い方の記事はよく見つかったのですが、バックアップの世代管理みたいなものは見つけられなかったので作ってみたという感じです。

もしよろしければ参考にしてみてください！