コネヒト開発者ブログ

コネヒト開発者ブログ

ママリで使用している Aurora MySQL のアップグレード(v2 から v3)を行った EOL 対応プロジェクトの進め方を振り返る

こんにちは。コネヒトでは Aurora EOL 対応芸人となっているささしゅう(@sasashuuu)です。本日は、コネヒトを代表するサービス「ママリ」のデータベースを Aurora MySQL v2 から v3 へアップグレードした際のプロジェクトの全容をお伝えしたいと思います。

はじめに

まず、ママリというサービスと Aurora の構成について触れておきます。 ママリは、子育て Q&A アプリおよび情報サイトを通じてママ向けのサービスを提供しています。詳細や各種サービスの利用については、弊社ホームページをご覧ください。

connehito.com

現在のママリの主要な AP サーバーとデータベースの構成は以下の通りです(※具体的なサーバーの台数については図では省略)。

AP サーバー構成

用途 利用サービス サーバー台数
Q&A アプリ用 AP サーバー(Native アプリ API 向け) ECS Fargate 4 ~ 30タスク
(サービスは1つ)
Q&A アプリ用 AP サーバー(Web アプリ向け) ECS Fargate 2 ~ 4タスク
(サービスは1つ)
情報サイト用 AP サーバー(Web アプリ向け) ECS Fargate 4 ~ 20タスク
(サービスは1つ)

データベース構成

用途 利用サービス サーバー台数
ママリ共用 RDB Aurora MySQL v3 2インスタンス
(リーダー・ライターそれぞれ db.r6g.4xlarge)

※Aurora に関してはレプリカの Auto Scaling により必要時にスケールイン/アウトの設定あり。

※アップグレード前は db.r4.4xlarge が 3 台の構成(アップグレードの機会に見直しを実施)。

プロジェクトの進め方の全体像

プロジェクトの進め方は以下のフローで行いました。本記事では1つ1つのトピックの詳細な解説は省略しますが、どういったフローでアップグレードに向けた作業をおこなっていたのかをざっくりとイメージしていただけると幸いです。

プロジェクトの構成

プロジェクトのメンバーは基本的にインフラエンジニア1名とアプリケーションエンジニア(サーバーサイド)1名の体制で臨みました。作業が増えるフェーズではエンジニアを増員し(最大5名程度)、手分けして作業を行いました。主に CI・Dev 環境でのテストや動作確認、不具合修正対応のフェーズで特に人手が必要でした。アサインと業務分担については、インフラエンジニアとアプリケーションエンジニアの作業領域を分けつつ、必要に応じて互いに協力し合いました。下記は先ほどのフローに担当領域を示した図です。青色がインフラエンジニア、赤色がアプリケーションエンジニア、共に重なっている箇所は両方が担当しました。

良かった点

EOL 対応を進める上で特に良かった点をピックアップしていきます。

MySQL Shell アップグレードチェッカユーティリティの活用

MySQL Shell の アップグレードチェッカユーティリティ という機能があります。これは MySQL5.7 から 8.0 へのアップグレードに伴う互換性に関するエラーや問題を検知してくれるツールです。Aurora というマネージドサービスを使用している都合上一部正常にチェックできない項目もありますが、基本的な互換性はありますので十分に活用できる印象です。以下は今回アップグレード対象だった Aurora v2(MySQL5.7) へ実行した結果の例を一部記載したものです(※一部マスク処理あり)。

接続

mysqlsh -u xxx -h xxx -P xxx -p --ssl-ca=xxx

アップグレードチェッカーの実行

 MySQL  xxx:xxx ssl  JS > util.checkForServerUpgrade()
The MySQL server at xxx:xxx, version 5.7.12-log - MySQL Community
Server (GPL), will now be checked for compatibility issues for upgrade to MySQL
8.0.32...

中略

1) Usage of db objects with names conflicting with new reserved keywords
  Warning: The following objects have names that conflict with new reserved
    keywords. Ensure queries sent by your applications use `quotes` when
    referring to them or they will result in errors.
  More information:
    https://dev.mysql.com/doc/refman/en/keywords.html

  xxx.xxx.rank - Column name
  xxx.xxx.rank - Column name
  xxx.xxx.rank - Column name

4) Usage of utf8mb3 charset
  Warning: The following objects use the utf8mb3 character set. It is
    recommended to convert them to use utf8mb4 instead, for improved Unicode
    support.
  More information:
    https://dev.mysql.com/doc/refman/8.0/en/charset-unicode-utf8mb3.html

  xxx - schema's default character set: utf8
  xxx.xxx.xxx - column's default character set: utf8
  xxx.xxx.xxx - column's default character set: utf8
  xxx.xxx.xxx - column's default character set: utf8

以下略

上記の例では、MySQL8.0 で予約キーワードとなる rank カラムを使用している箇所や、MySQL8.0 で非推奨となる utf8mb3 を使用している箇所などが検知されました。

CI の活用

