👏

約1年かけて Aurora の MySQL バージョンを 5.7 → 8.0 に上げたが、やっぱり問題はおきた

2024/12/01に公開
2

こんにちは。2024年もいよいよアドベントカレンダーの季節がやってきましたね!

今年頑張ったことの1つに、保守運用しているシステムのDBのバージョンアップがあったので、そのことについて記事にしようと思います。

タイトルにある通り、Aurora の MySQL のバージョンを 5.7 から 8.0に計画や検証も含めて、1年ほどかかって、なんとか無事にやり切ることができました。私の知識不足や、他の業務の忙しさにかまけてこれだけ時間がかかってしまったのですが、終わった今振り返ると、作業内容としては、特別むずかしいことはなかったなと思い次回はもっと早くできるように反省している次第です。

とはいえ、大きなトラブルもなくやり終えたので、上司から新聞の見出しを切り抜いて作った脅迫メッセージ風のお祝いメッセージを Slack でいただけて、感謝感激です。

この記事ではどんな風に作業を進めて来たのか、また途中のトラブルシューティングなどに関しても記載して、これからバージョンアップさせていく人の参考になれば幸いです。

動機、なぜバージョンアップしたいのか

MySQL 5.7 (Aurora 2) のEOL

まず1つ目に、シンプルに 5.7 に対応する Aurora が標準サポートの期限切れになるという事情があったためです。

https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.MySQL57.EOL.html

意外と高い延長サポート費用

もしバージョンアップをせず、MySQL 5.7 のまま使い続けると、自動的に延長サポート というものに加入?され、その費用が請求されてしまうことになり、その金額が決して安いものではなかったのも、バージョンアップを行う理由の1つでした。


延長サポート料金の詳細

1ドル 150円というざっくりしたレートで考えると、最低でも 年間で 15万円程度かかります。本番のワークロードを捌くためのDBであれば、それなりのインスタンスクラスを選択するかと思うので、その場合、コストがどんどん膨れ上がることがイメージできるかと思います (^_^;)

(0.1224365)*150=157,680

バージョンアップ前の構成

簡単な図ですが、ざっくりDBは以下のような構成で利用されていました。右側のAuroraのMySQLのバージョンを8系にあげるのが今回の目的です。

DBの利用用途としては当然Webアプリケーションのデータ管理ですが、Readは 非マネージドな HAProxy を経由して接続し、Write の操作は、直接クラスターエンドポイントに繋いでWebアプリケーションが動作するようになっていました。また、分析用途でDBのデータを quicksight にも取り込んでいました。このとき、分析には不要な電話番号やメールアドレスなどの個人情報の類をマスキングして隠したかったため、MaxScale も非マネージドのものをつかって、それ経由で quicksight と接続する構成となっていました。一部Webアプリケーションのオンライン処理でもそのマスキング機能を利用している状態でした。(quicksight を VPC の枠内にいれるのは厳密には間違いかも、、、、けど細かいことは気にしない)

Blue/Greenデプロイを利用してバージョンアップ

MySQL のバージョンを上げるためにはいくつか方法があると思いますが、社内で前例もあった Blue/Greenデプロイで対応することにしました。

その方法もコンソール画面をちょっとポチポチするだけでできてしまうし、なにか費用がかかるものでもないので、よほどの理由が無い限りはこれが一番楽でいい方法なんだろうなと思っています。(クラスターをBlue と Green で複製するので、一時的に Aurora の稼働コストが倍にはなってしまいますが)

また、現在バージョンの Blue と新しいクラスターの Green の2つの環境ができるので、本番稼働は維持しつつ、部分的に本番のワークロードを Green につなげてテストするといったこともできるので、バージョンアップする前に、新バージョンで問題が起きないか確認しながら進めるのもやりやすいです。

Blue/Greenデプロイメントの詳細:

https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/blue-green-deployments-overview.html

全体のタイムライン

対応完了までに、どんな作業が必要だったのか、時系列で羅列します。

