Medley Developer Blog

株式会社メドレーのエンジニア・デザイナーによるブログです

サービスのダウンタイムなくRDSをAuroraに移行した話

こんにちは、人材プラットフォーム本部 プロダクト開発室 第一開発グループ所属の森川です。医療介護求人サイト「ジョブメドレー」の開発チームの一員として、現在はインフラタスクをメインに取り組んでいます。

今年(2021年)の6月の話ではありますが、ジョブメドレーの連携サービスにて使用しているデータベースを、通常のRDS(Amazon RDS for MySQL)からAurora(Amazon Aurora)に移行しました。

タイトルは自分でも大きく出たなと思ってはいるのですが、サービスの特性に助けられたところはありつつも、ダウンタイムなく移行することができました。今回はそのいきさつについてお話させていただこうと思います。

本稿ではAmazon RDS for MySQLを「通常のRDS」、Amazon Auroraを「Aurora」と表記させていただきます。

ジョブメドレーの連携サービスについて

ジョブメドレーは医療介護従事経験者が運営する就職・復職・転職のための求人サイトです。このジョブメドレーの連携サービスとして医療・介護・福祉・歯科従事者向けの情報サービスを他社と共同運営しています(以降、本サービス)。

本サービスはアプリケーションシステムのバックエンドの開発フレームワークとしてRuby on Rails(以降、Rails)を使用し、弊社で開発・運用を行っています。表示コンテンツは他社から共有していただくものを表示する形となっています。

発端

今年の1月末にAWSから以下のようなメールが届きました(内容を一部抜粋しています)。

Amazon RDS は、MySQL メジャーバージョン 5.6 の廃止 (EOL) プロセスを開始しています。

お客様は古いバージョンで実行されている RDS MySQL インスタンスを1つ以上お持ちであるように見受けられます。

Amazon RDS for MySQL 5.6 は、UTC 協定世界時間の2021年8月3日に廃止されます。

公式のお知らせはこちらです。

EOL通知はドキッとします。できれば見たくないものです。

この対象となっていたのが、本サービスで使用しているRDSのMySQL v5.6.39(当時)でした。

対応方針として以下を考えました。

  • 通常のRDSのままMySQLのバージョンを5.7に上げる
  • 通常のRDSからAuroraへの変更も行いつつ、MySQLのバージョンを5.7に上げる
  • MySQLのバージョンを8まで上げる

MySQL 8系へのバージョンアップは対応範囲が大きくなりすぎるため今回は断念しました。5.6のEOL期限が迫っているため時間の余裕はあまりありません。また、もし8系に上げるとしても、5.7へのバージョンアップは挟む必要がありそうです。

ジョブメドレー本体のシステムにおいても、性能やメンテナンス性の向上の観点から、通常のRDSからAuroraへの移行を計画しております。その足がかりとして、関連アプリケーションの中でも比較的システム規模の小さいものから、先行して移行作業をしておきたいという要望がありました。

これらを加味して、今回のミッションは「通常のRDSからAuroraへの変更も行いつつ、MySQLのバージョンを5.7に上げる」となりました。

検証

インフラ構成に変更を加える際には言うまでもなく検証が必要です。インフラ関連タスクに取り組むにあたって大きなウェイトを占めるのが検討・検証なのではないかと個人的には考えています。

Auroraへの乗り換え検証のために、新たにDBインスタンスを用意しました。既存相当のスペックの通常RDSとそれよりも少しスペックの落としたAuroraです。正確な比較を行う場合であれば、両DBインスタンスのスペックは揃えるべきかとは思いますが、現行のRDSがオーバースペックであることも課題のひとつであったため、検証段階からAuroraのスペックを落として検証を行っています。よって検証結果による成否判断としては「著しい悪化が見られないか」という観点になっています。

既存相当のRDSのスペックはdb.m4.large 、Auroraのスペックは暫定的にdb.t3.mediumを選択しました。また、データベースに流し込むデータは本番相当のものを個人情報などはマスクした上で用意しています。

検証の項目は以下の4点です。

  • レスポンスタイム検証
  • SQLごとの速度検証
  • 実行計画の検証
  • フェイルオーバー後に元writerに対する接続が継続される問題への対応検証

レスポンスタイム検証

まずは、レスポンスタイムの影響について確認しました。

本サービスにおける主要なページのURLを選出します。それぞれのページに対して数回のリクエストを行い「低負荷状態」でのレスポンスタイムを比較しました。Auroraの方が一桁ミリ秒程度遅いという結果となりました。

