こんにちは、開発本部インフラチームテックリードの櫻井です。
今回は2022年9月に行ったオンプレミスからAWSへの移行プロジェクトについて紹介したいと思います。
オンプレ環境の抱えていた課題
弊社の主力サービスである prtimes.jp はAWSなどのクラウドサービスではなく、自社サーバーをデータセンターに置くオンプレミスで運用してきました。
ほとんどのサーバーはVMware vShereを使って仮想サーバーとして構築されていましたが、データベース(PostgreSQL)だけは物理サーバーとして構築されていました。
このデータベースには750GBほどのストレージがありましたがそのうち90%近いデータが保存されており、なおかつ物理的にこれ以上ストレージを増設することができなかったため、データベースのストレージの枯渇を防ぐために早急に別のサーバーに移行する必要がありました。
またデータベースサーバーのCPU使用率も高い状態が続いており、フェイルオーバーが頻繁に発生しているという状態でした。
以前行ったストレージの空き容量を増やすための対応についても記事を書いているので、良ければこちらも参照ください。

移行対象のサーバーについて
今回早急に移行が必要だったのはDBサーバーのみでしたが、DBサーバーだけをAWSに移行してWebサーバーがオンプレにある状態だとWeb-DB間の通信がインターネット越しになってしまい大幅なパフォーマンス劣化が予想されます。
当時オンプレ環境とAWS環境の間にはDirect Connectを使った専用線もありましたが、帯域幅があまり大きくなくこちらを使った場合も大幅にパフォーマンスが劣化してしまうことが予想されました。
そのためWebサーバーなども含めた以下のサーバーを全てまとめてAWSに移行することを決めました。
- Webサーバー, Batchサーバー → EC2
- DBサーバー → RDS for PostgreSQL
- ロードバランサー → Application Load Balancer
- セッションサーバー → ElastiCache for Redis
- 共有ストレージ → FSx for NetApp ONTAP
移行前のオンプレ環境のアーキテクチャ図と移行後のAWS環境のアーキテクチャ図は以下のようになります。


