2022年11月21日(月)~22日(火)に「CloudNative Days Tokyo 2022」がハイブリッド開催されました。GMOインターネットグループはダイヤモンドスポンサーとして協賛・登壇しました!今回は、CFPセッション「しきい値監視からの卒業!Prometheusによる機械学習を用いた異常検知アラートの実装」の書き起こし記事となります。ぜひご覧ください。イベント告知:https://developers.gmo.jp/25932/
登壇者(敬称略)
GMOペパボ株式会 プリンシパルエンジニア高橋 拓也(@takutaka1220)
「しきい値監視からの卒業! Prometheus による機械学習を用いた異常検知アラートの実装」
https://speakerdeck.com/takutakahashi/sikiizhi-jian-shi-karanozu-ye-prometheus-niyoruji-jie-xue-xi-woyong-itayi-chang-jian-zhi-aratonoshi-zhuang
「しきい値監視からの卒業!Prometheusによる機械学習を用いた異常検知アラートの実装」と題しまして、GMOペパボの高橋 拓也が発表させていただきます。
今回のアジェンダはこのようになっております。
自己紹介
まず軽く自己紹介させてください。私はGMOペパボでインフラエンジニアをやっております高橋拓也と申します。コードを書くこととKubernetesが好きで、自宅サーバを飼っていることが自慢です。たくさんあります。宣伝です。GMOペパボでは今回のCNDT2022で合計5セッション登壇しております。もうすでにすべて終わってしまっているので、後日アーカイブが放送されますのでそちらをご覧ください。
それではアジェンダの続きにいきます。
監視どうしてます?
「監視どうしてますか?」という話です。大クラウドネイティブ時代においてもなくならないもの、それは監視です。いろいろな監視手法がありますね。外形監視したりメトリクスを監視したりとか。監視対象もたくさんあります、CPUとかレスポンスコードとかプラットフォームが動いているかとか、いろいろあります。監視はめちゃめちゃ大変ですよね、これは皆さん同意していただけるかなと思います。
では監視するものっていうのはどういうものがあるのか、だいたいこんな感じかなというのがあります。システムのメトリクスだとCPU使用率とかメモリ使用率などが想定よりも高くないか、あるいは低過ぎないか、あとサービスメトリクスに関してはステータスコードが200が多いか404が多いか、500が出ていないかとか、リクエストレイテンシに関しては遅過ぎないかとか、そういったようなことを監視します。この想定より高くなっていないか低くなっていないかといった部分に関しては、主にしきい値を用いてアラートを発報する手法がよく行われます。これをしきい値アラートと呼んでみます。特定の基準を超えた場合にアラートを発報する手法です。例えばCPU使用率が80%を超えたらアラートを出すとか、レスポンスタイムが2.0秒を上回ったらアラートを出すとか、そういった手法です。
では、このしきい値はどうやって決めていくのかといいますと、平常時の負荷から決めていくことが一般的だと思います。平常時のCPU使用率がだいたい40%で、高くても50%だから60%にしよう、そういったようなことをよくやられると思います。そういう場合、だいたいピーク時に60%を実はちょっとだけ超えてしまってアラートが頻発してしまうとか、繁忙期にはもうベースが50%を超えてしまって、ずっとアラートが出ているとか、そういったことが起こりがちと思います。そういう場合は、しきい値を見直していくと思うのですけれども、しきい値を都度見直していくのってすごく大変です。何回やるのだっていう感じになってしまいます。ではしきい値をもともとどういう理由で定めていたかといいますと、その普段の挙動がどういった挙動でそれと違う挙動を見つけるためにしきい値を決めていた、そうですね。このいつもの挙動、というものを発見する、そして違う挙動を発見するといった手法に異常検知という手法があります。こちらを今回ご紹介したいと思います。
異常検知アラートとは
「異常検知」という言葉は聞き慣れない言葉の人もいるかなと思いますが、Anomaly Detectionという英語で表現されます。何らかの方法で通常の動作というものを定義します。そして通常の動作の範囲内とする幅を指定します。今ここの下のグラフに映っている灰色の幅、これが通常の動作の範囲内としている幅です。その幅を超えたら異常であるとして検知をする、そういった手法になります。この赤いところ、これが異常だと判断されているポイントです。DataDogには「異常検知モニター」という名前でこの異常検知アラートが提供されています。DataDogに送られているすべてのメトリクスに対して、この異常検知モニターは設定が可能になっています。さらに皆さん大好きなAWS、こちらに関してもCloudWatchのmetrics/alarmで利用することができます。このように、プルダウンから簡単にパッと選んだらパッとこのようなアノマリーバンドが出てきて、AWSの監視はこれだけで実はよいのではないかというほどすごいと思っている機能です。パブリッククラウドいいなあ、AWSいいなあ、と思います。オンプレにも異常検知アラートが欲しい…オンプレ向けの異常検知アラート…オンプレ異常検知…監視…Cloud Native…Prometheusだ!ということで、Prometheusをデータソースとした異常検知を実装することができないか、と考えました。
Prometheusを利用した異常検知アラートの実装
Prometheusを使用した異常検知アラートの実装の中身についてみていきましょう。異常検知ってそもそも何をやったらできるのかという問いに関してですが、異常検知というものは時系列データの予測という問題に置き換えることができます。既知のデータを使って未来の値を予測する、これが時系列データの予測になっています。そして、実際にメトリクスとして収集された値と、予測した値、これを比較することによって、異常であるかどうかというのを見ることができるようになります。時系列データというものに関しては、この時間軸に沿って並べられたデータのことを指します。例えば年間の気温の推移とか、株価の推移とか、そういったものが時系列データですね。システムのメトリクスも時系列データといえます。Prometheusに格納されるデータは基本的にすべてが時系列データとなります。
ではこの既知のデータを使って未来の値を予測する、この方法について研究していきます。時系列データの予測モデルとしてはいくつか存在します。その有名なものの中にこの2つ、ARIMAとLSTMというのがあります。まずARIMAモデルに関しては、Auto-Regressive Integrated Moving Averageの略で、自己回帰(AR)と差分(I)、移動平均(MA)、この3つを組み合わせたモデルのことを指すみたいです。DataDogの異常検知モニターでも利用されており、結構枯れた古典的なモデルだと評価されているみたいです。対してLSTMは、Long Short Term Memoryの略で、リカレントニューラルネットワークというものを使った深層学習、いわゆるディープラーニング、そういったものらしいです。今回はARIMAの方を採用して予測モデルを作ってみました。
ARIMAを選択した理由としては、計算量が少なくて済む点がよいと思ったからです。構築済みモデルに適切な次数、パラメータを与えるだけで使えるモデルとしてそこそこ精度が出るものができるみたいでした。あとはGPUを使わなくて済むからLSTMよりやりやすいというのがあります。さらに、採用実績は実は結構豊富で、先ほどDataDogでもARIMAモデルは使われていると言及しましたが、詳しくはSeasonal-ARIMA、季節性ARIMAモデルというものを使っています。今回、以下この季節性ARIMAモデルのことをARIMAと指しておりますが、使っているのは同じものです。さらにBigQuery MLの時系列予測モデルとしても、実はARIMAモデルが使われている、かなり豊富に世の中でよく使われているものみたいです。
それでは早速このARIMAを使った時系列予測の手順について解説していきたいと思います。主にこの6手順をやっていきます。
環境を用意するデータを用意するデータを整形するデータをテスト用と学習用に分ける問題に最適なモデルを探索する探索してできたモデルに対して結果をテストデータで確認する
この6手順です。
まず環境を用意します。機械学習といえばPythonということでPythonを使います。そしてあれこれ試行錯誤するために、Jupyter Notebookというものを使います。そしてモデルを使うためのライブラリとしてpmdarimaというものを使っていきます。Jupyter Notebookについて聞いたことない方もいらっしゃるかと思うのですけれども、特定のスニペットを実行したりとか、グラフをシュッと表示してくれたりとか、結構便利なインタープリターというような感じです。VS Code Extensionが実はかなりよくできていて、環境構築までやってくれてめちゃくちゃ便利なので、ぜひ、なんだこれは?と思った人は使ってみてください。そしてデータを用意します。とあるKubernetes PodのCPUの使用率を利用しました。だいたいこんな感じの周期的なデータで、予測しやすそうだというデータを用いました。PrometheusからREST APIを利用してデータを取得します。こんな感じで書いて、そうするとunix timeとvalueの配列を得ることができます。さらにこのデータに関してですが、データの数を減らして計算しやすい周期にするということが必要になってきます。例えば、この左のデータはPrometheus APIのstepパラメータ=600というのを指定したもので、結構ギザギザしていますが、右のデータ、step=3600、3,600秒につき1個のデータ数というものを使っても、これでも十分周期が表現できているので、予測ができそうということで、今回は3,600秒=1時間に1個のデータを使って予測することにしました。データを学習用とテスト用に分割します。これは過学習というものを防ぐためです。テスト用データで学習をしてしまうと、これは一種のカンニングになってしまうので、学習用の時は学習用のデータだけを使おうというのが定石となっております。これはpandas.Seriesという形を使っていろいろ設定して、このようにpmdarima提供のメソッドに入れてあげると、なんとシュッと分割してくれる結構便利なメソッドがあります。
分割した結果がこれで、赤が学習用、青がテスト用のデータになっています。時系列データでは未来のデータを使って過去のデータを予測することは基本的にないので、過去のデータを使って未来のデータを予測します。つまり、赤の学習用のデータが先に来て、青のデータがあとに来るというような感じになります。
それでは、最適なモデルを探索していきます。データによって適切なモデルへの入力が異なります。ARIMAモデルでは以下の入力を必要とします。pとdとq、それぞれ自己回帰と差分と移動平均のパラメータを表すものです。こんな感じで表現して、さらにSeasonal-ARIMAの場合はARIMA自体の周期というかARIMA自体の次数でまたもう1個次数、P,D,Qがあります。そして周期を表すmを用いてこんな感じで、この赤字で示した感じで表記します。このパラメータの意味するところは正直私にはよく分からないので、お近くに機械学習に詳しい方がいらっしゃったらお聞きくださると非常に助かります。
では、これらのデータを埋めていきます。差分のd,Dの部分は、1が一般的らしいので1を使っていきます。なぜかは聞かないでください。mは季節周期なので、データによって個別に設定いたします。今回はここの部分がmになってきます。なので1日周期です。1日周期なので、m=24となります。それで残ったのがp,Pとq,Q、この2つを埋めていく必要がありますが、ここでpmdarimaのすごいメソッドが登場してきます。auto_arima()、”自動arima”というメソッドがなんとあります。最適な次数を出してくれる最強メソッドで、総当たりでモデルを作成してその中で一番結果のよいものを自動的に選ぶような挙動をします。そのモデルの評価には赤池情報量基準(AIC)というものを使っていきます。これも何かは聞かないでください。こんな感じで表示があるのですけれども、AICが小さいほどその結果がよいモデルというふうに認識します。最終的にこの(1,1,1)(2,1,1)というARIMAのモデルが選択されました。このように(1,1,1)(2,1,1)の次数が選ばれました。これが一番よいモデルになります。
これで問題に最適なモデルを探索することができたので、最後、テストデータで結果を確認していきます。今回はmodel.predict()というメソッドを使って予測の数値を出していきます。このn_periodsは、そのpredicateの長さを表します。今回はテストデータの長さ分を出します。そしてもう1つ、return_conf_intというものにTrueを入れていますが、これは何かというと、信頼区間の配列をリターンするかどうかのパラメータとなります。信頼区間というのは統計的に真の値が含まれる可能性の高い範囲を指します。このグラフでいうところの灰色の部分、これが信頼区間になります。信頼区間のminと信頼区間のmaxが2つデータとして出てきます。実績値がこのminとmaxの間だったら予測は当たりとなります。
最終的に同じグラフに対して予測データとテストデータをプロットしてみました。こんな感じで、よい感じに予測できているのではいう結果が出ました。
作成したモデルで予測APIを実装する
作成したモデルで予測APIを実装していきます。アラートが出せるようなAPIとして実装していきたいので、メトリクスとしてexporterの形式でレスポンスを返せてPrometheusが収集監視できるとよさそうだなと思ったので、exporterとして実装していきます。GitHubの私のリポジトリのpromadというプロダクトとして実装しております。引き続き pmdarimaを使うためにPythonで実装しているexporterになります。
それでは、最適な次数を手に入れたのでそれを入力値として持っていきます。rules.yamlにクエリとそれに対するARIMAの次数を指定して、yamlを書いてあげてこれを入力とします。次数が既知の場合はpmdarimaのメソッドを使ってモデルを作成することができます。このようにfit()とやると学習させることができて、モデルが出てきます。探索するよりも計算量が少なめになります。orderはtupleなのでliteral_evalメソッドを使ってstrをtupleにしてあげます。そしてfit()の入力の部分にはPrometheusをクエリしたデータを入れてあげます。このquery_range()メソッドでラップしていますが、これはクエリの結果になります。これが実際のメトリクスとして出している部分ですが、大まかにどんなことをやっているかというと、model.predict()、先ほど使ったメソッドで予測値と信頼区間を出して、現在時刻と一番近い時刻の予測値を出して、現在時刻の実績値を出して、現在値が信頼区間に入っているかを計算して、Prometheusがscrapingできる形式に変換してレスポンスしてあげる、こういった流れになります。大事なのはこの部分、現在時刻と一番近い時刻の予測値を出すという部分ですが、なぜこんなことをやっているかというと、Prometheusの制約として過去や未来の時刻のメトリクスをPrometheusは入れることができません。その収集したタイミングの時刻のデータとして、Prometheusは認識します。なので、モデルを作成した時に過去2~3時間前までのデータを使ってモデルを作って、そしてscrapeした時に予測値を出して、その3個目の値、2時間前までのデータを使ったら2時間後の値を出すと現在時刻のデータになる、そしてそれを収集させる、そういったことをさせる必要があります。こんな感じで、現在時刻が例えば深夜0時だったとしたら、現在時刻の2時間前、10時からのデータを使ったら11時と深夜0時のデータが含まれてきて、この一番近い予測値を出してきてそれを実際に使うというようなことをやる、という感じです。そして最終的に exporterとして収集できる形式で予測値を出力します。で、Prometheusにscrapeさせて完成となります。この紫の線(図では赤線と書いてありますが)が実績値で、その上下の線が信頼区間となって、実際、実績値の間に信頼区間、実績値が信頼区間の間に入っているので概ねよさそうな感じに見えます。そして、最終的に異常検知したかどうかというメトリクスとして、anomaly_detectedメトリクスを作りました。これが0か1かで、1だったら予測から外れた、anomalyがdetectedされたというような表現にして、これを監視しているだけである程度よい監視ができるようなメトリクスにしてみました。
異常検知APIの課題
それでは最後に、異常検知APIの課題をお話しいたします。なんと、これだけではしきい値監視を卒業できないということに気付いてしまいました。タイトル詐欺をしてしまったのですけれども、結果そうなってしまったという感じで、課題について言っていきたいと思います。
まず1つとしては、リソース使用量がとても多いという課題が挙げられます。データ間隔が1時間のメトリクス1つに対して、2.8GHzのCPUにリソースリクエストとして2000m割当を使ってそれを100%消費されて、だいたい1分ぐらい1つのモデルを作るのに時間がかかってしまいます。さらに傾向が変わるとモデルを作り直さなければいけなかったり、時間経過によってモデルの鮮度が落ちてしまって作り直さないと精度のよいモデルにならなかったり、そのようなことがあるので、結構何回もモデルを作る必要がありました。すべてのメトリクスを異常検知モニタリングするには、リソース使用量の削減の工夫が必要そうだなと思いました。
さらに、苦手な時系列が存在するということに気付きました。先ほどのメトリクスのような一定周期のものは得意ですが、例えば単調増加とか徐々に増加していく、こういったグラフは変化に追従していってしまうため苦手だ、ということが分かりました。Seasonal-ARIMAが苦手というだけなので別の手法を使えばよいのですけれども、CloudWatchはこの点12,000以上の内部モデルにルーツを持っているというふうに書いてあるのでめちゃめちゃがんばっているんだなぁという感想は得ました。すごいなあと思いました。
終わりに
異常検知の道はまだまだ続くよ!ということで、実際に実装してみて有用性は確認することができました。しかし、既存のしきい値監視の置き換えはまだデータ量や計算量の問題で難しそうだということが分かりました。さらにこのpromad自体の実装としてはまだまだいくつかやることがあるので、それをやっていきたいと思いました。そしてインフラ課題をこのようないろいろな技術を使って解決していきませんか?We are HIRING!!!ということで、ぜひ弊社に応募していただけると嬉しいです。
ということで、今回の発表をおしまいにしたいと思います。ご静聴ありがとうございました。
アーカイブ映像
映像はアーカイブ公開しておりますので、まだ見ていない方、もう一度見たい方は 是非この機会にご視聴ください!
https://youtu.be/pjvyqWwrbzA