Development Division/Platform Team/Sys-Infra Unit で実施した Amazon EMR 1 のバージョンアップについてどのようなことをやったのか紹介します。 Repro では Presto や Hive などのセットアップに EMR を使用しており、以下の用途で活用しています。
- プッシュ通知の配信対象を抽出する
- 管理画面で参照するデータを抽出する
- S3 などに貯まっているイベントデータを集計する
EMR のアプリケーションとしては以下を使用していました。
- Presto
- Tez
- Hadoop
- Hive
- Hue
経緯
Repro では多くのミドルウェアを運用しており、日々バージョンアップしているのですが、最近まで2019年2月18日にリリースされた EMR 5.21.0 というバージョンを使用していました。もちろん Log4Shell のような脆弱性には別途対応していました。
ちょうど Kafka の導入や Kafka Streams app の導入のような大きめの変更が続いていた時期でもあり、特に問題なく稼動しているミドルウェアのバージョンアップは後回しになっていました。安定して稼動しており Hive や Presto で実行しているクエリに手を入れる機会も減少していました。
2019年から2022年くらいの間は Presto を自分たちで運用してオートスケーリングの最適化をしよう、というタスクが断続的に動いていた時期でもありました。
バージョンアップの検討を開始したのは2022年くらいだったと記憶しています。Amazon Linux のメンテナンスサポート期間の終了が迫っているからアップデートしていく必要がありました。
そうこうしているうちに Presto ではなく Trino に移行して新たなデータソースを使用したいという声が社内から聞こえてくるようになりました。
アップグレードパスの検討
まずは、どのバージョンの EMR を経由して最新の EMR までアップグレードしていくか検討しました。EMR のバージョンアップ1回でバージョンがあがるアプリケーションの数はなるべく少なくしたいので、以下のようにバージョンアップを進めるようにしたいと考えました。 また EMR は 5.36.0 と 6.6.0 から Amazon Linux 2 になっているので、EMR 5 系から EMR 6 系へのアップグレードで OS 部分の移行をスムーズにしようと考えました。
アプリケーション等 | EMR 5.21.0 | EMR 5.36.1 | EMR 6.6.0 | EMR 6.15.0 |
---|---|---|---|---|
Tez | 0.9.1 | 0.9.2 | 0.9.2 | 0.10.2 |
Hue | 4.3.0 | 4.10.0 | 4.10.0 | 4.11.0 |
Hive | 2.3.4 | 2.3.9 | 3.1.2 | 3.1.3 |
Hadoop | 2.5 | 2.10.1 | 3.2.1 | 3.3.6 |
Presto | 0.215 | 0.267 | 0.267 | 0.283 |
Trino | N/A | N/A | 367 | 426 |
Amazon Linux | 1 | 2 | 2 | 2 |
Log4Shell 対応 | 必要 | 不要 | 不要 | 不要 |
EMR のバージョンアップを3回やって EMR 6.15.0 まで上げたあと、最後に Presto から Trino に移行すればうまく進められるだろうと考えました。
最初に検討した時点では、以下の対応が必要でクエリの修正は必要ないだろうと考えていました。
- Amazon Linux 1 から Amazon Linux 2 への移行
- Hive と Hadoop のメジャーバージョンアップ
- Presto から Trino への移行
- Presto のバージョンアップや Trino への移行に伴って以下のコンポーネントの更新
- パフォーマンス向上のためにあてている presto-cassandra へのパッチの更新2
- presto-fluentd の更新
安全にバージョンアップを進めるのに必要なこと
EMR のバージョンアップで大事なことは 4 つあります。
- EMR のバージョンアップ前後で Presto/Trino にクエリした結果が異ならないこと
- EMR のバージョンアップ前後で Hive にクエリした結果が異ならないこと
- EMR のバージョン切り替え手順がシンプルであること
- EMR のバージョン切り替え時に他のアプリケーションのリリースが不要であること
EMR のバージョンアップ前後で Presto/Trino にクエリした結果が異ならないこと
Presto についてはクエリログや統計情報を BigQuery のテーブルに保存して検索できるようになっていました。また、任意の抽出条件のクエリを異なる条件(EMR のバージョン等)で実行して結果の同一性をチェックするための campaign_segmentation_comparator
と呼ばれる社内独自の仕組みは以前から利用していました。
Trino については、後述しますがクエリログや統計情報を BigQuery のテーブルに保存して検索できるようにする必要がありました。campaign_segmentation_comparator
は Trino でもほぼそのまま使えるようになっていました。
EMR のバージョンアップ前後で Hive にクエリした結果が異ならないこと
Hive で実行しているクエリについてはログもなく、同一のクエリを別々の EMR cluster で実行して結果を比較する仕組みもありませんでした。
Hive on Tezのメトリクスを任意のデータ基盤に蓄積する方法 - おくみん公式ブログ みたいなことをやれば、比較的簡単に Hive (Tez) で実行したクエリを記録できます。ただ、今回は色々とバージョンアップしなければならないので、社内でレビューできる人が少なそうなことをするのはやめました。 幸いなことに Repro では Hive を使ってクエリを実行するのは、定期的に実行しているバッチからだけでした。バッチの実行方法も統一されていたので、そこに少し手を入れて実行したクエリのログを Fluentd 経由で S3 に保存し、Athena でクエリできるようにしました。
記録したクエリを抽出して、複数の EMR cluster で同時に実行し、結果を S3 に保存し、厳密に比較できるようにしました。クエリの抽出・実行・結果の比較をそれぞれコマンド一発で実行できるようにしました。
EMR のバージョン切り替え手順がシンプルであること
EMR のバージョン切り替え手順も見直しました。
EMR cluster 自身はデータを持たないため、ディスポーザブルですが、バージョン切り替えを実行するには後述のように煩雑な手順を実行する必要がありました。このことが EMR のバージョンアップを放置してしまった原因の1つと言えます。
バージョン切り替えの手順を見直す前は、テキストでまとめられた手順書を注意深く読んで理解し、1つずつコマンドを実行していく必要がありました。 簡略化しますが、Hive 用と Presto 用で若干コマンドの異なる手順が2種類ありました。 以下は Hive 用の例です。
1. Hive 用 EMR cluster を起動する 2. 新しいクラスタの TASK node の数が 0 になっていることを確認する 3. メタデータストアになっている RDS に MySQL クライアントで接続しておく 4. Rundeck で一部のジョブのスケジュールを停止する 5. Master node の Private DNS name を確認し、メタデータストアのレコードを更新する 6. 新しい Hive 用 EMR cluster と、元から存在する Presto 用 EMR クラスターで Hive のメタデータサーバーを再起動する 7. 新しい EMR cluster の Standby タグを削除し、古い EMR cluster に Standby タグを付与する 8. Rundeck で停止していたジョブを実行可能状態に戻す 9. Redash で datasource のホストを更新する 10. 問題なければ古い EMR cluster を削除する
慣れてくると手順2や手順4は忘れそうですね。Presto の入れ替え手順も同様に存在していたのですが、この検討をした時点でほぼスクリプト化されていました。 EMR cluster の起動は別でスクリプト化されていたので、上記のうち手順2から手順9を対話的に確認しながら実行するスクリプトを用意しました。
このスクリプトによって、思い出したり、コマンドに間違いがないか何度もチェックしたりする手間や精神的な負担がなくなって、以前よりもかなり楽に EMR cluster の入れ替えを実施できるようになりました。
EMR のバージョン切り替え時に他のアプリケーションのリリースが不要であること
Repro では Rails アプリケーションやバッチから EMR cluster で稼動している Trino/Presto や Hive にクエリを投げています。そのため、Rails アプリケーションやバッチで使用しているクエリやクライアントが、EMR のバージョン切り替え前後でシームレスに動作しないとなるとリリースの順番や切り戻しの手順の検討など、EMR のバージョン切り替え以外に検討・実施しなければならないことが増えます。
考えることややることの手順が複雑化すると失敗する可能性が高まります。
なので、EMR のバージョンアップ実施前に、Rails アプリケーションやバッチ側の改修し、EMR のバージョンアップ時には EMR cluster の入れ替えのみを実施すればよいようにしました。
切り戻しについても EMR cluster の入れ替えのみで可能なようにしました。
EMR 5.21.0 から EMR 5.36.1 への更新
検証手順と EMR cluster 入れ替え手順の整備をしたので大きなトラブルもなく1週間くらいで作業完了しました。
- Amazon Linux 2 対応
- 不要になった bootstrap actions の削除
- datadog-agent のインストールスクリプト更新
- td-agent 4 へ更新
- presto cassandra connector の更新
- presto-fluentd の更新
- Presto の設定を一部 EMR が提供するデフォルト値に寄せた
- root volume のサイズを増やした
- デフォルトの 10GiB だと 98% 使用してしまう
presto-fluentd の更新について補足
wyukawa/presto-fluentd の v0.0.4 を使ってクエリの統計情報を取得していたのですが、動かなくなっていたので更新しようとしたところ単純にアップデートはできませんでした。
v0.0.4 までは com.facebook.presto.presto-spi
を使用していたのですが v0.0.5 から io.prestosql.presto-spi
を使うように変更されており、そのままだと EMR 5.36.1 の Presto (Prestodb) では使えませんでした。そこで v0.0.4 の時点から fork して、削除された API の対応や API の名前変更への対応を行ないました。
しばらくは使うので自社で管理できるようにしましたが、このときはまだ Trino 移行に伴う様々なことに気付いていませんでした。
次回予告
EMR 5.21.0 から EMR 5.36.1 への更新は成功しました。 次回は EMR 5.36.1 から EMR 6.6.0 への更新について書いていきます。