PostgreSQLサーバーについて
PostgreSQLサーバーを移行するにあたり以下の2点の課題がありました。
- 当時使用していたPostgreSQL9.6がRDSのサポート対象外だった
- バックアップファイルから復元しようとすると数時間かかる
これらの課題を解決するためにAWS DMSを使ったロジカルレプリケーションを構築しました。
ロジカルレプリケーションではメジャーバージョンをまたいでレプリケーションを行うことが可能なので、AWSへの移行とバージョンアップを同時に行うことができます。
移行とバージョンアップを同時に行うとリスクも大きくなるのでスケジュールに余裕があれば別々に行うべきですが、今回は前述のようにストレージの枯渇やサーバーの負荷などもあり移行とバージョンアップを同時に行うことにしました。
予めレプリケーションをしておけば移行時はDBの向き先を切り替えるだけで良いのでメンテナンス時間もかなり短縮することができました。
レプリケーションを構築する際はDMSの機能を使ってテーブル等のスキーマを作成することもできますが、カラムのDEFAULT値やNOT NULL属性やシーケンスなどが同期されないため以下のようにpg_dumpとpg_restoreを実行してスキーマやシーケンスの値をコピーしておくのがおすすめです。
(今回PostgreSQLのOIDを使用していたので --oids
オプションをつけていますが、通常は不要です)
# データベースのスキーマのみを移行する
$ pg_dump --schema-only --oids -Fc -U ユーザー名 -d データベース名 -h 移行元ホスト名 > schema.dump
$ pg_restore --schema-only -c -U ユーザー名 -d データベース名 -h 移行先ホスト名 schema.dump
# シーケンスの値を移行する
$ pg_dump --data-only -t *_seq -Fc -U ユーザー名 -d データベース名 -h 移行元ホスト名 > sequence.dump
$ pg_restore --data-only -U ユーザー名 -d データベース名 -h 移行先ホスト名 sequence.dump
また、ロジカルレプリケーションを構築した後にレプリケーションに使っているDMSインスタンスやレプリケーション先のRDSを停止すると、レプリケーション元のデータベースのWALを削除することができずストレージを圧迫してしまう可能性があるので注意が必要です。
共有ストレージについて
画像などを保存している共有ストレージもDBと同様にデータ移行を行う必要がありました。
共有ストレージは移行前のタイミングで約12TB、ファイル数で約6000万ファイルものサイズがありました。
なるべくメンテナンスの時間は短くしたいので、データ移行の方法としては以下の方法を検討しました。
- rsync
- DataSync (AWS)
- SnapMirror (NetApp)
- xcp (NetApp)
rsyncコマンドは比較的使い慣れていて便利なんですが、あまり速くないため今回のケースだとメンテナンス時間内に終わらせるのが難しそうということで除外しました。
AWSの提供するDataSyncについても調べたところrsyncと同じくらいの速度という情報があったので同様に除外しました。
ストレージのベンダーであるNetApp社の提供するSnapMirrorは速度はrsyncよりずっと速いということで期待していたのですが、SnapMirrorを使うために必要な料金が非常に高く除外となりました。
同様にNetApp社の提供するxcpは調べてもあまり情報が出てこなかったのですが、rsyncよりも圧倒的に速いという情報があったので今回はxcpを採用することにしました。
参考 : https://qiita.com/REALKTMR/items/a4ab7f75aa11b1dbd939
しかし実際に移行してみたところ、xcpの差分同期に大量に抜け漏れがあったことが判明しました。
急遽rsyncを並列で実行してなんとか抜け漏れの分も同期することができましたが、振り返ると最初からrsyncを使っておけば良かったなぁと思いました。
移行時につまづいたこと
移行を行うにあたっていくつものトラブルはありましたが、その中でも印象的だったものを紹介します。
PostgreSQLのOIDが使えなくなっていた
前述の通りAWSへの移行と同時にPostgreSQLのバージョンアップを行いましたが、バージョンアップによる非互換がありました。
具体的にはバージョン9からバージョン13までアップデートしようとしたところ、ユーザー登録などの一部の処理が動かなくなりました。
原因を調査したところ、バージョン12からOIDが廃止されたことが原因だと分かりました。
OIDとはテーブル作成時に WITH OIDS
のように指定することで使えるようになる隠しID列のようなものです。
このOIDを使って例えば以下のように直前にINSERTしたレコードを取得していました。
$oid = pg_last_oid($result);
$sql = "SELECT * FROM users WHERE oid = $oid";
このようにOIDを使用している部分はINSERT時にRETURNING句を使うことで代替することができます。
$sql = "INSERT INTO users(name) VALUES ('櫻井') RETURNING id";
参考 : https://www.postgresql.jp/document/14/html/dml-returning.html
移行当時は全てのコードをRETURNING句に書き換える時間がなかったためバージョン13ではなくバージョン11に移行することにしました。
現在はOIDを使用していた箇所は全てRETURNING句への書き換えが完了しています。
テスト用に作成したCloudFrontの一部で502エラーが返されていた
移行先の環境で動作確認を行うためにテスト用のCloudFrontを作成しました。
しかしALBからの接続はできるのになぜかCloudFrontからアクセスすると502エラーが返されていました。
ALBのCloudWatchを見るとTLSネゴシエーションエラーが発生していたため調べながら色々試したところ、CloudFrontのキャッシュキーにHostヘッダーを加えることで直ることが分かりました。
原因についてはCloudFrontとALB間の通信にHostヘッダーが含まれなかったため、ALBに設定しているSSL/TLS証明書のドメイン名とリクエストのドメイン名が一致せず、エラーになってしまったのかなと推測しています。
実際にAWSのドキュメントを読むと証明書のドメインが”オリジンドメイン名に指定した値”または”Hostヘッダーの値”と一致する必要があると書いてあり、このときオリジンドメイン名はALBのドメイン名(xxxxxx.ap-northeast-1.elb.amazonaws.com
)を使用していました。
参考 : https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/http-502-bad-gateway.html#ssl-negotitation-failure
移行してみて感じたこと
データベースが爆速になった
今回PostgreSQLデータベースをオンプレからRDSに移行してみて、明らかに速くなりました。
例えば以下のグラフはNewRelicから取得したプレスリリーステーブルに対する平均QueryTimeですが、移行を行った9月19日までは30ms前後かかっていましたが移行後はずっと5ms以下で安定しています。

また移行前は頻繁にDBのフェイルオーバーが発生していると書きましたが、移行後はCPU使用率もかなり低い値で推移しておりフェイルオーバーも一度も発生していないため、RDSのスペックの高さを改めて実感しています。
バックアップの取得が爆速になった
移行するまではデータベースのバックアップを取得するためにpg_dumpを使用しており1~2時間ほどかかっていましたが、RDSのスナップショット機能を使うことで10分以内にバックアップが取れるようになりました。
また、共有ストレージのバックアップはサイズが大きすぎて今まで取れていませんでしたが、FSxの機能を使って10分以内にバックアップが取れるようになりました。
このバックアップはリストアも高速にできるため、本番同様のデータが入ったDBを構築して検証するなどもすぐにできるようになりました。
IAM管理が楽になった
今回WebサーバーやBatchサーバーをEC2に移行したため、IAMロールを直接サーバーにアタッチできるようになりました。
これによりIAMユーザーのアクセスキーを作成してサーバー上に保存するよりも安全かつ楽に権限管理ができるようになりました。
踏み台サーバーが不要になった
今回の移行に伴って踏み台サーバーを廃止し、AWS Session Managerを使ってサーバーに接続するようにしました。
これにより踏み台サーバーの管理やユーザーごとに鍵を登録するのが不要になりました。
AWS Session Managerについては以前記事を書いたのでこちらを参照ください。

まとめ
今回は先日行ったAWS移行について紹介しました。
今までに経験したことがないほど大規模なプロジェクトでリーダーを務めることになり、うまくいかないことだらけでしたがメンバーの協力もありなんとか予定通りに移行できて安心しました。
まだまだ他にもやるべき課題は山積みですが、引き続き頑張っていきたいと思います。