株式会社メドレーDeveloper Portal

2021-11-08

サービスのダウンタイムなく 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 と呼ばれる)を配置していた場合は、それが次のプライマリへと昇格し、エンドポイントも切り替わってくれます。

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

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

20211105182842.png

対応策

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

  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 のバージョンアップを行える、ということになります。

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

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

20211105182450.png
実際に使用した手順書(一部抜粋)

リリース作業

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

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

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

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

まとめ

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

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

さいごに

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

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

https://www.medley.jp/jobs/

株式会社メドレーDeveloper Portal

© 2016 MEDLEY, INC.