Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
APMをちゃんと使おうとしたら、いつのまにか独自gemを作っていた話
Search
kokuyouwind
October 28, 2023
Programming
0
620
APMをちゃんと使おうとしたら、いつのまにか独自gemを作っていた話
Kaigi on Rails 2023 セッションの発表資料です
https://kaigionrails.org/2023/talks/kokuyouwind/
kokuyouwind
October 28, 2023
Tweet
Share
More Decks by kokuyouwind
See All by kokuyouwind
Let's use LLMs from Ruby 〜 Refine RBS types using LLM 〜
kokuyouwind
0
5.4k
RBS meets LLMs - Type inference using LLM
kokuyouwind
0
730
オンラインボードゲームを作りたい人生だった
kokuyouwind
0
370
1年間本番運用してわかった、スタートアップこそAWS Copilot CLIを使うべきNつの理由
kokuyouwind
2
10k
なるべく楽したいAWSセキュリティ
kokuyouwind
1
37
Railsパフォーマンス・チューニング入門
kokuyouwind
0
210
Rubyパターンマッチに闇の力が備わり最強に見える
kokuyouwind
0
56
Slackワークフロー活用術
kokuyouwind
0
64
10分で作る勉強会アプリ
kokuyouwind
0
36
Other Decks in Programming
See All in Programming
Go製CLIツールGatling Commanderによる負荷試験実施の自動化
okmtz
3
700
Competitionsだけじゃない! Kaggle Notebooks Grandmasterのすすめ
corochann
1
340
CSC509 Lecture 03
javiergs
PRO
0
140
Modern Functional Fluent CFML REST by Luis Majano
ortus24
0
140
2024-10-01 dev2next - Observability for Modern JVM Applications
jonatan_ivanov
0
130
C#および.NETに対する誤解をひも解く
ymd65536
0
280
個人開発で使ってるやつを紹介する回
yohfee
1
700
MLOps in Mercari Group’s Trust and Safety ML Team
cjhj
1
120
Kotlin Multiplatform at Stable and Beyond (Kotlin Vienna, October 2024)
zsmb
2
360
デバッグの話 / Debugging for Beginners
kaityo256
PRO
8
550
GrafanaのHTTP API を眺めてみよう
rinchoku
0
130
Vue :: Better Testing 2024
up1
1
400
Featured
See All Featured
Facilitating Awesome Meetings
lara
49
6k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
25
1.7k
Debugging Ruby Performance
tmm1
73
12k
Stop Working from a Prison Cell
hatefulcrawdad
267
20k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
191
16k
The Brand Is Dead. Long Live the Brand.
mthomps
53
38k
The Language of Interfaces
destraynor
154
24k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
225
22k
Creatively Recalculating Your Daily Design Routine
revolveconf
217
12k
Building an army of robots
kneath
302
42k
What's new in Ruby 2.0
geeforr
341
31k
In The Pink: A Labor of Love
frogandcode
139
22k
Transcript
APM をちゃんと使おうとしたら、 いつのまにか独⾃gem を作っていた話 Leaner Technologies, Inc. 黒曜 (@kokuyouwind)
$ whoami 黒曜 / @kokuyouwind Leaner Technologies Inc. 所属 Rails
エンジニア インフラ・SRE 的なこともやっている APM など開発周辺ツールの管理
APM の話をします
APM とは Application Performance Monitoring の略 リクエスト数や応答速度を監視・管理できる 代表的な APM サービス
etc.
APM の導⼊⾃体は結構簡単 https://docs.datadoghq.com/ja/tracing/trace_collection/dd_libraries/ruby/ ⼤抵の APM は エージェントソフトウェアと gem を設定するだけ #
Gemfile gem 'ddtrace', require: 'ddtrace/auto_instrument' # config/initializers/datadog.rb Datadog.configure do |c| c.service = 'my-service' end 1 2 3 4 5 6 7
APM の画⾯例 (1) https://docs.newrelic.com/jp/docs/apm/new-relic-apm/getting-started/introduction-apm/
APM の画⾯例 (2) https://docs.datadoghq.com/ja/tracing/other_telemetry/connect_logs_and_traces/
🤔 すごいけど、何を⾒ればいいの… ?
APM 導⼊後のあるある 導⼊で満⾜して、その後画⾯を開かなくなった 応答速度の遅いエンドポイントを⾒て、 「やっぱりここは重いよね〜」と話して終わる ソースコード上でどこが重いのか、結局わからない 筆者は過去に全部⼼当たりがあります…( ⼩声)
ひとつの参考事例として、 弊社での APM の使い⽅を紹介します
アジェンダ 簡単な APM の使い⽅ トレースを使ったパフォーマンス・チューニング APM Traceable gem を作った話 具体的なチューニング事例紹介
まとめ
アジェンダ 簡単な APM の使い⽅ トレースを使ったパフォーマンス・チューニング APM Traceable gem を作った話 具体的なチューニング事例紹介
まとめ
簡単な APM の使い⽅ ⼤抵の APM は集計単位ごとにページに分かれている サイト全体 エンドポイントごと リクエストごと 最初は「サイト全体」の指標で⼤まかな傾向を⾒ると良い
細かく掘り下げていくときに「エンドポイントごと」 「リクエストごと」の順に⾒ていく
ここから先は Datadog APM を例に スクリーンショットを出します ( 他APM でも同様の画⾯があるはず)
APM の⾒⽅ - 全体の統計
APM の⾒⽅ - 全体の統計
APM の⾒⽅ - 全体の統計 リクエスト数も応答速度も、極端な変化がないか確認 曜⽇や時間帯で傾向が違うため、前週同曜⽇と⽐較する サービスによっては⽉末・年末なども考慮する 異常値があった場合はトリアージする ⼀時的要因であれば様⼦⾒、問題がありそうなら調査・対応
APM の⾒⽅ - 個別エンドポイント
APM の⾒⽅ - 個別エンドポイント
APM の⾒⽅ - 個別エンドポイント
APM の⾒⽅ - 確認タイミング 週1 で「APM を⾒る会」を実施している 悪化傾向を⾒つけられるよう定期的に⾒る機会を作る トリアージもこのタイミングで実施 開発・運⽤に関わるメンバー全員で⼀緒に⾒ると良い
悪化しているエンドポイントに機能追加などの ⼼当たりがあるか確認しやすい
アジェンダ 簡単な APM の使い⽅ トレースを使ったパフォーマンス・チューニング APM Traceable gem を作った話 具体的なチューニング事例紹介
まとめ
重いエンドポイントを改善する際、 リクエストごとのトレースが⾜がかりになる
リクエストごとのトレース 何に時間がかかっているか( スパン) を表示できる DB アクセスやWeb API 呼び出しのスパンを⾃動で収集
N+1 クエリへの対処 SQL クエリを確認し、適切に includes などを設定する 特定条件やデータセットがないと発⽣しないN+1 クエリは bullet gem
などで⾒つけづらいケースがある
重いクエリへの対処 まずは EXPLAIN で実⾏計画を確認する インデックス追加だけで改善することも多い
時間が⾜りないので 詳しいチューニング⼿法は割愛
https://slides.com/kokuyouwind/rails-performance-tuning 詳細は以前の Kaigi on Rails 2020 で話した 「Rails パフォーマンス・チューニング⼊⾨」をご覧ください!
パフォーマンス・チューニングについては 他の⽅も発表されていたのでおすすめです! Rails アプリの 5,000 件の N+1 問題と戦っている話 初めてのパフォーマンス改善〜君たちはどう計測す( はか)
るか〜
アジェンダ 簡単な APM の使い⽅ トレースを使ったパフォーマンス・チューニング APM Traceable gem を作った話 具体的なチューニング事例紹介
まとめ
⾃動トレースは便利だが、 標準のトレースだけだと困るときがある
困るとき 1. SQL の発⾏場所がわからない ActionController スパン直下に SQL クエリのスパンがある 単なるメソッドの呼び出しは標準だとスパンにならない クエリ構築がどのコードで⾏われているかわからないと
修正しようがない ( 重いクエリの例を再掲)
困るとき2 . Ruby での処理が重いケース Ruby 処理は⾃動でスパンに区切られない 原因が全くわからないので対処もできなくなってしまう
⼿動インストゥルメンテーション https://docs.datadoghq.com/ja/tracing/trace_collection/dd_libraries/ruby/ Datadog::Tracing.trace(name, **options) do |span, trace| # 計測したい処理をここに記⼊ end
1 2 3 Datadog では Datadog::Tracing.trace を使って ⼿動でスパンを追加できる 渡したブロック内がスパンとして区切られる
Datadog::Tracing の微妙な使いづらさ # ↓ 標準だとこう書く必要がある class User def awesome_method Datadog::Tracing.trace(
'awesome_method', service: 'my-service', resource: 'User') do # ... 処理 end end end 1 2 3 4 5 6 7 8 9 10 11 処理対象をブロックで囲むとインデントが変わる 差分が⼤きくなって気軽につけ外ししづらい 引数が多く、記述がやや冗⻑
APM Traceable gem # ↓ こう書けるようにした class User include ApmTraceable::Tracer
trace_methods :awesome_method def awesome_method # ... 処理 end end 1 2 3 4 5 6 7 8 9 trace_methods にメソッド名を指定するとスパンとして表示 UseCase やPresenter など、独⾃レイヤークラスの呼び出し 重いメソッドを切り分けたプライベートメソッド
APM Traceable gem 各Presenter#call をtrace_methods した結果
APM Traceable gem 元々は1 モジュールでlib 以下に置いていた 以前 に書いた話 複数プロダクトに必要だったのでgem に切り出した
APM Traceable gem 本体 Datadog への送信⽤アダプタ アダプタを作れば他の APM にも切り替えられるようにした ブログ記事 apm_traceable apm_traceable-datadog
APM Traceable gem - 仕組み module ApmTraceable::Tracer def self.trace_methods(*method_names) wrapper
= Module.new do method_names.each do |method_name| define_method method_name do |*args, **options, &block| trace_span(method_name.to_s) { super(*args, **options, &block) } end end end prepend(wrapper) end end 1 2 3 4 5 6 7 8 9 10 11 12 13 trace_methods で各メソッドを持つモジュールを作り、 prepend して呼び出しをラップするだけ
APM Traceable gem - 仕組み Class User awesome_method prepended Module
awesome_method メソッド呼び出し Datadog Adapter trace_span トレース送信
アジェンダ 簡単な APM の使い⽅ トレースを使ったパフォーマンス・チューニング APM Traceable gem を作った話 具体的なチューニング事例紹介
まとめ
事例 1. SQL クエリの速度悪化 MySQL 8.0 に更新した際、特定エンドポイントだけ遅くなった
事例 1. SQL クエリの速度悪化
事例 1. SQL クエリの速度悪化 テンポラリテーブルでの SELECT COUNT(*) の問題っぽい… https://zenn.dev/hmatsu47/articles/mysql80-count-slowdown
事例 1. SQL クエリの速度悪化 元のクエリが GROUP BY してから最初の⾏を取っており、 実質 DISTINCT
相当の処理がテンポラリテーブルで⾏われていた DISTINCT を使えばテンポラリテーブルを使わなくなるので直した
事例 1. SQL クエリの速度悪化 元々 1.0sec -> 悪化して 4.0sec ->
改善後 100ms 結果的に元の10 倍に⾼速化🚀
事例 2. 外部API 呼び出しのトレース 外部 API を並列で呼び出す箇所のトレースが取れていなかった ( 単独だと取れるが、2 箇所以上Parallel
だと取れない)
事例 2. 外部API 呼び出しのトレース Parallel をまたぐとスパンの親⼦関係が途切れるようなので、 ⼿動で親ダイジェストを引き渡すよう修正 def search_parallel(clients) +
trace_span('search_parallel') do |_span, trace| results = Parallel.map(clients, in_processes: clients.count) do |client| + trace_span(client.name, continue_from: trace.to_digest) do client.search + end end + end end 1 2 3 4 5 6 7 8 9
事例 2. 外部API 呼び出しのトレース この変更で、きれいにトレースが⾒られるようになった 🎉
事例 2. 外部API 呼び出しのトレース トレースを精査したところ、外部API 呼び出しよりも XML レスポンスのパース処理に時間がかかっていた
事例 2. 外部API 呼び出しのトレース ローカルで再現させ、 Stackprof でプロファイルを取ったところ DidYouMean::Jaro.distance が⼤半を占めている… ?
事例 2. 外部API 呼び出しのトレース XML で未知の attribute が出てきた際、 const_get でNameError
が起こる実装になっていた。 const_defined? で確認を挟むよう実装を修正 klass = "CXML::#{camelize(key)}" - send("#{key}=", Object.const_get(klass).new(val)) + send("#{key}=", Object.const_get(klass).new(val)) + if Object.const_defined?(klass) + send("#{key}=", Object.const_get(klass).new(val)) + else 1 2 3 4 5 6
事例 2. 外部API 呼び出しのトレース
事例 2. 外部API 呼び出しのトレース エンドポイントのレスポンスタイムも、 修正を境に⾶び上がった値が出ることがなくなった
アジェンダ 簡単な APM の使い⽅ トレースを使ったパフォーマンス・チューニング APM Traceable gem を作った話 具体的なチューニング事例紹介
まとめ
まとめ APM は本番で起こっている問題を⾒つけるのに便利 統計値を定期的に⾒て、変化に気づけるようにすると良い トレースを⾒ると重い原因を⾒つける⾜がかりになる 重要なメソッドにスパンを仕込んでおくと原因を掘り下げやすい これをやりやすいように独⾃gem を作った トレースで⽬星をつけて、掘り下げた調査を別で⾏うこともある APM
全然使いこなせてないので、いい使い⽅教えてください! 休憩時間や懇親会でお話しましょう
補⾜資料
各 APM での⼿動トレース機能 New Relic add_method_tracer が trace_method とほぼ同等機能 Scout
APM だけで⼗分かもしれない Open Telemetry Ruby カスタムインストゥルメンテーション Custom Instrumentation Auto Instruments Manual Instrumentation