ママリの開発では GitHub Actions による CI/CD を構築しています。このワークフロー上では PHPUnit による自動テストが実行され、アプリケーションのデプロイ前に問題があればワークフローで異常を検知できます。Dev や Prd 環境のアップグレード前に、このワークフロー上で使用している MySQL のイメージを MySQL5.7 から 8.0 へ変更しテストすることによって、アップグレードにおける不具合を効率よく検知することができました。以下はワークフローの設定ファイルの例です(※一部マスク処理あり)。

name: Pull request
on:
  pull_request
env:
  AWS_ROLE_ARN: xxx
permissions:
  id-token: write
  contents: read
jobs:
  php:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8.0 # 5.7から変更

中略

      - name: Run phpunit
        env:
          xxx: xxx
          xxx: xxx
          xxx: xxx
          xxx: xxx
        run: vendor/composer/bin/phpunit -d memory_limit=512M

以下略

CLI ベースでの手順構築

Aurora のアップグレード方法には様々な手法がありますが、弊社では AWS CLI を使用したオペレーションを採用しました。CLI ベースだと移植性が高く、順序立てた手順の構築がしやすいというメリットが大きいと感じたためです。個人的な見解になりますが、各アップグレード方法の内容について比較したものは以下となります。

アップグレード方法の比較

メリット デメリット
コンソール GUI による直感的な操作が可能 移植性が低い(リソースのリンクやキャプチャを手順に埋め込んでいる場合、他のデータベースのアップグレード作業でも使いまわしづらい)
AWS CLI ・移植性が高い(他のデータベースのアップグレード作業でもパラメータを変えて使いまわしやすい)
・IaC に比べ順序立てた手順が作りやすい
コマンド実行時のコピペミス等によるリスクがある
IaC ・(コードの)バージョン管理やレビューのしやすさがある
・fix したコードを使用するためオペミス等のリスクが減る
順序立てた手順を実行する観点で扱いが難しい(例として段階的なバージョンアップグレードが必要であったり、アップグレードとインスタンスクラスの変更を同時に行えない問題がある)

CLI を用いたアップグレードについて実際の使用例を一部ご紹介します。弊社の場合、Notion であらかじめ手順書を作成し、作業時にコマンドをコピーアンドペーストで実行していくという方法を取っていました。

RDS Blue/Green Deployments の利用

今回のアップグレードでは Amazon RDS Blue/Green Deployments を利用しました。Green 環境の構築および Blue 環境とのレプリケーションからスイッチオーバーによる切り替えを提供してくれる機能です。以下の手順でアップグレードを行いました。

  1. Green 環境の作成
  2. Green 環境のアップグレード
  3. Green 環境への DDL 実行
  4. スイッチオーバー

良かった点は、事前に必要な作業を Green 環境で済ませておくことによって、サービスの計画停止の時間を短縮できることでした。特に今回はエンジンのアップグレードに加え、アップグレードに伴う DDL の実行(※後述)も必要であったこともあり、計画停止の前段階でネックとなる作業を終わらせることができました。ただし、Green 環境への DDL 実行は基本的に推奨されていない(サポートの方へ確認済み)ことから、実施する場合はリスクを承知の上で行う必要があります。

チームや専門領域を跨いだエンジニア間の定期的なコミュニケーション

1〜2週間に1回程度、インフラエンジニア・アプリケーションエンジニアが同期を取り連携していました。互いの進捗や状況を共有することで、発生している問題や課題が把握できたので、プロジェクト全体で見た際の人的リソース調整などが上手く行えていたように感じます。また、インフラエンジニアのみでは把握しづらい開発組織全体への影響を考慮した舵取りをよりしやすくなったのも良いポイントだったと思います。

プロジェクト内で共通のカンバンを使用する

所属チームや取り扱うサービスにより issue やタスクカード等の置き場所が異なる弊社ですが、基本的に EOL 対応のプロジェクトでは共通のものを使用するようにしました。 プロジェクト全体の状況が一元的に管理しやすくなるのでオススメです。

弊社の場合、Notion のデータベース等を活用していました。 下記は実際に使用していたものの一例です。

タスク管理
発生した問題等の管理

反省点・課題

続いて EOL 対応を進める上での反省点や課題をピックアップしていきます。

通常のアプリケーション開発業務との並行運用

Dev 環境アップグレードから Prd 環境アップグレードまでにバージョンの乖離期間があり、その期間に通常のアプリケーション開発に支障をきたすことがありました。Prd 環境がまだ MySQL5.7 であるもCIの環境を 8.0 に変えたことにより、CI をパスしたとしても Prd 環境での動作の担保ができない状態になっていました。Prd リリース前にシステムの異変を検知するという CI の目的を踏まえると、Prd 環境と同じ 5.7 で CI が通れば Prd リリースして良いことになります。ただタイミング悪く乖離期間にリリースが重なってしまったものがありました。5.7 ではなく 8.0 を使用した CI でテストを通した結果、テストが落ちてしまうということがありました。これに関しては旧バージョンと新バージョンの CI を構築し、どちらでもテストおよびリリースが可能な状態にする対策を取りました。また、次回以降の EOL 対応では対象データベースに関わる開発チームから1名以上をアサインしてもらい、EOL 対応と通常のアプリケーション開発業務をよりうまく調整しながら進めていけるようにする対策を考えました。

