Zenn
💽

Render.comのPostgreSQLのダンプをS3に毎日バックアップして直近1ヶ月分だけを残す

に公開
1

Render.comのPostgreSQLの自動バックアップ機能

https://render.com/docs/postgresql-backups

こちらの公式ドキュメントで説明されているとおり、Render.comのPostgreSQLには自動でバックアップを保持し、Web UIから簡単にリカバリーを実行できる機能が搭載されています。

ただし、この機能はHobbyプランでは直近3日分のみ、Professional以上のプランでも直近7日分のみしか保持してくれず、やや心許ないです。

pg_dumpによるバックアップをS3に毎日自動で保存させる

長期間のバックアップの保持が必要な場合、pg_dumpによるバックアップをS3に毎日自動で保存させるCron Jobサービスを自前で建てる必要があります。

具体的なやり方は以下の公式ドキュメントで説明されています。

https://render.com/docs/backup-postgresql-to-s3

ただ、このドキュメントで解説されている方法にはやや冗長な内容が含まれているので、本稿では必要最低限の部分だけに絞って簡潔に解説します。

1. Render.comにCron Jobサービスを作成する

まずはCron Jobサービスを作りましょう。

サービスの実装コードの雛形が以下のリポジトリで公開されています。

https://github.com/render-examples/postgres-s3-backups

これをフォークして修正を加えていきます。

  • Dockerfile:修正不要
  • render.yaml:実際のプロジェクトの内容に合わせて要修正
  • backup.sh:半分以上不要なコードなので、不要部分をバッサリ削除しつつ、バックアップファイルのファイル名に使う日付をUTCからJSTに要修正

render.yaml を修正

まず render.yaml ですが、これはCron Jobサービスの設定ファイルなので、実際のバックアップ対象のプロジェクトの内容に応じて修正が必要です。

例えば以下のような感じで修正することになるでしょう。

  services:
    - name: backup-db
      type: cron
-     schedule: "0 3 * * *"
+     schedule: "0 18 * * *" # 3am JST everyday
-     region: oregon
+     region: singapore
      env: docker
-     plan: standard
+     plan: starter
      dockerfilePath: ./Dockerfile
      autoDeploy: false
      envVars:
        - key: DATABASE_URL
          fromDatabase:
-           name: replace-with-your-postgres-instance-name
+           name: {対象のPostgreSQLサービスのインスタンス名}
            property: connectionString
        - key: AWS_REGION
-         sync: false
+         value: ap-northeast-1 # 例えば
        - key: S3_BUCKET_NAME
-         sync: false
+         value: {S3バケット名}
        - key: AWS_ACCESS_KEY_ID
          sync: false
        - key: AWS_SECRET_ACCESS_KEY
          sync: false
        - key: POSTGRES_VERSION
-         sync: false
+         value: 16 # 例えば
        - key: ALPINE_VERSION
-         sync: false
+         value: 3.20 # 例えば

この場合、AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY の2つの環境変数だけは(sync: false のままとしたので)Web UIから手動で設定する必要があります。

これらの環境変数に設定すべき値は後ほど取得します。

backup.sh を修正

次に backup.sh ですが、こちらは バケットの作成もCron Jobから行えるように実装されている ために不要なコードがたくさんあります。

バックアップ先のバケットは最初に手動で作成すればその後はCron Jobから作成し直すことはないので、不要なコードをバッサリ削除してしまいましょう。

また、バックアップファイルのファイル名に使う日付が、そのままだとUTCになってしまうので、これをJSTで出力されるように修正しておきましょう。

具体的な修正内容は以下のとおりです。

  #!/bin/bash
  
  set -o errexit -o nounset -o pipefail
  
  export AWS_PAGER=""
  
  s3() {
      aws s3 --region "$AWS_REGION" "$@"
  }
  
  s3api() {
      aws s3api "$1" --region "$AWS_REGION" --bucket "$S3_BUCKET_NAME" "${@:2}"
  }
  
- bucket_exists() {
-     s3 ls "$S3_BUCKET_NAME" &> /dev/null
- }
- 
- create_bucket() {
-     echo "Bucket $S3_BUCKET_NAME doesn't exist. Creating it now..."
- 
-     # create bucket
-     s3api create-bucket \
-         --create-bucket-configuration LocationConstraint="$AWS_REGION" \
-         --object-ownership BucketOwnerEnforced
- 
-     # block public access
-     s3api put-public-access-block \
-         --public-access-block-configuration \
-         "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
- 
-     # enable versioning for objects in the bucket 
-     s3api put-bucket-versioning --versioning-configuration Status=Enabled
- 
-     # encrypt objects in the bucket
-     s3api put-bucket-encryption \
-       --server-side-encryption-configuration \
-       '{"Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "AES256"}}]}'
- }
- 
- ensure_bucket_exists() {
-     if bucket_exists; then
-         return
-     fi    
-     create_bucket
- }
- 
  pg_dump_database() {
      pg_dump  --no-owner --no-privileges --clean --if-exists --quote-all-identifiers "$DATABASE_URL"
  }
  
  upload_to_bucket() {
      # if the zipped backup file is larger than 50 GB add the --expected-size option
      # see https://docs.aws.amazon.com/cli/latest/reference/s3/cp.html
-     s3 cp - "s3://$S3_BUCKET_NAME/$(date +%Y/%m/%d/backup-%H-%M-%S.sql.gz)"
+     s3 cp - "s3://$S3_BUCKET_NAME/$(TZ=JST-9 date +%Y-%m-%d-%H-%M-%S.sql.gz)"
  }
  
  main() {
-     ensure_bucket_exists
      echo "Taking backup and uploading it to S3..."
      pg_dump_database | gzip | upload_to_bucket
      echo "Done."
  }
  
  main

コードをGitHubにpushしたら、Render.comでCron Jobサービスを作成し、GitHubリポジトリと連携させてください。

2. AWSのIAMユーザーを作成する(バケット作成権限は不要)

実際にCron Jobサービスを動かすにあたり、バックアップ先のS3バケットのフルアクセスのみを許可されているIAMユーザーを作成するなどして認証情報を取得しましょう。

公式ドキュメント の説明では、バケットの作成もできるようにするために「バケット指定なしのS3フルアクセス」を許可するようになっており、とても微妙です。

前項で述べたとおり、バケットの作成は最初に一回だけ手動で行うという前提にすれば、バケット指定でS3フルアクセスを許可できるので安全かつ簡単です。

S3バケットとIAMユーザーを作成したら、アクセスキーとシークレットキーを取得し、Cron Jobサービスの AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY の2つの環境変数に設定してください。

3. 実行してみる

Trigger Run ボタンをクリックしてCron Jobを実行させてみます。

S3バケットを覗いてみて、2025-04-01-00-00-12.sql.gz のようなファイルが作成されていれば成功です。

4. 直近1ヶ月分だけを残して古くなったファイルが自動で削除されるようにする

最後に、直近1ヶ月分だけを残して古くなったファイルが自動で削除されるようにS3バケットを設定します。

これは 公式ドキュメント では紹介されていませんが、S3の ライフサイクルルール を使って簡単に実現できます。

バケットの「管理」を開いて、「ライフサイクルルールを作成する」をクリックします。

例えば以下のような内容で作成すれば、作成されてから31日が経過したファイルが自動で完全に削除されるようになります。

GitHubで編集を提案
1

Discussion

ログインするとコメントできます