DBバックアップをGASのAPI経由でGoogle Driveに保管する

掲載日
更新日

はじめに

アプリケーションをリリースしたら、DB等のバックアップを取ることはほぼ必須だと思います。
サーバー内に配置するバックアップはもちろんですが、ハード障害などでサーバー丸ごと吹っ飛んだ場合のためにサーバー外にバックアップを取得しておくことも肝要です。

企業の場合お金と責任があるのでちゃんとお金を払ってバックアップ用サーバーや検証環境等、置く場所を持ってると思いますのでscpやrsyncで外に逃がして保管すると思いますが、このサイトは個人運営なので無料で使えるGoogleDriveに保存していきたいと思います。

今回は月次、日次バックアップの取得を行うshellscriptの作成と、バックアップをGoogle Driveにも同期する処理を作成していきます。

バックアップを取得する

まずバックアップを取得するスクリプトを作成します。
個人サイトで、更新頻度も週に何回程度なので毎日のバックアップ(1か月間保存)と毎月の保存(1年間保存)だけ行います。
仮に/backup/dailyディレクトリに日次のバックアップを、/backup/monthlyに月次バックアップを入れる想定で以下のようなスクリプトを2つ作成します。

  • DailyBackupDB.sh
 #!/bin/bash

cd /backup/daily

filename='Backup_Daily_'"$(date +%d)"'.tar'
# $(date +%d)で日付01~31を取得

pg_dump -U [ユーザー名] -d [DB名] -Ft -f $filename
# 各オプションの機能は以下の通り
# -U: postgresに設定したユーザー名を入力して標準入力をスキップする
# -d: 操作対象のDatabaseの名前を指定。今回はバックアップ取りたいDB名を入力
# -F: dumpするフォーマット。plain, tar, custom等指定できる。今回はtarを使用。
# -f: 出力するファイル名。
  • MonthlyBackupDB.sh
#!/bin/bash

cd /backup/monthly

filename='Backup_Monthly_'"$(date +%m)"'.tar'

pg_dump -U [ユーザー名] -d [DB名] -Ft -f $filename

各単位(月なら01~12まで、日なら01~31まで)のファイル名を使って上書きすることで、
保存期間を過ぎたら削除する等の面倒な処理は省きます。
(なお、30日、31日のない月にN月分古いデータが残ることにはなります。)

後はcrontabに毎日0時と毎月1日の12時にバックアップを取得するよう設定するだけ。

0 0  * * * /www/bin/DailyBackupDB.sh
0 12 1 * * /www/bin/MonthlyBackupDB.sh

サーバーにバックアップを置くだけならとっても簡単です。
(※業務とかでバックアップ取るときはちゃんとエラーハンドリング・ログ出力等しましょう。
特にエラーになったらメール送らないと、いつの間にかバックアップ取れなくなってていざって時に困ったりします。)

 

余談ですが、crontabを設定するときはチェックサイトが視覚的に設定日付を確認しやすくてお勧めです。

バックアップをGoogle Driveに保存する。

今回の本題です。Google Driveは無償で保存できる容量が決まっており、バックアップを使用する想定はサーバーがぶっ壊れて最新データを復旧したい場合だと思うので、月次、日次の最新だけを逃がすことにします。

Google Drive API を使う方法もありますが、クレジットカードを登録する必要があります。
今回の用途ならほぼ確実に無料枠で済みますし、不安であれば制限を設ければいいのですが、GoogleはMapのAPIの有料基準を下げて来たことがあるのでクレカ登録はやめておきたい。

(これで1度仕事で使ってたAPIで課金されました。恐らくお知らせメールは来てたはずなので、確認が漏れた自分が悪いし、個人の場合自分の腹が痛むだけなので仕事でやらかすよりはマシですが、GCPにクレカ登録するのがトラウマ気味。)

そこで使用するのがGAS(Google App Script)です。

GAS(Google App Script)とは

GASとは、ざっくりいうと「Googleドライブ上に配置したJavaScriptコードをGoogleのサーバーで実行できるよ」というものです。

Google Apps Script は効率的なアプリケーション開発プラットフォームです。Google Workspace と連携するビジネス アプリケーションをすばやく簡単に作成できます。コードを最新の JavaScript で記述し、Gmail、カレンダー、ドライブなど、お気に入りの Google Workspace アプリケーション用の組み込みライブラリを利用できます。インストール作業は不要です。ブラウザ内で動作するコードエディタが用意されており、スクリプトは Google ドライブに保存され、Google のサーバーで実行されます。

Google Apps Script の概要より引用

 

GASのメリットは、ただJavaScriptを無料のサーバー上で実行できるだけではなく、引用に「Gmail、カレンダー、ドライブなど、お気に入りの Google Workspace アプリケーション用の組み込みライブラリを利用できます」とある通り、Googleのサービスへのアクセスが容易に行えることです。

例えば、以下のようなことが行えます。

  • 任意のファイルをGoogleDriveに配置
  • スプレッドシートにアクセスして内容を変える。

 

GASを使うことでGCPにクレジットカードを登録することなくGoogleDrive上にバックアップファイルを配置していきます。

GASでファイルアップロードAPIを作成

バックアップファイルをアップロードするためのAPIを作成します。

// Google Driveのアップロード用フォルダのID
 ※ フォルダIDの確認方法は下記参照