次にApache Benchを用いて短時間に並列でのリクエストを行い「高負荷状態」を再現して比較を行いました。本サービスはスパイクで負荷が高まるような性質ではないため、普段のアクセス頻度を元に「高負荷状態」がどの程度かを想定して定義しています。結果として、一部のページにて1秒ほど遅くなっていることが分かりました。スペックを上げることで解決ができるのかを調査するため、インスタンスサイズをdb.t3.mediumからdb.t3.largeに変更して改めて試しました。しかしAuroraの方が遅いという結果に変化は見られませんでした。アプリケーション側の調査を進めると、データベースの問題ではなくアプリケーション側の問題であることが分かり、別途対応ということになりました。

以上より、Auroraへの移行によるレスポンスタイムへの影響としては、多少の性能の低下は見られたものの許容範囲内であり、大きな問題は見られないことが分かりました。

SQLごとの速度検証

こちらはレスポンスタイムの検証に近しい検証ではありますが、生のSQLの速度の調査も行いました。アプリケーションを通して実行されるSQLの中でも実行される頻度の高いものを選出し、アプリケーションを介さずクライアントツールから実行しています。

こちらも大きな問題は見られませんでした。

実行計画の検証

この検証でもアプリケーションの中で実行される頻度が高いクエリを選出し調査を行いました。使用したのはお馴染みのEXPLAINステートメントです。

実行計画に差分は見られなかったため問題はありませんでした。

フェイルオーバー後に元writerに対する接続が継続される問題への対応検証

RailsアプリケーションへのAurora導入において、必ずと言ってよいほど障壁となるのがこの問題かと思います。

AuroraのフェイルオーバーとRailsへの影響

Auroraの特徴や通常のRDSとの差分については様々なサイト・記事で詳しくまとまっている情報ですので本稿での詳解は割愛しますが、Auroraのフェイルオーバーについてのみ概要を説明させていただきます。

マルチAZでのAurora DBクラスターのプライマリDBインスタンス(writerと呼ばれる)において障害が発生した場合、フェイルオーバー(待機システムへの切り替え)が自動で実行されます。プライマリDBインスタンスのリードレプリカ(readerと呼ばれる)を配置していた場合は、それが次のプライマリへと昇格し、エンドポイントも切り替わってくれます。

次にRailsActiveRecordのコネクションプールについてです。ActiveRecordはデータベースへの接続情報を保持し、そのコネクションを再利用することで接続時のオーバーヘッドを解消し、高速化を図る仕組みを保有しています。本サービスにおいてMySQLのクライアントとして使用しているmysql2では、コネクションがアクティブであることをmysql_pingが判定してくれるようです。しかし残念なことにAurora側でのフェイルオーバーの発生までは検知することができないようです。

したがって、フェイルオーバーが発生すると、プライマリから降格した元writer現readerであるDBインスタンスに対してコネクションが張り続けられてしまうため、書き込みのリクエストに対してはエラーとなってしまう状態になります。

f:id:medley_inc:20211105182842p:plain

対応策

対応策としては以下を考えました。

  1. Aurora用のクライアントGemを使用する
  2. Mysql2::Errorが発生した場合、サーバーエラーにしつつコネクションを切断して再接続を促す
  3. 一定回数リクエストを処理するごとに再接続させる

今回採用したのは上記の3の「一定回数リクエストを処理するごとに再接続させる」対応です。

当初は対応策1のGemの使用を検討しておりましたが、トランザクション中にフェイルオーバーが発生した際の挙動が本サービスには適合しないことが分かりました。またいくつかの対応策を複合して実装するという方法も考えられました。

今回においては、実装工数や、サービスの規模から考えうる必要十分の最小値を検討した結果、この判断としています。

再接続の度に発生するオーバーヘッドによる速度影響についても低負荷・高負荷の状態で検証を行いましたが、数値的には軽微であり、少々の遅延は許容することになりました。

この対応は、Auroraへの移行の前、つまり通常のRDSで稼働している状態であってもリリースして問題ないものだったため、事前に実装・リリースを行うことができました。

料金

「現行のRDSがオーバースペックであることも課題のひとつ」と上記しておりましたが、その是正による料金の合理化についても副次的効果として期待していました。

Auroraの料金形態には、通常のRDSと同じ「DBインスタンス時間従量課金」「ストレージ時間従量課金」に加え、「I/Oリクエスト数従量課金」という項目が追加されているため、比較を行う際は注意が必要です。

