モノリシックなRailsアプリケーションで、APIのエンドポイント毎にSLOを設定する

こんにちは、@r_takaishiです。今回は、モノリシックなRailsアプリケーションが提供するAPIについてエンドポイント毎にSLOを設定できるようにしたので紹介します。

解決したい問題

ReproではRailsアプリケーションが様々なAPIを提供しています。このとき、APIのAvailabilityやLatencyについて可視化して障害が起こっていないか、パフォーマンスが低下していないかを調べることがあります。また、APIについてSLOを設定し、サービスの信頼性を保ちつつ開発を行うこともあるでしょう。

Reproでも可視化やSLOの設定は行ってきました。しかし、それらの対象がALBのTargetGroup単位だったり、APIを提供するECS Service単位でした。このような単位だと、API全体についての状況は分かりますが、個々のAPIについての情報は得られません。例えばエンドポイントによって利用するミドルウェアが異なっているとAvailabilityやLatencyの性質も異なるでしょう。それぞれのエンドポイント単位で可視化され、SLOが設定されているとより適切な対処を行うことが可能となります。

そこで今回、エンドポイント毎のメトリクスをDatadogに送り、個別にSLOを設定できるようにしました。

どのように実現したか

全体像は下図の通りです。メトリクスの元となるデータはALBのログです。ログはS3 Bucketに保存されるようになっています。ログが保存されるとEventBridgeで検知し、Lambda Functionを実行します。Lambda Functionはログファイルを読み込み、メトリクスとしてDatadogに送ります。

ここで動いているLambda Functionは reproio/send-alb-metrics-to-datadog です。send-alb-metrics-to-datadogは読み込んだログからSLOを設定したいパスのみDatadogにメトリクスを送っています。

Lambda Functionの設定例です。メトリクス名や、メトリクスとして収集するエンドポイントを設定します。また、 /api/v1/foo/123 のようにパスの一部にIDのような一定ではない値が入っている場合 /api/v1/foo/$idのように変換する機能も持っており、その変換ルールについても設定できます。

request_count_metrics_name: foo.alb.request_count
target_processing_time_metrics_name: foo.alb.target_processing_time
target_paths:
  - /api/v1/foo
  - /api/v1/bar
  - /api/v1/hoge
path_transforming_rules:
  - prefix: /api/v1/bar
    transformed: /api/v1/$id
  - prefix: /api/v1/hoge
    suffix: /start
    transformed: /api/v1/hoge/$id/start

Datadogへはディストリビューションデータとしてメトリクスを送っています。ディストリビューションとして送ることでDatadogのダッシュボードなどでp99などのパーセンタイル値を計算できたり、SLOでしきい値クエリを利用できます。

この仕組みの場合、ALBのログがS3 Bucketに保存されたタイミングでメトリクスを送ります。そのためやや遅延があり、リアルタイムにメトリクスを見ることはできません。しかし、遅延があるといってもせいぜい5分強ですので、SLO用途であれば問題なく利用できます。

下図はDatadog上に作成したエンドポイント毎のAvailabilityとLatencyのSLOです。エンドポイントで分けることでより適切な閾値を設定したり、閾値以下になった時に調査する領域が絞られたりと便利になりました。

他の方法との比較

Rails側でリクエスト毎にメトリクスを送る

Rails内でリクエストの処理終了時に実行時間などをDatadogに送り、それを用いることも可能でしょう。しかし、現時点でRailsではないサービスが一部のAPIを提供していることもあり、汎用的な仕組みの方がよいだろうと考えこの方法は採用しませんでした。

ログをDatadogに送る

DatadogにはDatadog Logsというログを送る機能があります。検証してはいないのですが、もしかすると、この機能を使うことでパス単位のSLOを作成できるかもしれません。しかし、Reproでは現在ログ管理にPapertrailを使っています。そのため、もしDatadog Logsで実現できたとしてもログ管理をDatadogに移行する必要がありました。やりたいことに対してコストが見合わないと考え、今回はこの方法は採用しませんでした。

まとめ

Railsアプリケーションが提供するAPIについて、Lambda FunctionでALBのログからメトリクスを生成しDatadogに送ることでエンドポイント毎にSLOを設定できるようにしました。Lambda Functionは reproio/send-alb-metrics-to-datadog として公開しています。同じような課題をお持ちの方は是非お試しください。