const FOLDER_ID = '[アップロードしたいGoogleDriveのフォルダID]';

function doPost(e) {
  try {
    // ファイルデータを取得
    const postData = JSON.parse(e.postData.contents);

    const filename = postData.filename;

    const data = Utilities.base64Decode(postData.file);
    const blob = Utilities.newBlob(data, null, filename);

    // アップロード用のGoogleDriveフォルダを取得
    var uploadFolder = DriveApp.getFolderById(FOLDER_ID);

    // 同名ファイルがあるなら一度削除してからファイルをアップロードする
    const targetFiles = uploadFolder.getFiles();
    while (targetFiles.hasNext()) {
      let targetFile = targetFiles.next();
      if (targetFile.getName() == filename) {
        targetFile.setTrashed(true);
      }
    }

    // 新しいフォルダ内にファイルをアップロード
    uploadFolder.createFile(blob);
    // 成功メッセージを返す
    return ContentService.createTextOutput(JSON.stringify({ status: "success", message: "File uploaded successfully." })).setMimeType(ContentService.MimeType.JSON);

  } catch (error) {
    // エラーメッセージを返す
    return ContentService.createTextOutput(JSON.stringify({ status: "error", message: error.message })).setMimeType(ContentService.MimeType.JSON);
  }
}

GASではdoPostという名称で関数を作成する事で、POSTリクエスト時に自動的に関数を実行してくれます。
後述しますが、jsonデータとしてPOSTリクエストを送ると、内容は「e.postData.contents」に入ります。

今回は以下のようなjsonデータを送付します。

{
    filename: [Googleドライブに配置する時のファイル名],
    file: [バックアップファイルのbase64エンコードデータ]
}

受け取ったデータをJSON.parse()することでJsonデータとして扱えますので、それぞれ変数に格納。
fileデータの方はbase64エンコードしているので、デコードし、ファイルとして保存するためblobに直します。
後は任意のフォルダにcreateFileして保存すればOKです。

※ なお、Google DriveはファイルをIDで管理するため、同名データが同一ディレクトリ上に存在できます
よって、ただcreateFileするだけだと同じ名前のデータが複数作成されてしまいますので、予め同名データは消してから作成しています。
 

GASにコードをコピペしたら、デプロイします。(deployIDは後で使うのでメモしておきます。)
デプロイは特に設定等はせず、デフォルトのままデプロイしてしまって大丈夫です。

フォルダIDを確認する方法

フォルダIDを確認する場合、GoogleDriveにアクセスし、該当フォルダにアクセスします。
URLが「https://drive.google.com/drive/folders/[ここにフォルダID]」という形式になるので、末尾の文字がフォルダIDになります。

GoogleDriveのフォルダIDの確認方法のスクリーンショット。

APIを実行する方法

以下のようなCurlコマンドを実行することでファイルをアップロードできます。

URL='https://script.google.com/macros/s/[メモしたdeploy ID]/exec'
DATA="$(base64 [アップロードしたいファイル名])"
curl -L -d @- $URL << EOM
{
    "filename": "[GoogleDriveに配置する際のファイル名]",
    "file": "${DATA}"
}
EOM
# 各オプションの機能は以下の通り
# -L : リダイレクトさせる。GASはリクエスト後、別のURLにリダイレクトしてデータを受け取っているらしく、このオプションが無いとエラーが表示される。
# -d @- : POSTで持たせるデータを設定できる。 @- とすることで、標準入力をデータとして渡すことが可能。

後はこれをcronに登録したshellscriptファイルに以下のように追記するだけで、GoogleDriveに日次・月次のDBバックアップファイルを配置出来るようになりました。

#!/bin/bash

cd /backup/daily

filename='Backup_Daily_'"$(date +%d)"'.tar'
# $(date +%d)で日付01~31を取得

pg_dump -U [ユーザー名] -d [DB名] -Ft -f $filename

##### 以下を追記 #####
URL='https://script.google.com/macros/s/[deployID]/exec'
DATA="$(base64 $filename)"
curl -L -d @- $URL << EOM
{
    "filename": "DailyBackup.tar",
    "file": "${DATA}"
}
EOM

これを使えばDBだけでなく、アップロードした画像ファイル等もzipで固めてGoogleDriveにバックアップを取得といったことも可能です。

※ アップロードファイルを含める場合、サイトの規模によっては簡単に容量制限に引っ掛かりそうなのでGoogleDriveの容量には要注意。

※ 300KB程度のデータまでは行けましたが、さらに重くなった場合にアップロードできるか(ファイルの容量制限はあるのか)は検証してないです。

おわりに

これでバックアップをサーバーの外に逃がすことが出来るようになりました。
容量がどの程度までいけるのか分からないので、業務などで使うにはやや不安がありますが、
個人サイトのバックアップに使うならこれで十分だと思います。

pg_restoreでちゃんとバックアップが反映できるかローカルなどで確認することもお忘れなく。

参考リンク

URL='https://script.google.com/macros/s/[メモしたdeploy ID]/exec'
curl -L -F "file=$(base64 [アップロードしたいファイル名])" $URL
記事の作成者のA.W.のアイコン

この記事を書いた人

A.W.
茨城県在住Webエンジニアです。 PHPなどを業務で使用しています。 趣味ではGoやNuxt、Flutterをやってます。

Comment