コンテンツに移動
デベロッパー

PostgreSQL 拡張機能の Google Cloud マイクロサービスへの変換

2021年11月15日
https://storage.googleapis.com/gweb-cloudblog-publish/images/pg_cron_header.max-1300x1300.png
Google Cloud Japan Team

※この投稿は米国時間 2021 年 11 月 2 日に、Google Cloud blog に投稿されたものの抄訳です。

データベースを移行する際の課題の一つに、互換性の問題を回避できるよう、お客様の環境を整備することが挙げられます。たとえば、Cloud SQL のような Google Cloud 上のマネージド サービスに移行することを検討している際に、使用中の拡張機能がサポートされていないことがわかる場合があります。Google Cloud では、できる限りすべてのプラグインをサポートしたいと考えていますが、Cloud SQL との統合で、確実に障害が起こらないように対応するには時間がかかります。

たとえば、pg_cron というデータベース内で crontab を使用するために必要な PostgreSQL プラグインを例として見てみましょう。このプラグインは、VACUUM を使用して、使われていない古いデータを枝刈りする、テーブル内の不必要なデータを削除するなど、定期的なタスクの処理に活用でき、非常に便利です。

この pg_cron は、今のところサポートされていませんが、必要な処理によっては、この機能を容易に再実装できる場合があります。また、今後 Google Cloud で pg_cron がサポートされたとしても、処理をサービスとして分割し、データソースからビジネス ロジックを独立させて隔離した方が適切な場合もあります。本日取り上げるのは pg_cron についてのみですが、ビジネス ロジックをデータベース拡張機能から独立させて複数のサービスに分割すれば、データをどこにでも必要な場所に移行でき、柔軟性を高めることができます。データ固有のソリューションについて心配する必要もありません。

では、pg_cron のタスクを分割する方法について、順を追ってご紹介します。

ツール

cron タスクを作成するためには、主に Cloud Scheduler を使用します。このプロダクトは、簡単に言うと GCP プロダクト用の crontab です。コンソールで新しいジョブを作成すると、お馴染みの cron インターフェースが立ち上がり、ジョブをトリガーする日時を特定のタイムゾーンとともに定義できます。

https://storage.googleapis.com/gweb-cloudblog-publish/images/image6_Jn6o3kK.max-700x700.max-700x700.png

次のステップは、cron とは若干異なります。通常の cron では実行するジョブへのパスを定義しますが、Cloud Scheduler の場合はこれと異なり、トリガー ターゲットを定義する必要があります。任意の HTTP URL のヒット、事前定義された Pub/Sub トピックへのメッセージ送信、作成した App Engine インスタンスへの HTTP メッセージの送信が可能です。どのメソッドを使用するかは、当然ながら既存のどのタスクを移行するかによって異なります。

たとえば、特に関連性のない複数のアクションをトリガーする必要があるジョブの場合、おそらく最も妥当なのは、Pub/Sub にメッセージを送信し、別のサービスでメッセージの送信先のトピックをサブスクライブする方法です。この場合、委譲元のパターンをミラーリングすることになります。また、一連の関連タスクをトリガーする必要のあるジョブの場合、エンドポイントとして App Engine アプリケーションをビルドし、関連するタスクをまとめて処理できるようにするのが最も理に適っています。最後に、ここでご紹介するのは、小さなタスクを実行する 1 回限りのジョブの場合です。こうした 1 回限りのタスクは、Cloud Functions の関数 を作成するか、コンテナを Cloud Run で実行できるように設定して処理します。サーバレス サービスはゼロにスケーリングするため、実行している間以外は、料金が発生しません。

これを実行するための手順の一つを、簡単な例を挙げて説明します。

チュートリアル

たとえば、毎晩バックアップが完了した後、午前 1 時に pg_cron ジョブを実行して、あるテーブル内の古いデータを枝刈りし、運用データを 30 日間分に保つとします。

読み込んでいます...

最初のステップは、古いデータを取り除く SQL クエリの機能に他の場所からアクセスすることです。すでにお話した通り、GCP ではこれを実現する複数の方法がありますが、今回は、Google Cloud Functions を使用します。Cloud Functions での構築は非常に簡単です。また、この種の 1 回限りの関数はユースケースとしても最適です。

Cloud SQL インスタンスと対話する Cloud Functions 関数の作成方法を記述した非常に優れた Codelab がありますが、この Codelab に 2 点変更を加えます。まず 1 つ目は、このコードサンプルに含まれる Insert 呼び出しの stmt 変数を、pg_cron 関数の Delete 呼び出しに変更します。2 つ目に、この Codelab では Google Cloud Functions 関数の未承認の呼び出しを許可していますが、これを許可しないようにする必要があります。未認証リクエストを許可しても、捨てる必要のある古いデータのみを削除するため、致命的な影響はありません。しかし、たまたま誰かが URL を入手してスパム行為に使用すると、データベースのパフォーマンスに影響を及ぼすだけでなく、Cloud Functions の関数の呼び出しに余分な費用がかかることがあります。

