Cloud SQLからAlloyDBへ移行した時にやったこと
業務委託としてお手伝いをしている渡部です。主にバックエンド、インフラ領域を担当しています。
Stena Expenseでは、メインのデータベースとしてCloud SQLを用いていましたが、速度周りで課題が多かったため、Cloud SQLと同じくSQL系のデータベースのサービスである「AlloyDB」へ移行しました。
AlloyDBへの移行は、ドキュメントがすべて英語である点や、参考にできる事例が少ない点もあり、ゼロベースで移行の計画を立て、やり切る必要がありました。
この記事では、Cloud SQLからAlloyDBへ移行した際に、特に大変だった点や工夫した点を記載します。
※移行作業は2024年6月に実施したため、記事執筆時点(2024年11月)と異なる点もあるかもしれませんがご了承ください。
AlloyDBへの移行の背景
Stena Expenseでは、2024年2月に「Analysis機能」という新機能をリリースしています。
この機能では、経費データの分析を行う特性上、集計クエリが頻繁に発行されます。集計クエリは全捜査を伴うため、Cloud SQLではレイテンシーが悪化していたことが課題でした。
そのため、AlloyDBの高性能な分析クエリ処理能力に期待して、CloudSQLからの移行が決定されました。
接続方法の変更
移行に際して、KubernetesやCloud Runなどの接続からAlloyDBへの接続方法の変更が必要でした。Cloud SQL使用時はPublic IPによる接続をしていましたが、AlloyDBへ移行する際には、セキュリティ面とPublic IP接続がPreviewだった点(2024年7月にGAになりました)を考慮し、Private IPによる接続を選択しました。
このPrivate IP接続を実現するために、既存リソースの設定変更や新規リソースの作成を行いました。
この作業は、接続元ごとに最適な接続方法を検討し検証を重ねたため、最も大変な作業でした。
接続元/接続先 | Cloud SQL | AlloyDB |
---|---|---|
Kubernetes(GKE) | Cloud SQL Auth Proxy | AlloyDB Auth Proxy |
Cloud Run Jobs | 専用の接続機能 | Language Connector(認証) + Direct VPC Egress(接続) |
Cloud Build | デフォルトプール | プライベートプール |
Local | Cloud SQL Auth Proxy | IAPトンネリング |
プライベートサービスアクセスの作成
AlloyDBにPrivate IP接続するためには、プライベートサービスアクセスの作成が必要です。これはユーザーのVPCとAlloyDBが配置されるサービスプロデューサーネットワークをVPCピアリングで接続するためのリソースです。また、このプライベートサービスアクセスは、後述のDatabase Migration Serviceによるデータベース移行でも使用します。
必要なリソースはこちら。
- IPアドレスレンジの割り当て
- プライベート接続の構成
詳しくは、以下のドキュメントをご参考ください。
ちなみに、Terraformを使用する場合、こちらの例が参考になります。
以降では、接続元ごとの接続方法について記載します。
Kubernetes(GKE)
Kubernetesでは、Cloud SQL Auth Proxyのコンテナをサイドカーとして起動しCloud SQLに接続していました。AlloyDB移行後は、AlloyDB Auth Proxyをサイドカーとして起動し、同一VPC内のAlloyDBへ接続しています。
また、GKEのPodからPrivate IP接続するために、クラスタをVPCネイティブクラスタとして作成し直しました。VPCネイティブクラスタの設定は既存のクラスタには適用できないため、新規に作成する必要があります。
Cloud Run Jobs
Cloud Run Jobsからは、Cloud Runに備わったCloud SQLへの専用の接続機能を使用してCloud SQLへ接続していました。
AlloyDB移行後は、Language Connectorによって認証を行い、Direct VPC EgressによってAlloyDBへ接続しています。
まず、Cloud Runでは、Cloud SQL向けの専用の接続機能が存在しますが、AlloyDB向けには存在しません。さらには、Cloud Run Jobs では、コンテナをサイドカーとして起動することはできないため、AlloyDB Auth Proxyを利用した接続はできません。(Cloud Run Serviceならばサイドカーの起動が可能です。)
そのため、アプリケーションのコードを alloydb-go-connector を使って認証できるように修正しました。Cloud Run JobsからAlloyDBへの接続には、Direct VPC Egressによる方法とServerless VPC Connectorによる方法を検討し、パフォーマンスとコストの観点からDirect VPC Egressによる方法を採用しました。
余談ですが、計画当初はDirect VPC EgressがまだPreviewだったため採用を見送りVPC Connectorを利用していましたが、2024年4月にDirect VPC EgressがGAとなったため、Direct VPC Egressへ切り替えました。
Cloud Build
Cloud Buildでは、デフォルトプールでCloud SQL Auth Proxyを利用しCloud SQLへ接続していました。AlloyDB移行後は、プライベートプールでAlloyDB Auth Proxyを利用しています。
Cloud BuildでVPCピアリングを利用してプライベートリソースにアクセスするためには、プライベートプールの利用が必須となります。
デフォルトプールとプライベートプールの比較は以下のドキュメントをご参考ください。
Local
ローカルからはCloud SQL Auth Proxyを利用しCloud SQLへ接続していました。AlloyDB移行後は踏み台サーバーとしてCompute Engineを作成し、ローカルからIAPでトンネリングをしてAlloyDBに接続しています。
Compute Engine等のリソース作成には下記の記事を参考にしました。
ローカルからIAPトンネリングを利用してCloud SQLにで接続する記事ですが、AlloyDBへの接続もほぼ同様の方法で実現できました。
Database Migration Serviceによるデータベース移行
移行方法の選定
Cloud SQLからAlloyDBへの移行には、Database Migration Service for AlloyDB(以下、DMS)を選択しました。
DMSを選択した理由は、Continuous ReplicationやMinimal Downtimeが有用だと考えたからです。
結果として、期待通り短いダウンタイムでデータベース移行を完遂できたため、DMSを採用したのは正解だったと思います。
一方で、psql や pg_restore コマンドを使ったバックアップとリストアの方法とは異なり、DMSを利用するにはソースのデータベースの設定変更がいくつも必要だったため、つまづく点が多かったです。
以下、DMSによるデータベース移行をするために行った手順を記載します。
Cloud SQLの設定変更
Private IPの有効化
まず前提として、このPrivate IPの有効化はプライベートサービスアクセスの作成が前提となっています。プライベートサービスアクセスが作成されていないと NETWORK_NOT_PEERED
などのエラーが発生します。
DMSのソースのデータベースにCloud SQLを設定し、宛先のデータベースにAlloyDBを設定する場合、どちらのデータベースも同一ネットワークに配置しVPCピアリングによる接続を行う必要があります。
そのため、Cloud SQLのPrivate IP接続を有効化する必要があります。
Terraformを使っている場合は、ドキュメントに記載の通り google_sql_database_instance の ip_configuration の設定をすることでPrivate IPを有効化できます。
1つ注意すべきなのは、Private IPを有効化すると、ダウンタイムを伴う再起動が発生することです。
拡張機能のインストール、ユーザーへの権限の付与
DMSで必要な拡張機能をインストール可能にするためにCloud SQLのデータベースフラグである、cloudsql.logical_decoding
と cloudsql.enable_pglogical
のフラグを on
にします。
その後、移行対象のデータベースが複数ある場合はすべてのデータベースにログインし、以下のコマンドで拡張機能をインストールします。
CREATE EXTENSION IF NOT EXISTS pglogical;
上記コマンドを実行後、インスタンスを再起動します。
また、DMS実行時に使用するデータベースのユーザーに、GRANTコマンドで必要な権限を付与する必要があります。
詳細は以下のドキュメントを参照ください。
DMS移行ジョブのテスト
DMSを使いデータベース移行をするには、様々なリソースの追加や設定が必要です。移行当日にDMSを実行し、想定外のことが起きないように事前にDMSの移行ジョブのテストを行いました。
DMSでは移行ジョブを作成すると、テストを実行しソースのデータベースへの接続を確認することができます。
このテストでは、必要リソース不足や権限不足で何度も失敗したため、その都度ドキュメントを読み修正しました。
また、実際にDMSを実行することで、移行にかかる時間やソースのに与えるパフォーマンス影響も把握することができました。
以下余談ですが、DMSの移行テスト実行にあたってはこのエラーに悩まされました。様々な検証をしても移行テストは成功せず、結果的にCloud SQLのインスタンスを再起動したら解消しました。
Failure connecting to the source database. Make sure the connectivity information on the connection profile is correct and the source database is reachable. Address the issues then start the migration job.
generic::unknown: unable to connect to source database server: failed to connect to the source database "postgres": connectWithTimeoutAndRetry timeout with error: dial tcp <IP_ADDRESS>:<PORT>: connect: connection timed out
データベース移行後のテストの実施
DMSによるデータベース移行が成功しているかどうかを検証するためのテストケースを作成し、実施しました。
移行前のCloud SQLと移行後のAlloyDBで以下の内容が同一なことを検証しました。
- テーブル数
- インデックス数
- 主要なテーブルのレコード数
実際に利用したクエリを数パターン記載します。
テーブル数を抽出するクエリ
SELECT COUNT(*)
FROM information_schema.tables
WHERE table_schema = 'public' AND table_type = 'BASE TABLE';
インデックス数を抽出するクエリ
SELECT COUNT(*)
FROM
pg_index AS idx
JOIN pg_class AS c
ON c.oid = idx.indexrelid
JOIN pg_namespace AS ns
ON c.relnamespace = ns.oid
WHERE ns.nspname = 'public';
レコード数を抽出するクエリ
SELECT COUNT(*) FROM <table_name>;
PostgreSQLのバージョンアップ
バージョンアップに伴う破壊的変更の調査
移行前のCloud SQLのPostgreSQLのバージョンは13であり、移行後のAlloyDBのPostgreSQLのバージョンは15でした。
そのため、PostgreSQLの13→14、14→15でそれぞれの破壊的変更が含まれる処理がないかどうかを調査しました。
影響調査の方法としては、以下のドキュメントの「バージョン14への移行」や「バージョン15への移行」の内容を1つずつ確認し、影響のある関数がアプリケーションで使用されていないかどうかを都度 grep して確認しました。
結果として、バージョンアップに伴うコードの変更は必要ありませんでした。
バージョンアップの実施
ローカルやCIで使用しているPostgreSQLのDocker imageのバージョンを13から15へ上げました。
ローカルでは、単にバージョンを上げるのみだと以下のエラーが出ました。
FATAL: database files are incompatible with server
DETAIL: The data directory was initialized by PostgreSQL version 13, which is not compatible with this version 15.7 (Debian 15.7-1.pgdg120+1).
エラーメッセージに記載の通り、data directoryが初期化された際のPostgreSQLのバージョンと現在実行しているPostgreSQLのバージョンに互換性が無いことが原因です。
ローカルのデータは削除しても業務に影響ないことをチームのメンバーに確認し、rm -rf <data_directory>
によってdata directoryを削除することでこのエラーを解消しました。
移行作業
移行手順書の作成
DB移行当日に作業を円滑にできるよう、移行手順書を作成しました。テスト環境でも同じ手順書を利用してDB移行を実施することで、本番環境に向けて手順をブラッシュアップすることができました。
## メンテナンスモード開始
- [ ] メンテナンスモードを開始した
## DMSのプロモート
- [ ] フェーズが`増分読み込み進行中` であることを確認した
- [ ] プロモートを実行した
## DMS完了時のテスト
Cloud SQLとAlloyDBにそれぞれクエリを以下を確認する
- [ ] テーブルの数が一致した
- [ ] インデックスの数が一致した
- [ ] <table_name_a>のレコード数が一致した
- [ ] <table_name_b>のレコード数が一致した
## Cloud SQLからAlloyDBへの切り替え
- [ ] 接続先を切り替えるPRをマージした
- [ ] CIはすべてGreenになった
## AlloyDBへの接続先切替後のテスト
### Application A
- [ ] ログインできること
- [ ] ログアウトできること
- [ ] <重要機能>が実行できること
### Application B
- [ ] ログインできること
- [ ] Xのリソースを作成できること
- [ ] <重要機能>が実行できること
## メンテナンスモード終了
- [ ] メンテナンスモードを完了した
その他実施したこと
その他詳細は省きますが以下の点も実施しました。
- Infrastructure as Code
- バックアッププランの策定
- 必要リソース(CPU)の算定
- SSLモードの強制
- コネクタの適用の強制
- Postgresのロールの運用の見直し
- readonlyuserの導入
やればよかったこと
- 本番環境と同等のデータを投入したデータベースでのパフォーマンス試験
- 本番環境と開発環境の差分確認
- 開発環境のGKEはVPCネイティブクラスタが有効化されていたため移行がスムーズに出来ましたが、いざ本番環境の移行時にGKEのVPCネイティブクラスタが無効化されていることがわかり、移行作業時にクラスタごと作り直すなどの想定外の事象が発生しました
まとめ
初めてのデータベース移行作業でしたが、完遂できて良い経験となりました。
データベースの移行はさまざまなリスクが存在するため、できるだけリスクを最小化できるよう様々な方法で検証を重ねましたが、本番環境の移行時には想定していなかったトラブルもありました。
もしかしたら今後データベースの移行に携わる機会は多くないかもしれませんが、今回の学びを今後のアプリケーション開発やインフラ移行の経験に活かしていきたいと思います。
Discussion