💡

【GCP】Cloud SQLのデータエクスポートを自動化

2021/08/31に公開

趣旨

2021/8/30現在、Cloud SQLのデータエクスポートを自動化するオプションは無いようです。
本ブログは、公式を参考に、自動化ツールの作成を解説していきます。

前提

  • 対象のCloud SQLが構築されていること。

使用するサービス

  • Cloud SQL
  • Cloud Storage
  • Cloud Functions
  • Pub/Sub
  • Cloud Scheduler
  • App Engine

※各サービスのAPIを事前に有効化しておくとスムーズです。

イメージ図は下記。


1. 環境変数の定義

1. Cloud Shell起動

GCPにログインして、プロジェクトを選択。
画面右上のアイコンにて、一番左側のアイコンをクリック。
Cloud Shellが開いたことを確認。

2. 環境変数の定義

下記コマンドを実行。
※1 自分で作成したもののID/名前を参照
※2 任意

$export PROJECT_ID="※1"
$export SQL_INSTANCE="※1"
$export BUCKET_NAME="※2"
$export GCF_NAME="※2"
$export PUBSUB_TOPIC="※2"
$export SCHEDULER_JOB="※2"
$export REGION="※2"
$export SQL_ROLE="sqlExporter"
$export STORAGE_ROLE="simpleStorageRole"

2. カスタムロールの作成

1. ロール(1)の作成

Cloud Storageに対して、オブジェクトの作成・読み込みを可能にするロールを作成。
本ブログ上では、ロール(1) と表現します。

$gcloud iam roles create ${STORAGE_ROLE} --project ${PROJECT_ID} \
    --title "Simple Storage Role" \
    --description "Grant permissions to view and create objects in Cloud Storage" \
    --permissions "storage.objects.create,storage.objects.get"
2. ロール(2)の作成

Cloud SQLに対して、データエクスポートの実行を可能にするロールを作成。
本ブログ上では、ロール(2) と表現します。

$gcloud iam roles create ${SQL_ROLE} --project ${PROJECT_ID} \
    --title "SQL Exporter Role" \
    --description "Grant permissions to export data from a Cloud SQL instance to a Cloud Storage bucket as a SQL dump or CSV file" \
    --permissions "cloudsql.instances.export"

3. バケットの作成

エクスポートしたデータを格納するバケットを作成します。

$gsutil mb -l ${REGION} gs://${BUCKET_NAME}
$gsutil ls
# バケットが作成されていることを確認。

4. サービスアカウントの設定

1. サービスアカウントの紐づけ(SQLインスタンス)

SQLインスタンスのサービスアカウントに対して、「2.カスタムロールの作成」で作成したロール(1)を紐づけます。
これで、SQLインスタンスから対象のバケットへ、エクスポートしたデータを置くことが出来ます。

# 環境変数の定義
$export SQL_SA=(`gcloud sql instances describe ${SQL_INSTANCE} \
>     --project ${PROJECT_ID} \
>     --format "value(serviceAccountEmailAddress)"`)

# SQLインスタンスのサービスアカウントへロール(1)を紐づけ
gsutil iam ch serviceAccount:${SQL_SA}:projects/${PROJECT_ID}/roles/${STORAGE_ROLE} gs://${BUCKET_NAME}

サービスアカウントと権限の付与についてはこちら

2. サービスアカウントの作成と紐づけ(Cloud Functions)

Cloud Functionsのサービスアカウントを作成し、ロール(2)を紐づけます。
これによって、ファンクションからSQLインスタンスに対し、データのエクスポートを実行させることが可能になります。

※ファンクション自体は作成しておりませんが、作成時と同時にサービスアカウントを紐づけられると楽なので、先に作っておきます。

#サービスアカウントを作成
$gcloud iam service-accounts create ${GCF_NAME} \
    --display-name "Service Account for GCF and SQL Admin API"

#サービスアカウントとロール(2)を紐づけ
$cloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member="serviceAccount:${GCF_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="projects/${PROJECT_ID}/roles/${SQL_ROLE}"


5. Pub/Subトピックの作成

