MySQL
AWS
java8
Aurora
Tomcat8

オンプレMySQLからAuroraへの移行で躓いたポイント(その3・移行後)

この記事は以下の記事の続きです。

一部、内容がその2と被りますが、移行後に生じた問題…というよりは、移行段階から移行後までずっと引きずっていた問題について触れていきます。

【注意】

  • ここで示した対策をそのままプロダクト環境に適用しているとは限りません。大抵はもう少し手を加えて適用しています。
  • 一部「これからプロダクト環境に適用する予定」のものもあります。

移行後まで引きずった問題点

ずばり、その2で記した「複数スレッドでのパフォーマンス問題」です。

移行前と移行後を比較すると、Webサーバの台数は1.5倍に増やしました。1台当たりの搭載メモリも1.5倍に増やしましたが、一方で仮想CPUのコア数は1台当たりで半分にしました。

「あれ?G1GC使ってるDBCP2のEvictionも走ってるのにコア減らしていいの?」となりそうですが、実際、r4.largeとm4.xlargeで結果の差が全くといっていいほど出ませんでした(同じような負荷レベルで処理が詰まる)。r4.largeでもCPU使用率が30~35%程度までしか行かず…腑に落ちないまま移行リリース。

移行当日は土曜日だったので負荷はあまりなく(でもリリース直後は普通の土曜日よりアクセスが多かった)特に問題は出ませんでしたが、最初の営業日、やはりWebサーバが1台、処理が詰まって落ち、再起動しました。
※50xエラーが一定期間に閾値以上発生したら再起動するように仕込んでありました。

しばらく様子見していましたが、頻繁ではないものの週に1回から数回、Webサーバが落ちます。アクセスログを見ると、不正なアクセスではなく正規のユーザが、

「以前より反応が遅くなったので、画面を何度かリロード」

しているようだったので(一部「イラついて連打」している人もいたりいなかったり?)、最初は画面で「処理中」表示を行い操作を抑制するとともに、WAFで過剰なアクセスを遮断する対応を行いました。

時間稼ぎ(?)の対策を開始

しかし、WAFでは「閾値を低くしすぎると正常なアクセスを遮断してしまう」こともあり、あまり効果的にリロードを抑制できず。画面表示での(心理的な)抑制も、処理の進行状況を表示しないとかえって逆効果になることも(ケースバイケースですが)。

この時点ではまだ「オンプレ時代より遅い」ことの正確な原因(Web~DB間のネットワークレイテンシが影響していることはまず間違いはなかったのですが)は把握できておらず、解決が長引きそうだったので、

  • Webサーバをさらに増やしてオンプレ時の2倍に
  • Aurora DBクラスタのWriterがあるゾーンとは別のAZにレプリカ(Reader)を設置し、SELECT負荷が高いアプリケーションを一部修正してReaderにアクセスするように

するとともに、

  • 「処理が詰まる」とはいっても「特定のWebサーバ」の「負荷が高くなったアプリケーション単体」でDBアクセスが詰まっており、ほかのWebサーバはそのまま動き続け、同じWebサーバでも別のアプリケーションはしばらく動作を続けていた(DBへのコネクションプーリングはWebアプリケーション個々で分けて張っていたため)

ことから、閾値以上の50xエラーが出たときにWebサーバを単純に再起動するのではなく、ALBのDrainingを使って再起動し「巻き込み被害」を極力減らす対応を行いました。

※タイトルがわかりにくくてごめんなさい(どう表現したらよいかわからず…)。

また、

  • 「負荷が高い画面」の処理であっても、利用ユーザによって処理するデータ量に差があり、結果として処理時間に大きな開きがある。一律に「単位時間当たりのリクエスト数」を制限しても(WAFによる遮断と同様に)うまくいかない

ので、「負荷が高い画面のリクエストを受け付けた時点からレスポンスを返しきるまで」を「1」とカウントし、(ユーザ毎に)並行処理(カウント)数が閾値以上になっているかどうかを見て遮断するように、JavaのローカルキャッシュとTomcatのサーブレットフィルタを使って対応しました。

※こちらも上に同じ。タイトルを付けるのは難しい。

なお、この対応を行っている最中、正規のユーザが「遅さに慣れた」(リロードしまくるとかえって遅くなることに気づいた)ことで、処理が詰まってWebサーバが再起動することはほぼなくなりました。但し、遅い状態が解消されたわけではないので、対応が不要になったわけではありません。

Meltdownパッチで動揺、そして問題解決

2018年1月になり、Meltdown&Specterの騒ぎが発生、AWSもEC2やAuroraなど関係するクラウド基盤サーバにパッチを当てたようです。その結果、「サーバ性能低下」という事案が発生しました。

ベンチマークを取りつつ急いで利用中AuroraインスタンスのバージョンアップとR4への移行を行いましたが、作業直前にほぼ元の性能に戻っていました(振り回された…)。

ただ、ここでベンチマークを取りながら別の対応策を検討。移行リリース前、コネクションプールをDBCP2からTomcat JDBC Poolに移行しようとして失敗して時間切れになっていましたが、今度はHikariCPへの移行にトライ。
※「ステージング環境での負荷テスト結果はともかく、プロダクト環境で処理が詰まるのはDB(Aurora)側が原因ではなさそうだけど、DBへの問い合わせで詰まっていることは間違いない」というところまでは絞り込んでいましたので、行き当たりばったりの対応ではありません(多分。おそらく。きっと)。

結果、これがうまくハマってオンプレ時と同等(一部はそれ以上)の性能を取り戻すことができました。

Auroraフェイルオーバー時の処理の詰まりもなくなりました。

また、これで(現状の負荷なら)Webサーバの台数も元に戻すことができます。

AWSのネットワーク環境は、同一AZであってもインスタンス間のレイテンシ(遅延)が往復平均で200マイクロ秒(μs)以上あるようです(OS上で計測した場合)。これは、同じ室内に1ラック~数ラック立てて運用しているオンプレ環境(特にSDNを使っていない環境)とは倍以上の差があります(オンプレが速い)。

通常のSQLクエリ/コマンドの実行では大した影響がなくても、コネクションプールでは接続のValidationやEviction等の際に確認クエリを発行する上、MySQL Connector/Jも色々と管理・確認用のクエリを流しがちな傾向があるので、わずかな処理時間の増加でもリソースの取得・開放がスムーズにいかない原因になります。

そこを、HikariCPにしたことで処理が軽減されて(クエリ発行数も減ったはず)、AWS環境でもうまく回るようになったようです。

なお、途中では触れませんでしたが、負荷を軽くするには「遅いSQLの改善」も重要です(当然、その対応も進めていました)。

HikariCPでWebサーバの動作が軽くなったことで、今度は「遅いSQLを処理するときのDB負荷」のほうにボトルネックが移りました。これまで以上にSQLの改善、INDEXやテーブル設計の調整などに力を入れる必要がありそうです。

まとめ

  • ユーザは「遅くなった」ことには割と敏感に気づき、そして(気軽に?)画面をリロードする
  • 一方で、「遅くなった」ことにはしばらくすると慣れる(限度はあるし、ユーザの時間を無駄に奪ってはいけない)
  • ちょっとしたレイテンシの変化が処理のサイクル・バランスを大きく崩すことがある(オンプレで実績がある構成でも、そのままで行けるとは限らない)
  • ボトルネックを1つ解消すると、別のところにボトルネックが移る(ので、移った先の対応を進める必要がある)