タスク
2024/1 - バージョンアップ実施に向けて計画を開始
2024/2 - 検証環境で、Blue/Greenデプロイの検証開始
- Blue/Green の作成が失敗、AWSサポートの協力もお願いしながらデバッグ
2024/3 - AWSサポートの方が何かしらの対応をしてくれて、問題の1つと思われていたエラーが解消
- ただし、まだ Blue/Green デプロイの失敗が続く状況
- テーブルに含まれるコメントに無効な文字列が含まれていて、それがBlue/Green作成失敗の原因の可能性があったが、コメントを消しても効果なし
- 他の業務が優先となって、一旦ペンド
2024/6 - Blue/Greenデプロイの検証再開
- テーブルに関する問題を特定、不要なテーブルを削除することで対応
2024/10 - 本番環境でBlue/Greenデプロイの準備開始
- HAProxyの設定変更、一部のEC2で新しいMySQLクラスターに接続
- E2EテストでMySQL 8.xへの互換性を確認
- RDSプロキシの検証も実施するも、導入は先送りに
2024/11 - スイッチオーバーの具体的な手順を決定
- 実施日時を25日夜に設定
2024/12 - 延長サポートの課金開始前にバージョンアップを完了予定

Blue/Greenデプロイの検証(1月 ~ 6月)

Blue/Geenデプロイの機能自体は、世の中にも実施例がたくさんあるものなので、問題なく行えると思いますが、自分たちのAuroraクラスター で問題なく行えるかは別問題かなと思います。

そこで、本番Auroraクラスターの複製を用意し、それで Blue/Greenデプロイメントが問題なく行え、Webアプリケーションに接続しても問題が起きないか検証することにしました。

詳細な手順は割愛しますが、ざっくりと以下のようにやりました。

  1. 本番Auroraクラスターの手動スナップショットを作成
  2. 検証用AWSアカウントにそのスナップショットを共有
  3. 検証用AWSアカウントでスナップショットからAuroraクラスターを作成
  4. Blue/Greenデプロイメントの作成
  5. Greenのクラスターで動作確認
  6. Blue/Greenでスイッチオーバーを実施

スイッチオーバーまですんなり行けたら良かったのですが、Blue/Greenデプロイメントの作成で失敗してしまいました・・・

謎のエラーの発生とAWSサポートによる解決

以下のスクショのように、Invalid Configuration の状態が表示されていていました。

Blue/Greenデプロイの作成を行うと、upgrade-prechecks.log を作成して、MySQL のバージョンを上げても問題が起きないか自動でチェックしてくれて、そのときにエラーが発生すると、上記のスクショのような状態になるようです。

upgrade-prechecks.log について

https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.upgrade-prechecks.html

そのため、上記のupgrade-prechecks.logの内容を確認してみると、Summary の部分に以下のようにエラーの数と、それを直してからアップグレードしてねというメッセージが。

    "errorCount": 10,
    "warningCount": 10378,
    "noticeCount": 1,
    "Summary": "10 errors were found. Please correct these issues before upgrading to avoid compatibility issues."

エラーの詳細の部分を見に行くと、、、

        {
            "id": "mysqlEmptyDotTableSyntaxCheck",
            "title": "Check for deprecated '.<table>' syntax used in routines.",
            "status": "ERROR",
            "description": "Cannot load from mysql.event. The table is probably corrupted"
        }

The table is probably corrupted

とあって、何が問題なのか全くわからない・・・・。(直訳: テーブルは多分破損しているよ)

上記の1箇所だけでなく、エラーの殆どがこれでだったので、ネット上に同じようなケースが存在しないか確認して、なかったので、AWSサポートへ問合せしました。

サポートからは以下の返答があり、

The team has applied a standard operation procedure to fix the upgrade issues because of pre-check failure. and they are kindly requesting to retry the upgrade.

指示どおりに再実行すると corrupted のエラーが全て解消!Summary を見てもerrorCount が 0!だったので、Blue/Green の作成がうまく行くかなーと思いましたが、何故か失敗!Invalid Configuration!

