yourmystar tech blog
著者: masyus 公開日:

[後編] AWS から Google Cloud への段階的な移行方法

当記事は 2024 年 3 月 1 日に開催された GOOGLE CLOUD Modern App Summit Tokyo '24 の、サーバーレスへの移行による、スケーラビリティ向上とコスト最適化までの道のりの詳細解説となります。

ユアマイスターでは 2021 年から、利用中のパブリッククラウドサービスを AWS から Google Cloud へ段階的に移行し始めました。主要プロダクトの移行が始まったのは 2023 年 8 月で、その後 2024 年 2 月に主要プロダクトが Google Cloud へ移行完了しました。今回は、今まで取り組んできた段階的な移行方法について紹介する記事の後編となります。

前編が気になる方はこちらをどうぞ。

既存の構成を維持しつつ段階移行する流れ

大雑把な段階は以下の通りです。

  1. DB と Session のデータを移行
  2. ランタイムをコンテナ化し、ソースコードをパッケージしたものをイメージ化 → Google Cloud へデプロイ
  3. ロードバランサおよびコンピューティングシステムを移行

ロードバランサおよびコンピューティングシステムの移行が完了した時点で Google Cloud への移行が完了です。本来はストレージ移行も必要ですが、別プロジェクトにて移行検討することになったためここでは取り扱いません。この流れを最初はステージング環境に、次に本番環境に順次適用しました。

先にステージング環境を移行完了させるメリットは、新しいパブリッククラウドに載せ替える際に気がつかなかった構成の盲点を事前に発見し、修正できることです。他方でステージング環境と本番環境とでインフラ構成が異なる期間が生ずることになりますが、その間並走する開発が基本的に Web アプリケーションに閉じたものであったため特に問題はありませんでした。

ここからは上述した 1. ~ 3. について、順を追って解説します。

1. DB と Session のデータを移行

ユアマイスターの場合、具体的には

  • RDS (AWS) → Cloud SQL (Google Cloud)
  • ElastiCache for Redis (AWS) → Memorystore (Google Cloud)

のように移行しつつ、AWS EC2 上で稼働中の Web アプリケーションにおける MySQL と Redis の接続先を Cloud SQL と Memorystore に差し替える流れを取りました。

RDS (AWS) → Cloud SQL (Google Cloud)

DB 移行に際し、気にする必要があったのはデータ量です。ステージング環境では大した量ではありませんでしたが、本番環境となると話は別です。単純にDBデータをダンプしてエクスポート → インポートさせるでは時間がかかりすぎます。そのため、

  1. DB 接続切り替え前に RDS:master → Cloud SQL:slave としてデータを同期させる
  2. ほぼ同期し終えている段階で Cloud SQL を master に promote しつつ、Web アプリケーションの DB 接続を RDS から Cloud SQL へ切り替える

という方法を採用しました。どの環境でも promote と DB 接続切り替え時にメンテナンス時間を取る必要がありますが、事前にデータの大半を同期しておくことでメンテナンス時間を最小限に抑えることが可能です。

RDS:master → Cloud SQL:slave のデータ同期を実現するためには、AWS と Google Cloud のパブリッククラウド間で VPN を設定する必要があります。この VPN は Google Cloud への移行が完了次第、閉じる前提の使い方となります。ユアマイスターでは Google Cloud の Cloud VPN をメインで構成し、AWS 側の VPC と Google Cloud 側の VPC をお互いの内部 IP が被らないようにすることで疎通できるようにしました。

また、RDS:master → Cloud SQL:slave としてデータを同期する方法は Google Cloud の Database Migration Service を採用しました。このサービスは AWS RDS から Google Cloud の Cloud SQL へのデータ移行をサポートしており、データ移行における細かい設定の手間を大幅に削減できるため、ユアマイスターではこのサービスを活用してデータ移行を実現しました。

ステージング環境ではメンテナンスを実施せずにこの手順を実施しました。2. の手順が 10 分程度で完了する見込みでしたため、他のエンジニアへ事前周知をかけた上で作業するで事足りました。

VPN によるレイテンシーの考慮