計画停止作業の影響範囲の認知負荷

今回のアップグレード作業では、夜間に計画停止を行いました。影響範囲が広く、認知負荷も高い状態でした。影響のあるサービスにはメンテナンス画面を出したり、監視システムを無効にしたりする対応が必要で、ステークホルダーに対して利用停止を告知・依頼する手配も必要でした。この点においては今後認知負荷を軽減していけるように情報を集約していく必要があると感じました。

バイナリログレプリケーションを使用していた場合の Amazon RDS Blue/Green Deployments の利用

ママリのデータベースは to C 向けのサービスでの利用がメインですが、分析用に RDS for MySQL へのバイナリログレプリケーションも行っていました。

Amazon RDS Blue/Green Deployments では仕様上バイナリログが引き継がれないという問題がありました。下記のように Blue/Green 環境のスイッチオーバー後は、Blue 環境で使用していたバイナリログは引き継がれないため、スイッチオーバー後はそのままレプリケーションの再開が行えないというものです。

スイッチオーバー前の状態

スイッチオーバー後の状態

そのため以下の手順でオペレーションを行う必要がありました。

  1. スイッチオーバー前に Blue 環境で目印となるクエリを実行
  2. Blue 環境と分析 DB のレプリケーションを停止
  3. Blue/Green 環境のスイッチオーバー
  4. Green 環境にて目印のクエリが含まれたログファイルを dump
  5. 目印のクエリのファイル名・ポジションをもとに Green 環境と分析 DB のレプリケーションを再開

このように、RDS for MySQL とのバイナリログレプリケーションを構築している場合、特殊なオペレーションが発生するため注意が必要です。

想定外のスロークエリの発生

SELECT COUNT(*) のクエリが遅くなる事例はいくつかあるため事前にチューニングを行っていましたが、Prd アップグレード後に別種のスロークエリが発生しました。以下のような構文のクエリでした(※一部マスク処理あり)。

SELECT
    `xxx`. `xxx`,
    `xxx`. `xxx`,
    `xxx`. `xxx`,
    ...中略
FROM
    `xxx`.`xxx` AS `xxx`
        LEFT JOIN
    `xxx`.`xxx` AS `xxx` ON (`xxx`.`xxx` = `xxx`.`xxx`)
WHERE
    `xxx`.`xxx` >= xxx
        AND (NOT (`xxx`.`xxx` = xxx)
        AND NOT (`xxx`.`xxx` = (xxx)))
        AND `xxx`.`xxx` = xxx
        AND `xxx`.`xxx` = 'xxx'
        AND `xxx`.`is_hoge` = '0'
ORDER BY `xxx`.`xxx` DESC
LIMIT xxx;

is_hoge(仮名) という boolean のカラムを使用していましたが、比較演算子を等価から不等価に変更することでパフォーマンスが向上しました。

修正前

`xxx`.`is_hoge` = '0'

修正後

`xxx`.`is_hoge` != '1'

変更前のクエリは実行計画で Using temporary, Using filesort が発生していたのですが、比較演算子の変更によりこれらは解消されました。 なぜこのスロークエリが発生したのか、また等価比較の変更でなぜ Using temporary, Using filesort が解消され、パフォーマンスが向上したのかは不明です。 とはいえ事前検証をもう少し入念にしておけば Prd 環境のアップグレード後に慌てる必要はなかったため反省点でした。

非推奨文字セット・照合順序変更の DDL の対応コスト

MySQL8.0 から utf8mb3 が非推奨になります。デフォルトの文字セットが utf8mb4 になり、照合順序が utf8mb4_0900_as_ci となる仕様変更もあります。utf8mb3 を使用している箇所があったため、アップグレードに伴いデフォルトの文字セットおよび照合順序を変更する DDL を実行しました。DDL の実行には数時間(おおよそ3時間程度)かかりましたが、事前に Green 環境で実施したため、運用中のデータベースや計画停止作業に影響はありませんでした。ただし、事前対応の準備コスト(DDL 実行時間を見積もった上で逆算し、 Green 環境の構築と DDL 実行を行う等)があるため、場合によっては他の方法を検討する必要がありそうです。

おわりに

色々と大変なことはありましたが、Aurora MySQL v2 の EOL 対応を無事に終えることができました。個人的には弊社の場合だと v1 から v2 へのアップグレードよりもネックとなるポイントが多く、難易度が高かったように感じました。今回はコネヒトにおける事例を紹介しましたが、これから v2 EOL 対応を控える方々の参考になれば幸いです。頑張ってください!それではさようなら!