エラーもゼロでそれでも失敗となり、途方にくれたので、再度AWSサポートに助けを求めると、テーブル定義のコメントに無効な文字が含まれていて、それが原因かもしれないとの連絡があり、コメントを削除して再実行してみるも、失敗の状態に変化無し。他の業務の優先度とかも上がってきたので、一旦ペンドすることに。

代替案として、インプレースアップグレードを試す

以下の記事などを見つけて、試しに検証用環境で複製したDBにインプレースアップグレードを試してみて、それでうまくいくなら、それでMySQLのバージョンアップを実施しようという方針に変更。

https://dev.classmethod.jp/articles/amazon-aurora-supports-in-place-upgrades-mysql-5-7-to-8-0/

しかし、エラーに。ただ、このときは今までには出てこなかったエラーが出てきました。

                {
            "id": "checkTableOutput",
            "title": "Issues reported by 'check table x for upgrade' command",
            "status": "OK",
            "detectedProblems": [
                {
                    "level": "Error",
                    "dbObject": "cslogs.user_log",
                    "description": "Can't find file: 'user_log' (errno: 2 - No such file or directory)"
                }
            ]
        },

user_log なるテーブルのファイルが見つからないという内容のエラーで、初めて見るテーブルだったので、なんだコレ?と思ってDBを確認すると確かに存在していました。

Webアプリケーションでは利用されていないテーブルだったので、存在を今まで知らなかったのですが、密かに存在していたようです。

> show tables;
+------------------+
| Tables_in_cslogs |
+------------------+
| batch_state      |
| user_log         |
+------------------+

その詳細を確認してみようとするも以下のようなエラーで確認できず。
MySQLのスキーマとしては存在しているけど、その実体のファイルが存在しないという状態になっているようでした。

> desc user_log;
(1017, "Can't find file: 'user_log' (errno: 2 - No such file or directory)")
> show create table user_log;
(1017, "Can't find file: 'user_log' (errno: 2 - No such file or directory)")
>

使われていないテーブルを削除した結果、Blue/Green デプロイの作成が成功!🎉

user_logは昔運用のなかで使われていたテーブルっぽいですが、今となってはだれもその詳細を知らないし使われてもいないテーブルだったので、削除して、再度 Blue/Greenデプロイメントの作成を実行してみると、今度は見事成功!!🎉

検証環境ではありますが、ここまで来るのに半年くらいかかりましたね・・・

MySQL version 8.0 の動作確認

検証環境でとりあえず MySQL version 8.0 の Green クラスターが作成できたので、動作確認も行いました。

HAProxy や MaxScale の接続先を ver 8.0 の Green に変えて Read の操作を確認する

HAProxyの接続先変更などの方法は以下ドキュメントを参考に。

https://www.haproxy.com/documentation/haproxy-configuration-manual/latest/

https://www.digitalocean.com/community/tutorials/how-to-use-haproxy-to-set-up-mysql-load-balancing-3

以下のように接続を構成しました。

Blue/Greenデプロイメントを作成すると、Green は最初は read_only が有効な状態で作成されるので、基本的に読み込みの動作確認しかできないです。

上記構成にして、Webアプリケーションを動かし、問題なくデータの取得ができていることを確認しました。

Write 部分は、手動テストや自動テストツールをつかって対応

Read の確認もできたので、検証環境でそのままスイッチオーバーまで実行し、スイッチオーバー後の新しいクラスターを利用して Write の確認も行いました。

このときは、軽く手動で確認もしつつ、Autify という自動テストのSaaSをつかって、一通りのデータ登録・更新・削除処理をテストし、全て問題がないことを確認しました。
こういうノンデグレードの確認テストとかで自動テストのツールがあるとめっちゃはかどりますよね!
Autifyつかってて良かった〜と思える瞬間でした。

本番での実施(11月)

検証環境で、Blue/Greenデプロイメントが実施できること、またその後のWebアプリケーションとの接続でも問題が起きないことを確認したので、本番で実施することになりました。

ひとまず Blue/Green デプロイメントの作成とReadだけMySQL Version 8 に

本番でも不要なテーブルやコメントを削除し、検証環境と同様に以下の構成にしました。