Cloud SchedulerからCloud Functionsに対して実行を促すため、トピックを作成します。

$gcloud pubsub topics create ${PUBSUB_TOPIC}

6. Cloud Functionsの設定

1. 関数の定義

「main.py」を作成します。全部コピペで大丈夫です。

$cat <<EOF > main.py

import base64
import logging
import json

from datetime import datetime
from httplib2 import Http

from googleapiclient import discovery
from googleapiclient.errors import HttpError
from oauth2client.client import GoogleCredentials

def main(event, context):
    pubsub_message = json.loads(base64.b64decode(event['data']).decode('utf-8'))
    credentials = GoogleCredentials.get_application_default()

    service = discovery.build('sqladmin', 'v1beta4', http=credentials.authorize(Http()), cache_discovery=False)

    datestamp = datetime.now().strftime("%Y%m%d%H%M") # format timestamp: YearMonthDayHourMinute
    uri = "{0}/backup-{1}-{2}.gz".format(pubsub_message['gs'], pubsub_message['db'], datestamp)

    instances_export_request_body = {
      "exportContext": {
        "kind": "sql#exportContext",
        "fileType": "SQL",
        "uri": uri,
        "databases": [
          pubsub_message['db']
        ]
      }
    }

    try:
      request = service.instances().export(
            project=pubsub_message['project'],
            instance=pubsub_message['instance'],
            body=instances_export_request_body
        )
      response = request.execute()
    except HttpError as err:
        logging.error("Could NOT run backup. Reason: {}".format(err))
    else:
      logging.info("Backup task status: {}".format(response))
EOF
2. 関数の定義(2)

「requirements.txt」を作成します。

$cat <<EOF > requirements.txt
google-api-python-client
Oauth2client
EOF
3. 関数のデプロイ

定義した関数のデプロイを行います。
なお「4.サービスアカウントの作成」で作成したGCFのサービスアカウントと「5. Pub/Subトピックの作成」で作成したトピックは、ここで紐づけられます。

$gcloud functions deploy ${GCF_NAME} \
    --trigger-topic ${PUBSUB_TOPIC} \
    --runtime python37 \
    --entry-point main \
    --service-account ${GCF_NAME}@${PROJECT_ID}.iam.gserviceaccount.com

7. Cloud Schedulerの設定

1.App Engineの作成

該当のリージョンで既に有効化されている場合は、スキップしていただいて構いません。

$gcloud app create --region=${REGION}
2.ジョブの作成

毎日AM1:00に実行するよう、ジョブを作成します。
正確に言うと、毎日AM1:00、関数にパラメータ(--message-body)を渡し、実行させるようPub/Subトピックの発動計画を作成します。

時間の定義方法が特殊(?)ですので、公式を参考に適宜調整してください。

# ※エクスポートするデータベース名を入力してください。
$gcloud scheduler jobs create pubsub ${SCHEDULER_JOB} \
    --schedule '0 1 * * *' \
    --topic ${PUBSUB_TOPIC}\
    --message-body '{"db":"※","instance":'\"${SQL_INSTANCE}\"',"project":'\"${PROJECT_ID}\"',"gs":'\"gs://${BUCKET_NAME}\"'}'\
    --time-zone 'Japan'

8. 動作確認

1.ジョブのマニュアル実行

本来はAM1:00に設定しましたが、テストとしてマニュアルで動かします。

$gcloud scheduler jobs run ${SCHEDULER_JOB}
2.SQLインスタンスのログを確認

「STATUS」が”DONE”、「ERROR」に何も表示されていなければ成功です。

$gcloud sql operations list --instance ${SQL_INSTANCE} --limit 1
NAME                                  TYPE    START                          END                            ERROR  STATUS
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  EXPORT  2021-06-04T02:57:44.290+00:00  2021-06-04T02:57:55.542+00:00  -      DONE
3.バケットの中身を確認

検証した日時のファイルが新しく保存されていることを確認します。

$ gsutil ls gs://${BUCKET_NAME}
gs://XXXX/backup-db-202106040257.gz

Discussion