なお、この設定では Cloud SQL インスタンスの作成時に、パブリック IP アドレスも作成されます。この記事のテーマは、拡張機能をマイクロサービスに変換することですので、ここではこれ以上の詳細には踏み込みませんが、Cloud SQL インスタンスの要件によっては、接続性の問題が生じる可能性があることをご承知ください。Google Cloud のサーバレス サービスの Cloud SQL への接続性に関しては、今後の記事でもう少し詳しく取り上げる予定です。

この記事の手順に従って作業される場合は、ここで Codelab にアクセスして前述の変更を行ってから、続きをご覧ください。急がずにゆっくり作業してください。

設定がすべて完了したら、続きをすすめましょう。

これで、関数の設定が完了しました。テストと実行を行ったところ、データベースから 1 か月以上前のエントリが適切に削除されました。次に、この関数を呼び出すために、Cloud Scheduler タスクを設定します。

先ほどの作成ページに戻って、詳細を説明しながら次に進みましょう。

https://storage.googleapis.com/gweb-cloudblog-publish/images/image4_wcbmHZd.max-700x700.max-700x700.png

UI に示されているように、頻度は cron の標準形式です。毎晩午前 1 時にクリーンアップ スクリプトを実行するためには、[頻度] フィールドを 0 1 * * * に設定します。

この場合、Cloud SQL インスタンスのリージョンを us-west2 で作成したため、タイムゾーンをアメリカ太平洋夏時間(PDT)に設定します。

https://storage.googleapis.com/gweb-cloudblog-publish/images/image1_Rh5fUcn.max-800x800.max-800x800.png

HTTP によって Cloud Functions 関数がトリガーされるように設定したため、HTTP エンドポイントをヒットするよう Scheduler タスクを設定します。URL は、作成した Cloud Functions 関数の詳細から確認できます。

https://storage.googleapis.com/gweb-cloudblog-publish/images/image2_gaK0zQX.max-700x700.max-700x700.png

試しに Cloud Functions の関数が未承認の接続を受け入れるよう設定していた場合(本番環境では避けてください)、設定はほぼ完了です。下部にある [作成] をクリックすると、すぐに処理が開始します。ただし、この設定を無効にしていた場合、リクエストに Auth ヘッダーを添付して送信する必要があります。これには、OAuth トークンまたは OIDC トークンのどちらかを使用できます。大ざっぱに言えば、少なくとも GCP ターゲットに関しては、*.googleapis.com 上の API をヒットする場合は OAuth トークンを、それ以外の場合は OIDC トークンをおすすめします。この場合、Cloud Functions は OIDC トークンを使用できます。サービス アカウントには、Cloud Functions の関数でサービス アカウントとして使用しているのと同じアカウントを指定できます。どちらの場合も、Cloud Functions 関数を正常に呼び出すためには、サービス アカウントに Cloud Functions 起動元のロールを追加する必要があります。新しく作成したサービス カウントか既存のサービス アカウントに Cloud Functions 起動元のロールを追加したら、サービス アカウントの完全なメールアドレスを Cloud Scheduler のフィールドに指定します。[Audience] フィールドは省略可能です。このサービスでは指定する必要はありません。

これで全部です。[作成] ボタンをクリックすると、Scheduler のタスクが作成され、指定したスケジュールで実行されます。このタスクをテストする際に、頻度を 5 * * * * に設定し、Cloud Function 関数の出力先をコンソールにすると、実行したかどうかを、Logging で確認できます。作成した Cloud Functions 関数をクリックして詳細を表示すると、ログのナビゲーション タブが表示されます。このナビゲーション タブをクリックすると、プロジェクトのログがこの関数のみに絞り込んで表示されます。

https://storage.googleapis.com/gweb-cloudblog-publish/images/image5_UJf7FtP.max-1000x1000.max-1000x1000.png

スパムが送信されないことを確認するには、Hello World と表示する簡単な Cloud Functions の関数を作成して、Scheduler でトリガーするテストを行うことをおすすめします。

これで、PostgreSQL 拡張機能をマイクロサービスに置き換えることができました。今回は、pg_cron の機能を Cloud Scheduler で置き換える方法をご紹介しました。この記事が、ビジネス ロジックの一部をデータベースから独立させ、複数のサービスに分割することについて考えるきっかけになれば幸いです。ここでご紹介したのは単純な事例ではありますが、プライマリ データベースの負担軽減にもつながる可能性があります。

最後までお読みいただきありがとうございました。ご質問やご意見などございましたら、Twitter の DM でお気軽にお問い合わせください。

- デベロッパー アドボケイト Gabe Weiss

投稿先