検証環境でも明らかな問題は見つけられなかったので、読み込み操作に関してはすぐにバージョンを上げた形です。
予想通り、特に問題も起きず、本番ワークロードで読み込み処理が行えることが確認できました。

メンテナンスは入れずにスイッチオーバーを強行!

社内で、いくつかのDBでBlue/Greenデプロイのスイッチオーバーをした実績があり、どれも2〜3分程度で終わり、その間も特に障害とかが起きていないという実績があったので、今回もシステムをオンラインにした状態で実行することにしました。

実績のあったケースに比べて、システムの利用者やDBの規模は結構大きめでしたが、問題ないだろうという判断に至りました。

とはいえ、手順書は用意

以下のような手順書を用意し、システムの利用者が少なくなる時間帯で実施しました。

  1. バッチの停止(オンライン処理は継続)
  2. Blue/Greenのスイッチオーバー実行
  3. 万が一の切り戻しに備え、レプリケーション設定を古い方のクラスターで実施
  4. バッチの再開

万が一に備えてのレプリケーション

検証環境で動作確認していたので、問題は起きない想定でしたが、もしかしたら、5.7 のクラスターに戻さないと行けない事態になることも想定して、レプリケーションの対応を実施しました。

参考にした記事は以下のものです。

https://aws.amazon.com/jp/blogs/database/implement-a-rollback-strategy-after-an-amazon-aurora-mysql-blue-green-deployment-switchover/

https://ca-srg.dev/00c421abf08d484298e27a84ab7ad9a3

レプリケーションの設定事態は、古い 5.7 クラスターで行い、新しい 8.0 のクラスターのデータ更新を取り込むような内容なので、スイッチオーバー実行するまでできません。ですが、レプリケーション用のユーザーの作成などは事前に済ませていました。

Blue/Greenの切り替えを実施!

特に問題もなく、こちらも2分もかからずにスイッチオーバーが行えました。
ただしちょっと問題も 🤦‍♂️

問題発生

レプリケーションができない

Aurora のドキュメントなどを参照しながら、レプリケーションを実行するSQL(ストアド・プロシージャ) を実行後、正しく行えているか確認すると・・・

mysql> show slave status\G
             Slave_IO_Running: Yes
            Slave_SQL_Running: No
                   Last_Error: Could not execute Delete_rows event on table XX.YY; Can't find record in 'YY', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log mysql-bin-changelog.001757, end_log_pos 55912059
                Last_IO_Error:
               Last_SQL_Error: Could not execute Delete_rows event on table XX.YY; Can't find record in 'YY', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log mysql-bin-changelog.001757, end_log_pos 55912059
      Slave_SQL_Running_State:
      Last_IO_Error_Timestamp:
     Last_SQL_Error_Timestamp: 241125 11:44:00
1 row in set (0.00 sec)

mysql>

上記のようなエラーが出てきて、レプリケーションがうまくいきませんでした 🤦‍♂️

delete 操作をする対象のレコードが見つからなかったみたいなメッセージがあり、その原因は・・・わかりませんでした。
おそらく、メンテナンスなどを実施せず、システムをオンラインのままで実施したので、スイッチオーバーする1~2分の間新旧どちらのクラスターにも書き込みができる状態になってしまい、バイナリログの整合性が崩れてしまったのかもと推測はしています。

この問題を解決させようと色々調べてみましたが、問題のあるバイナリログの部分を大きくスキップして、正常な箇所からレプリケーションを再開させるみたいな方法しか見つけられず、それだとデータロスもあるし、万が一切り戻しした際に、不完全な状態でのDBになってしまうという懸念から、一旦レプリケーションすることは諦めました 😭

Webアプリケーションで新しいデータが表示されない

スイッチオーバーしてから、しばらくするとユーザーの方から、データの更新処理をしているのに、画面を更新しても古いままのデータが表示される というご連絡が・・・・!

一瞬頭が真っ白になりましたが、システムの挙動を思い出すと、データの読み込み操作は、HAProxy が中継しているので、そこに問題がありそうだなと思って、HAProxy を再起動してみると、こちらの問題は無事解決しました。

