Ruby 言語メトリクス (パブリックベータ)
この記事の英語版に更新があります。ご覧の翻訳には含まれていない変更点があるかもしれません。
最終更新日 2022年11月28日(月)
Table of Contents
Ruby ランタイムに関する可視性を向上させるために、Ruby 言語メトリクス機能では、アプリケーション関連のメトリクス内の追加の言語固有の時系列メトリクスを明らかにします。Ruby メトリクスには、空きおよび割り当て済みヒープオブジェクト数と空きメモリスロットの数が含まれます。
この機能は現在、パブリックベータです。 フィードバックは、heroku-metrics-feedback@salesforce.com までメールでお送りください。
Ruby 言語メトリクスは、eco
dyno を除くすべての dyno で使用できます。
JRuby アプリケーション
アプリが JRuby で動作している場合、そのアプリは Java 仮想マシン (JVM) 上で実行されるため、これらのメトリクスは使用できません。ただし、JVM メトリクスをエクスポートしたり、表示したりすることは可能です。詳細は、JVM ランタイムメトリクスのドキュメントを参照してください。
全般的な情報
メトリクスの表示設定に関する全般的な情報は、言語ランタイムメトリクスの親ドキュメントを参照してください。
この実装では、Basecamp の Trashed のフォークである Barnes を使用して Ruby ランタイムメトリクスを Heroku のシステムに報告します。
はじめに
最初に、アプリケーションで 「Language Metrics」 (言語関連のメトリクス) フラグを有効にします。これは、ダッシュボードまたは CLI を使用して実行できます。ダッシュボードから有効にするには、メトリクスの環境設定パネルを開き、「Enhanced Language Metrics」 (拡張言語メトリクス) トグルと 「Ruby Language Metrics」 (Ruby 言語メトリクス) トグルの両方をオンにします。
Heroku CLI から有効にするには、次のようにします。
$ heroku labs:enable "runtime-heroku-metrics" -a "my-app-name"
$ heroku labs:enable "ruby-language-metrics" -a "my-app-name"
メトリクスの buildpack を追加する
次のステップは、アプリケーションに heroku/metrics
buildpack を追加することです。これは、CLI またはダッシュボードを使用して実行できます。
CLI を使用した buildpack の追加
コマンドラインインターフェース (CLI) を使用して buildpack を追加するには、次を実行します。
$ heroku buildpacks:add -i 1 heroku/metrics
パイプラインプロモーションを使用してデプロイしている場合は、この buildpack を本番アプリとステージングアプリの両方に追加する必要があります。
このプロセスは、buildpack が使用されないコンテナベースのアプリケーションでは動作しません。現在、このベータではコンテナベースのアプリケーションがサポートされていません。
ダッシュボードを使用した buildpack の追加
アプリの Settings
(設定) タブから、次のように buildpack を追加します。
アプリケーションに Barnes gem を追加する
次のステップは、Barnes gem を利用するようにアプリケーションを準備することです。
Gemfile に Barnes gem を追加します。
gem "barnes"
次に、以下を実行します。
$ bundle install
フォーキング Web サーバー (Puma など) を使用している場合は、各ワーカーで Barnes.start
を呼び出す必要があります。Puma の場合は、次のようになります。
require 'barnes'
before_fork do
# worker specific setup
Barnes.start # Must have enabled worker mode for this to block to be called
end
注意: これは、ワーカーを使用していないと機能しません。これを機能させるには、少なくとも 1 つのワーカーが必要です。
コミットとプッシュ
完了するには、次のように変更をコミットし、Heroku にプッシュします。
$ git add -A .
$ git commit -am "Enable Heroku Ruby Metrics"
$ git push heroku master
使用可能なメトリクス
上記の手順が完了した後、これらのメトリクスが使用可能になるまでに数分かかることがあります。
Puma Pool Usage (Puma プール使用)
このメトリクスは、アプリケーションがアンダープロビジョニングされているか、またはオーバープロビジョニングされているかを判定するために役立ちます。このメトリクスは、アプリケーションのリクエスト容量の割合 (%) として提供されます。この値が 100% か、またはそれに近い場合は、アプリケーションが将来のリクエストの処理を開始する前に、そのリクエストをキューに入れる必要があることを示しています。この割合は、Puma Worker (プロセス) の数とワーカーあたりの最大スレッド数に基づいています。このメトリクスの適切な値は 80% 以下です。この範囲により、アプリケーションはある程度の “バースト” 容量を維持できるようになります。アプリケーションが 80% を超えている場合は、そのアプリケーションにさらに dyno を追加すると、容量が増えてより多くのリクエストを処理できるようになりますが、それによりこの使用率メトリクスは減ります。
例: アプリケーションに 2 つの Puma Worker プロセスがあり、各プロセスに 5 つのスレッドがある場合、生成される最大数は 10 スレッド (2 プロセス x 5 スレッド) になります。この Heroku アプリに 10 個の dyno があり、その半分が 4 つのリクエストを処理し、その半分が 2 つのリクエストを処理している場合、平均の使用数は 3 になります。
dyno の数を増やす以外に、さらにワーカーを追加するか、または最大スレッド数を増やした場合も、アプリケーションが一度に処理できるリクエストの理論的な最大数が増えるため、この使用率は減ります。dyno はメモリと CPU の両方によって制限されることに注意してください。スレッドの最大数を任意の大きな値に設定した場合は、それだけ多くの並列リクエストを無理なく処理することを期待できないにもかかわらず、アプリケーションが多くの容量を備えているように見えます。妥当な開始点としてワーカーあたり 5 つのスレッドを使用します。24 時間のメモリ制限を超えることなくそれに近づくまで、ワーカーの数を増やすことができます。
使用の割合の表示に加えて、生成されたスレッドの数も表示されます。アプリケーションが最小スレッド数を最大スレッド数に等しい値に設定している場合は、上の図に示すように、この生成された値は常に 100% になります。
Heroku では、dyno の冗長性を維持するために少なくとも 2 つの dyno を実行することをお勧めします。
このメトリクスにアクセスするには、Barnes gem >= 0.0.7
と Puma gem >= 3.12.0
が必要になります。
このグラフを使用する方法
プール容量の使用率が一貫して 80% を超えている場合は、そのアプリケーションがアンダープロビジョニングされており、Web アプリにさらに dyno を追加することでメリットが得られる可能性があります。dyno を追加すると、同じ負荷がより多くのインスタンスに分散されるため、このメトリクスは低下します。アプリケーションが 80% 以下に安定するまで、引き続き dyno を追加します。80% の使用率の前後でアプリケーションを実行すると、トラフィックの急増を処理する余地が残されます。
プール容量の使用率が一貫して 20% 以下である場合は、そのアプリケーションがオーバープロビジョニングされており、Web アプリから dyno を削除することができます。dyno を削除すると、同じ負荷がより少ないインスタンスに分散されるため、この使用率メトリクスは増えます。dyno の冗長性を通してアプリケーションに回復性を提供するために Heroku がアプリケーションあたり少なくとも 2 つの dyno の実行を推奨していることは注目に値します。
Free Memory Slots (空きメモリスロット)
Free Memory Slots (空きメモリスロット) のプロットには、選択された時間間隔の最小、最大、平均の使用可能な空きメモリスロットが表示されます。最新の間隔がデフォルトとして表示されます。
このグラフを使用する方法
多数の (たとえば、300,000 を超える) 空きスロットは、多数のオブジェクトを割り当てたり、解放したりするコントローラアクションが存在することを示します。これについての詳細は、heap_free_slots
メトリクスに関する GC.stat のガイドで参照できます。
これを確認すると、オブジェクト割り当てに関する詳細情報を提供する Scout や Skylight などのパフォーマンス分析サービスに到達できます。あるいは、多機能ベンチマークを使用して、アプリケーション内のどこに大量のメモリが割り当てられているかを見つけることができます。
Heap Objects Count
(ヒープオブジェクト数)最大および平均の割り当て済みと解放済みのヒープオブジェクト数がログスケールで表示され、解放済みが Y 軸のベースラインの下にミラーリングされて表示されます。 このメトリクスには、選択された時間間隔が反映されます (デフォルトは最新の間隔)。
このグラフを使用する方法
割り当て済みと解放済みのオブジェクト数は、どちらもほぼ同じ速度で増加します。アプリケーションがそのメモリの “定常状態” に近づくまで、時間の経過と共に割り当て済みのオブジェクトの方が少し多く存在することになります。ただし、割り当て済みのオブジェクトの方が解放済みのオブジェクトより大幅に多く存在する場合は、コード内のどこかでオブジェクトを保持していることを示している可能性があります。どのオブジェクトが保持されているかを見つけるために、メモリプロファイラや多機能ベンチマークなどのツールを使用することが必要になります。
メトリクス収集の無効化
Ruby メトリクスの収集を無効にするには、単純に 「Metrics Preferences」 (メトリクスの環境設定) パネルまたは次の CLI コマンドを使用して 「Enhanced Language Metrics」 (拡張言語メトリクス) トグルをオフに切り替えます。
$ heroku labs:disable "runtime-heroku-metrics" -a "my-app-name"
デバッグ
メトリクスが表示されない場合、チェックすべきいくつかの一般的な事項を次に示します。
- アプリで Puma
>= 3.12.0
と Barnes>= 0.0.7
を使用している。 Barnes.start
がbefore_fork
ブロックの内部にある。- アプリがシングルモードではなく、クラスターモード (2 ワーカー以上) で実行されている。そうでないと、
before_fork
は起動されません。 - メトリクスの buildpack がアプリに追加されている。および、
- [Labs] フラグが有効になっている。
これらはすべて正しいが、依然としてメトリクスが表示されない場合は、デバッグ出力をチェックできます。
$ heroku run bash
$ BARNES_DEBUG=1 DYNO="web.1" bundle exec puma -p ${PORT:-3000}
警告 Procfile 内のものと同じコマンドを使用してアプリケーションを起動してください。
アプリケーションが起動されてから数秒待機すると、デバッグ出力が標準出力に表示されます。
{"barnes.state":{"stopwatch":{"wall":1561579468665.421,"cpu":8387.169301},"ruby_gc":{"count":10,"heap_allocated_pages":2451,"heap_sorted_length":3768,"heap_allocatable_pages":1317,"heap_available_slots":999027,"heap_live_slots":930476,"heap_free_slots":68551,"heap_final_slots":0,"heap_marked_slots":921621,"heap_eden_pages":2451,"heap_tomb_pages":0,"total_allocated_pages":2451,"total_freed_pages":0,"total_allocated_objects":3068719,"total_freed_objects":2138243,"malloc_increase_bytes":2716264,"malloc_increase_bytes_limit":30330547,"minor_gc_count":6,"major_gc_count":4,"remembered_wb_unprotected_objects":11936,"remembered_wb_unprotected_objects_limit":23872,"old_objects":898986,"old_objects_limit":1797974,"oldmalloc_increase_bytes":23976536,"oldmalloc_increase_bytes_limit":58823529}},"barnes.counters":{"Time.wall":10096.75341796875,"Time.cpu":11.80766799999947,"Time.idle":10084.94574996875,"Time.pct.cpu":0.1169451952643102,"Time.pct.idle":99.88305480473569,"GC.count":0,"GC.major_count":0,"GC.minor_gc_count":0},"barnes.gauges":{"using.puma":1,"pool.capacity":40,"threads.max":40,"threads.spawned":40,"Objects.TOTAL":999027,"Objects.FREE":68593,"Objects.T_OBJECT":72287,"Objects.T_CLASS":25522,"Objects.T_MODULE":3083,"Objects.T_FLOAT":14,"Objects.T_STRING":401533,"Objects.T_REGEXP":3051,"Objects.T_ARRAY":78539,"Objects.T_HASH":44655,"Objects.T_STRUCT":2244,"Objects.T_BIGNUM":148,"Objects.T_FILE":13,"Objects.T_DATA":44890,"Objects.T_MATCH":78,"Objects.T_COMPLEX":1,"Objects.T_RATIONAL":765,"Objects.T_SYMBOL":2470,"Objects.T_IMEMO":241182,"Objects.T_ICLASS":9959,"GC.total_allocated_objects":9474.0,"GC.total_freed_objects":36.0,"GC.count":10,"GC.heap_allocated_pages":2451,"GC.heap_sorted_length":3768,"GC.heap_allocatable_pages":1317,"GC.heap_available_slots":999027,"GC.heap_live_slots":930476,"GC.heap_free_slots":68551,"GC.heap_final_slots":0,"GC.heap_marked_slots":921621,"GC.heap_eden_pages":2451,"GC.heap_tomb_pages":0,"GC.total_allocated_pages":2451,"GC.total_freed_pages":0,"GC.malloc_increase_bytes":2716264,"GC.malloc_increase_bytes_limit":30330547,"GC.minor_gc_count":6,"GC.major_gc_count":4,"GC.remembered_wb_unprotected_objects":11936,"GC.remembered_wb_unprotected_objects_limit":23872,"GC.old_objects":898986,"GC.old_objects_limit":1797974,"GC.oldmalloc_increase_bytes":23976536,"GC.oldmalloc_increase_bytes_limit":58823529}}
出力に pool.capacity
キーが存在することを確認してください。
メトリクスのプラグインは、正しく設定されている PORT
環境変数に依存します。それが環境から正しく読み取られていることを確認してください。
$ heroku run bash
~$ PORT=12345 rails runner "puts 'port is ' + ENV['PORT']"
port is 12345
一部のアプリケーションは、PORT 環境変数を変更する可能性がある bin/proxy bundle exec rails s
などのプロキシスクリプトよりも遅れて実行されます。PORT
を変更するプロキシよりも遅れてアプリケーションが実行される場合は、元のポート値をエクスポートするようにプロキシスクリプトを変更する必要があります。
たとえば、プロファイルに次の内容があるとします。
web: bin/proxy bundle exec rails s
これを、元のポートを記録するように変更できます。
web: PORT_NO_PROXY=$PORT bin/proxy bundle exec rails s
アプリケーションではこの環境変数を使用します。
require 'statsd'
Barnes.start(statsd: Statsd.new('127.0.0.1', ENV["PORT_NO_PROXY"]))