こんにちは。エムスリーエンジニアグループAI・機械学習チームで2021年新卒の北川(@kitagry)です。一人でtechblog書くの久しぶりなのでちょっと緊張気味です。
最近はRustでKubernetesのカスタムコントローラーを作るのにハマっています。他にも作っている方がいれば知見を教えて欲しいです。
今回はチーム内のJobの監視について作成したので、何を作成したかとなぜそういうものを作成したかについて書いていきたいと思います。
AI・機械学習チームのサービスの特徴
AI・機械学習チームはその名の通り機械学習を用いたプロダクトがたくさんあります。 そしてその多くはGKE上でCronJobとして動いており、各々のJobは弊社謹製のツールであるgokartで動いています。 2021/11現在CronJobの数を数えてみたところ88個のCronJobがありました。 AI・機械学習チームは現在14人ほどのチームなので、単純計算で一人6個のJobを監視しないといけません。 恐ろしいことですね。
この事態をなんとか改善しようと、チーム内でrunbook委員会が立ち上がりそれぞれのプロダクトのrunbookを充実や監視体制の強化することにしました。1 このrunbookの取り組みについてはまた何かの形でアウトプットできたらいいなと思っています。
監視の話に戻ります。
チームの監視上の課題
チームでは、今まではGKEのJobの失敗イベント(BackoffLimitExceeded, OOMKilling)の取得やログのTraceback情報をslack通知することによって監視してきました。 しかし、この情報だけでは対応できないものがいくつか発生していました。
その例を1つ紹介します。 弊チームでは複数の機械学習のバッチを定時で回しており、あるJob Aの実行結果を使ってJob Bの学習をすることがしばしばあります。 しかし、サービスを長らく運用しているとデータの増加に伴い、徐々に学習時間が伸びることによって、Job Aの終了時間がJob Bの開始時間を追い越してしまう事件が発生しました。 しかもJob Bは数ヶ月分のJob Aのデータを使用しているため問題の発覚に数ヶ月かかりました。 Job AもJob Bもエラーを出しているわけではないので、既存の監視ではこれらのエラーを検知できません。 このような障害の再発防止に向けて、新たな監視が必要になりました。
この問題の原因は依存しているJobが何時に終わるかという保証がなかったことにあります。 そこで、今回はそれぞれのJobが終わるべき時間をSLI(サービスレベル指標)として定め、その時間内にJobが終了しているかを確認するために以下の2つを監視しました。
- BigQuery監視
- CronJob監視
これらの監視について、詳しく述べていこうと思います。
BigQuery監視
弊チームでは、ある機械学習の実行結果をBigQueryに保存して、その保存した情報を他のサービスで使うという運用をしばしば行っています。 このサービス間が連携しているBigQueryテーブルが作成されているかを監視することによって、SLO時間を守れているかを監視することにしました。
監視ツールの作成に当たっては当ブログで以前書かれた「BigQueryのテーブル連携時間を監視する」のOSSのツールを用いて、スプレッドシートで連携して通知するシステムを作成しました。 このツールはブログで書かれてから監視に導入まで至っておらず、今回を機に導入に至りました。 このツールを用いることによって、GKE上に載っていないJob(実はGKE以外のJobを含めると、100個程のJobがあった)でもSLO時間までにテーブルが作成されているかの監視ができます。
また、SLO時間の30分以内に終了していないJobについてはWarningを出すことによって、上記で説明した徐々に遅くなっているJobを事前に検知できるようになりました。 それによって、Warningが出たJobは開始時間を早めるなど、余裕を持って対策ができます。
スプレッドシートで連携することによって、どのサービスがどの時間に終わるかをすぐに確認ができるようになりました。 また、監視もされているのでドキュメントが腐ることの防止もできていると思います。
また、スプレッドシートでいつ終わることを保証するかという管理になり、外部連携の際にSLOとして使うことができるという副産物を得ることができました。
BigQuery監視で解決したこと
- 徐々に遅くなっているJobを早期に発見できるようになった
- スプレッドシートでテーブルがいつ作成されるかの把握が楽になった
- そのテーブルを使うJobを作成しやすくなった
CronJob監視
BigQuery監視によって、チームにSLOを導入し監視の質を向上させることができました。 しかし、弊チームのJobでBigQueryに結果を保存しているものは1/4しかなく、残りのものはプロダクトによってCloud SQLやGCSやそもそも出力しないなど多岐に渡っていました。 これらのプロダクトについてはまだSLOの監視ができていないままでした。 しかも、BigQuery監視によって意外と時間通りに動いていないJobがあることがわかりましたが、Cloud SQLやGCSなど1つ1つについてちゃんと出力されているかを監視するものを作成するのは現実的に不可能でした。 そこで、出力を監視するのは諦め、CronJobが決まった時間内にちゃんと成功しているかを監視するツールを作成することにしました。 これはBigQuery監視のようにちゃんと結果が出力されているかのチェックは出来ませんが、BigQuery監視ツールと同様にそもそも動くべきなのに動いていなかったケースの検出には役に立ちます。
ロジック
ここからはどのように監視ツールを作成したのかについて書いていきます。 KubernetesのCronJob Controllerは以下のようにJobの作成、Jobの正常終了・異常終了などのイベントを吐き出してくれます。
$ kubectl get events | grep cronjob LAST SEEN TYPE REASON OBJECT MESSAGE 8m55s Normal SuccessfulCreate cronjob/hello Created job hello-27210634 8m51s Normal SawCompletedJob cronjob/hello Saw completed job: hello-27210634, status: Complete 7m12s Normal SuccessfulCreate cronjob/hello Created job hello-27210636 63s Normal SawCompletedJob cronjob/hello Saw completed job: hello-27210636, status: Failed
これによって、CronJobのそれぞれについて実行と終了のログを取得できます。 あとは、決められた時間までにJobが正常に終了したかどうかのイベントがあるかを見るだけです。
簡単なインフラについて説明します。
イベント情報は上記のようにKubernetesのAPIサーバに問い合わせることで取得が可能です。 しかし、注意すべきポイントとして、Kubernetesのイベントの存続期間はapiserver起動オプションで指定されており、デフォルトでは1時間になっています。2 そのため、イベントは別の場所に保存するのが良いです。 今回はCloud LoggingからBigQueryにエクスポートすることにしました。
あとは監視時間を設定したスプレッドシートとBigQueryからデータを読み込んで、決められた時間までに終了していないイベントについてアラートを出すという構造になっています。
簡単な図を以下に示します。
よもやま話
このCronJob監視ツールを作成したときは忘れた頃に役に立つツールなんだろうと思って作成していました。 しかし、いざデプロイしてみるとある1つのCronJobがアラートされました。 ツールにバグがあったのかなと思って見てみると、5日前からPodがなぜかずっと動きっぱなしで以降新しいJobが作成されていないCronJobを発見しました。
しかも、そのCronJobは自分が担当しているプロジェクトの1つだったので、かなり冷や汗ものでした。 このツールがなかったら、いつ気づいていたかわからないので本当によかったです。
CronJob監視で解決したこと
- BigQuery監視では監視しきれないJobについての監視体制を整えることができた
最後に
今回はチームの課題であった監視について、JobにSLOを設けてそれらが満たされているかを監視する2つのサービスを作成しました。 それによって、今まで発見できなかった種類のエラーについて知ることができました。 今後もこれらの監視ツールの精度を上げて、より良い監視をしてチームメンバーの快眠を守っていけたらいいなと思っています。
We are hiring!!
現在弊チームでは様々なサービスが動いており、現在も着実に増えています。 そこでインフラや運用の観点がますます大事になっていくと思います。 これらのことに興味あるかたと一緒に働きたいです!!
-
プロダクションレディマイクロサービスのP142. https://eng.uber.com/software-engineers-on-call-dashboard/↩
-
https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/ の
--event-ttl
オプションで設定可能↩