↓どうやら、以下のスクショの赤線のように、スイッチオーバー後もHAProxyが古いDBクラスターに接続していたため、古いデータしか表示できないという状態になっていたようです。

そのときはすぐに理由がわからなかったのですが、HAProxyの仕様を調べてみると、DNSのクエリ結果を起動時にキャッシュすることに原因があったようです。

自分たちの HAProxy は少し古いものをつかっているので、そのバージョンだと、HAProxy の起動時に設定ファイルに書かれた接続先ホスト名の名前解決を行い、以降はその結果のIPアドレスに対して通信を行うというものだったようです。Aurora の Blue/Green デプロイメントの良いところは、クラスターエンドポイントなどのホスト名をそのままに、DBクラスターの入れ替えができるため、WebアプリケーションやミドルウェアのDBの接続情報を変更する必要がないという点にありますが、逆に今回はこれが落とし穴になってしまったようです。DBクラスターの実体が入れ替わったことで、当然IPアドレスなども変わる可能性がありますが、DNSが静的な状態だと古いDBクラスターに接続したままになってしまうという挙動でした。

なお、最近のHAProxyでは、動的な名前解決も可能となっているようです。

↓HAProxy のDNS結果をキャッシュする仕様の詳細

https://www.haproxy.com/blog/dns-service-discovery-haproxy

Quicksight でのエラー

QuicksightとMaxScaleをつかって、データ分析などを行えるようにしていますが、この2つの組み合わせとMySQL version 8.0 にどうやら互換性がないらしく、Quicksight にデータが取り込めない状態になってしまいました・・・

具体的には、Quicksightから MaxScale に接続しようとすると、Unknown system variable 'query_cache_size' というエラーが起き、接続ができない状態です。

MaxScale 単体なら Aurora の MySQL バージョン 8.0 に接続し問題なく動作していることを確認していただけに、予想外でした。

もしかしたらこれは解決を図るより、別のソリューションを考えたほうが良さそうかもなと思いました。今後ほかの方法も検討してQuicksightとの連携を維持する方法を考えようと思います。

まとめ

レプリケーションが行えない事態にはなりましたが、それは万が一に備えてのものだったので、とりあえず問題はなかったです

バージョンアップから数日がたち、本番で問題が起きていないので、レプリケーションの件は取り越し苦労なだけだったかもしれません・・・とはいえ結果オーライです 👍

今回の反省や学びとしては、やはり事前の検証を行っていても、メンテナンス時間を入れて、Blue/Green の切り替え作業中にDBが一切使われないようにするなどの対応はやっぱり必要だったかもなと思いました。そうしないと色々不測の事態が起きてしまいそうです。

今回の作業を経験して、DBのレプリケーションの仕組みだったり、他のミドルウェアやシステムとの関わりに関して理解が深まったので、それは自分の中で成長につながった良い収穫だったと思います。
それと、HAProxy の名前解決問題もすぐに気づけて良かった 🙌

Discussion

takapitakapi

初めまして。記事拝見させていただきました。色々な問題を乗り越えての更新作業お疲れ様でした。
私も同様にこの1年いくつかのAuroraのMySQLバージョンを5.7から8.0に更新したのですが、その時にMySQL Shellのアップグレードチェッカーを使用して事前にバージョン更新を行うことによる影響や問題が存在しているかを調べる方法が便利でしたので共有させていただきます。
もしかしたらご存知かもしれませんが、次同じような対応をされる際は使用を検討してみてください。

https://dev.mysql.com/doc/mysql-shell/8.0/ja/mysql-shell-utilities-upgrade.html

EightTEightT

takapi さん

読んでいただきありがとうございます 🙇‍♂️

また、共有もありがとうございます!MySQL Client は普段使っていますが、MySQL Shell というのがあるのは知らなかったです!
次またバージョンアップの作業あるときは使ってみたいと思います!

↓動かしてみましたが、プロンプトかっこいいですね、なんかJSも使えそうなのがすごい!😄