1 点だけ気にする必要があったのは、AWS 上の Web アプリケーション → VPN → Google Cloud の Cloud SQL へ接続する構成ではレイテンシーが発生することです。実際どれくらいレスポンス速度に影響が出るかは未知数でしたため、ステージング環境における動作検証項目にはレイテンシーの影響調査も入れました。

結果、ページによってはステージング環境でも秒単位でレイテンシーが増加する画面があることが判明しました。当初は Web アプリケーションの Google Cloud 移行前に DB 移行を実現しようとしていましたが、この結果により本番環境における DB 移行作業と並行して Web アプリケーションも Google Cloud へ移行することに決めました。これで、VPN を通すことによるレイテンシーを発生させないようにすることができます。

ElastiCache for Redis (AWS) → Memorystore (Google Cloud)

Redis のデータ量は RDS よりもはるかに少なかったため、シンプルに ElastiCache for Redis から手動ダンプ → Memorystore へ手動インポートする方針を採用しました。この手順を本番環境のメンテナンス時に組み込みます。詳細な手順は次の通りです。

  1. AWS ElastiCache for Redis から AWS S3 へ Session データをダンプ
  2. ダンプしたデータを S3 から Goole Cloud Storage へ転送
  3. ダンプしたデータを Google Cloud Storage から Memorystore へインポート

特に 2. の作業では Google Cloud の Storage Transfer というサービスを使い、転送設定および作業を簡略化しました。

ステージング環境では、やはりメンテナンスを実施せずに他のエンジニアへ事前周知をかけた上で 1. ~ 3. の手順を実施しました。元々揮発性が高く、データが消えてもアプリケーションの動作に重大な影響を与えないため、DB 移行よりは気軽にできました。

2. ランタイムをコンテナ化し、ソースコードをパッケージしたものをイメージ化 → Google Cloudへデプロイ

次にソースコードを含むランタイムをコンテナ化してイメージをpushし、Google CloudのCloud Runへデプロイできるようにします。おおまかな流れは以下の通りです。

  1. AWS EC2 の構成を Dockerfile に書き起こしてランタイムを構成
  2. GitHub Actions でユニットテストを実施後に Docker image を build し、Google Cloud の Artifact Registry へ push
  3. GitHub 上の Pull Request が main ブランチにマージされたらステージング環境へデプロイ
  4. デプロイ先は Cloud Run (Cloud Run サービス, Cloud Run ジョブ)を採用

ここで特筆すべき点は 3 つあります。

  1. CI / CD パイプラインの構築にあたり、なぜ GitHub Actions を選定したのか?
  2. なぜ GitHub Pull Request が main ブランチにマージされたらステージング環境へデプロイするようにしたのか?
  3. なぜ Cloud Run を採用したのか?

それぞれ解説します。

CI / CD パイプラインの構築にあたり、なぜ GitHub Actions を選定したのか?

CI / CD パイプラインの構築で最初に候補となったのは Google Cloud の Cloud Build でした。しかし以下を検討の結果、GitHub Actions を採用しました。

  • GitHub 連携を Terraform で管理できなかった
  • 既に GitHub Actions の利用実績が社内にあった

ユアマイスターではインフラ構成管理を Terraform で統一しようとしているため、その観点で考えますとそもそも Terraform で管理できない構成は避けるべきです。これが GitHub Actions 採用の決め手になりました。但し、今後 Cloud Build の GitHub 連携が Terraform で管理できるようになれば再検討の余地があるかもしれません。

なぜ GitHub Pull Request が main ブランチにマージされたらステージング環境へデプロイするようにしたのか?

ステージング環境においては、feature ブランチのソースコードが GitHub へ push されたら GitHub Actions で当該ブランチ仕様のコンテナイメージを build & push し、自動的に Cloud Run へデプロイという流れを当初想定していました。これにより開発中の機能を Google Cloud のインフラ上にデプロイして精密な動作確認を可能にする予定でしたが、以下の背景により一旦複雑な構成をとらずにステージング環境へデプロイする流れに変更しました。

  • DB, Session, ストレージをブランチ単位で分けるかの検討時間がプロジェクトの都合上あまり取れなかった
  • 実際の開発プロセスではブランチ単位で厳密に Cloud Run に乗せて動作確認する必要がさほど無かった
  • 少人数で開発しているため、ステージング環境が動作検証で多少占有されても特に支障が無かった