移行前後の条件は以下の通りです。

  • 通常のRDS(移行前)
    • オンデマンド
    • RDSで稼働しているのは本番環境のみ
    • db.m4.large
    • ストレージ 100GB
  • Aurora(移行後)
    • オンデマンド
    • 本番環境だけでなく検証環境もAuroraで稼働させる
    • 本番環境 db.t3.medium, 検証環境 db.t3.small
    • ストレージ 100GB(両環境とも)

移行前の通常のRDSでは本番環境のみで月々4万円以上かかっていたコストですが、移行後は本番環境に加え検証環境においてもAuroraを使用するようにしても合計3万円以下という計算となりました。月々1万円、年額12万円の節約になります。さらにはオンデマンドからリザーブドへ変更することも今後検討ができるため、コストカットの余地がまだ残されているのも嬉しいところです。

リリースに向けた準備

リリース作業のキモとなるデータベースの切り替えは、「リードレプリカからの昇格」という方法を採ることとしました。MySQLのバージョンアップについては、プライマリが通常のRDSからAuroraに切り替わった後に、AuroraインスタンスMySQLバージョンを上げるための変更を実行します。

これらの作業をデータの不整合を発生させることなく完遂させるためには、データベースへの書き込み動作をすべて停止させる必要がありました。

これを実現するためには、データベースへの書き込み処理が発生するケースをすべて洗い出さなければいけません。調査をしたところ、本サービスでは「社内管理画面」と「バッチ処理」でのみ書き込み処理が実行されていることが分かりました。

つまり、一般ユーザからの書き込み処理は本サービスでは保有していないため、書き込みが行われるタイミングを把握・調整することが可能でした。したがって、一般ユーザが見ることのできるページをメンテナンスモードに切り替えるなどの「サービスのダウンタイム」を発生させることなくAuroraへの移行とMySQLのバージョンアップを行える、ということになります。

リリース時に影響が生じる周辺情報の洗い出しも一通り済んだところで、次に「手順書」の作成に取り掛かりました。必要な全ての操作について順序に沿って詳細にまとめます。その中には、各段階にて万が一障害が発生した場合の切り戻し手順についても記載しています。

手順書を元に、検証環境にて同様の手順を実行し、予行演習を行いました。これによって、本番での作業を鮮明にイメージすることができます。また大抵の場合、手順書の不備やブラッシュアップできるところが見つかります。予行演習は本番作業を滞りなく実行するための最も重要なファクターのひとつかと思います。

f:id:medley_inc:20211105182450p:plain
実際に使用した手順書(一部抜粋)

リリース作業

リリースで行った作業の要点をまとめると以下となります。

  • 本番用データベースのリードレプリカをAuroraで作成
  • データベースへの書き込みを停止
    • 社内管理画面の操作を停止いただくよう社内へのアナウンス
    • バッチ処理の実行をすべて停止
  • レプリカAuroraをプライマリへ昇格
  • データベースのコネクション更新のためサーバ1台ずつアプリケーションの再起動
  • バッチ処理の再開
  • 動作確認

大きめのリリース作業はアクセスの多い時間帯を避けて行われることもあるかと思いますが、今回はサービスのダウンタイムなく作業を行えるケースであるため午前中からの作業開始となりました。また作業を行う自分の隣には先輩社員が構えており、ダブルチェックをしていだきました。

手順書の作成と予行演習の甲斐もあって、本番リリースを問題なく進めることができました。

まとめ

サービスの特性に助けられたところは大きいですが、サービス停止などのダウンタイムを発生させることなく通常のRDSからAuroraへの移行を完了させることができました。

スティーブ・ジョブズは数分のプレゼン発表のために数百時間の準備をしていたといいます。プレゼンで言うところのシナリオの作成とリハーサルの実行もそうですが、サービス特性の理解や検証を綿密に行うことも本番を成功させるためには欠かせません。準備を怠らないエンジニアでありたい、と執筆しながら改めて感じているところです。

さいごに

メドレーでは「医療ヘルスケアの未来をつくる」というミッション実現のため、日々開発・運営を行っています。エンジニア・デザイナーをはじめ多くのポジションでメンバーを募集しております。もし少しでもご興味をお持ちいただけましたら、ぜひお気軽にお話しさせていただければと思います。

最後までお読みいただき、ありがとうございました。

www.medley.jp