もちろん feature ブランチ単位で Cloud Run にデプロイできればインフラ単位の変更における動作確認がしやすくなるため理想的です。その構成への変更は今後の残タスクとして積まれることになりました。

なぜ Cloud Run を採用したのか?

2 点メリットがありました。1 点目は Cloud Run サービスではオートスケーリングが自動で行われるため、スケールアウトの手間が省けることです。ユアマイスターは繁閑差があるビジネス上でプロダクトを運営しているため、オートスケーリングが要件に盛り込まれていました。

2 点目は Cloud Run サービスにはサイドカー機能が搭載されており、PHP と Nuxt のランタイムを別々に運用できることです。ユアマイスターの主要プロダクトは PHP と Nuxt で構成されており、且つ Nuxt へ移行中の段階にいるため、この機能は非常に都合が良かったです。

他の選択候補としては Google Kubernetes Engine もありましたが、GKE を採用しなければいけないほどの要件がなく、且つこのプロジェクトの着手時点で最も運用しやすいサービスが Cloud Run であったため、特に迷うことなく Cloud Run を採用するに至りました。

3. ロードバランサおよびコンピューティングシステムを移行

デプロイ先のドメインは AWS Route53 で管理していましたが、 これも当初は Google Domains へ移行する予定でした。しかし Google Domains が Squarespace へ売却されるというイベントがタイムリーに発生していたため、動向を踏まえつつ慎重に検討する必要がありました。結果的にドメイン管理は Route53 のままとすることになりました。

その上で、コンピューティングシステムおよびロードバランサの移行を実施しました。具体的な流れは以下の通りです。

  1. AWS EC2 と Cloud Run を並行稼働
  2. Google Cloud のロードバランサを立ち上げ、ルーティング先に Cloud Run を指定しつつ SSL 証明書も管理
  3. AWS Route53 の参照先を Google Cloud のロードバランサに変更

ポイントは並行稼働中の動作確認でした。Cloud Run は単独でランダムな URL が発行されるため、この URL からできる範囲で画面の動作確認を実施しました。

また、ステージング環境の移行は特にメンテナンス切り替えせず、エンジニアへ事前周知をかけた上で移行を実施しました。この移行作業で最も時間がかかるのは DNS の TTL が切れて証明書の発行が完了するのを待つ間で、実測値は 40 分程度でした。失敗したら再度 40 分近くかかることを鑑みるに、ステージング環境移行で一番緊張したかもしれません。本番環境ではメンテナンス時間を取った上で移行を完了させました。

本番環境の移行における要点

基本的にこの手の移行は最終的に深夜メンテナンスする必要があることが大半のため、「事前準備をどれだけしっかりやり切り、深夜メンテナンス時間を削減できるか」が重要です。特に

  • Google Cloud 上でロードバランサ以外の機能を予め構築して並行稼働させ、Cloud Run サービスにて発行される URL を使い可能な範囲で画面の動作確認を済ませておく
  • Data Migration Service による AWS RDS → Cloud SQL へのレプリケーションまでは済ませておく
  • Cloud Run ジョブで影響の軽微なバッチを実行して動作確認を済ませておく
  • ステージング環境を先んじて移行完了させておき、バグの発見や修正を行っておく

は非常に効果的だったと実感しています。おかげでシステムメンテナンス当日の TODO と予定時間を削減し、且つ切り戻しの可能性を最大限引き下げることができましたし、結果的に 1 度の深夜メンテナンスで無事に移行を完了させることができました。

振り返ってみて

今回の Google Cloud 移行プロジェクトは TODO の量が多い上に自分が触ったことのない機能も多かったため、並行して学習しながら進めることが求められました。そのため

  1. 事前に Google Cloud の機能を実際に触り動作検証してみる
    1. の試行錯誤で構築した構成を Terraform に自動で書き起こしてくれるツールを活用し、Terraform の習熟を早める
  2. Google Cloud の Professional Cloud Architect の資格を取る

等で学習時間の短縮を図りました。今回のプロジェクトを通して私自身学びが多かったですし、それに見合うだけの結果も会社に対して出すことができたと思います。こうした学びの機会を意図的に自身で作ることができる環境は、エンジニアとして働く上でも非常に有意義だと感じました。

ポストするはてなブックマークに追加シェアする