Mercari Engineering Blog

We're the software engineers behind Mercari. Check out our blog to see the tech that powers our marketplace.

バッチプログラムの運用と監視について検討しよう

こんにちは。メルペイでバックエンドソフトウェアエンジニアをしている id:koemu です。

バッチプログラムのお話、今回は運用・監視についてお話したいと思います。当社はすべての業務が24時間行われていますので、システムがオンラインのときに動作するバッチプログラムについてのみ議論します。

過去の記事はこちらにあります。

tech.mercari.com

tech.mercari.com

運用に備えて

バッチプログラムの運用について、「プリモーテム」「実行管理」そして「ログ管理」の3点について述べていきます。

プリモーテム

ポストモーテムという言葉を聞いたことがある方はいらっしゃるかと思います。ポストモーテムとは、GoogleのSRE本の15章*1によれば、障害などの失敗を振り返り、今後に活かすプロセスの総称と捉えることができます。

さて、プリモーテム(プリモータム)とは何でしょうか。この言葉は、私が最近読んだThe Manager’s Path*2*3で使われていた言葉をちょっと拝借しました。本エントリでは、運用に入るバッチプログラムがトラブルを起こして、お客様にご迷惑をおかけする、すなわち障害に至った場合、何が起こるのかを事前に想定し、運用に備えるものとして定義します。言い換えれば、プリモーテムを行うことで、実際に障害が起こった際にでも冷静かつ的確に障害対応に当たれるようにします。

私は、以下の3点を留意して備えます。

  • 障害が起こるとしたら、プログラムのどの箇所か。特に、外部との入力・出力部分そして計算ロジックは重点的に押さえる。
  • 障害が起こるとき、データが壊れないようになっているか。万一壊れるとしたらどのように壊れ、復旧させることができるのか。
  • 障害発生から対応、そして復旧までにどの程度の猶予時間があるか。猶予があるとその分だけ精神的に無理のない対応が可能。

プリモーテムができるのは、おそらく開発が佳境に入った段階であることがほとんどです。おそらくスケジュール的にしんどいときであると想像します。私もそうでした。それでもなお、プリモーテムをするメリットは、未来の自分を救うことになる可能性がある備えだからです。

実行管理

バッチプログラムの実行スケジュールをどのように管理するか、です。

原始的な方法ですとUNIX系OSならcron、Windowsならatコマンド等があります。より高度になりますと、ジョブ管理ソフトがあり、OSSならRundeckなど、商用ならJP1 Automatic Job Management Systemなどがあります。

ジョブ管理ソフトを使うメリットとして、次の5点が挙げられます。

  • 高度なスケジュールパターンを組める
  • バッチプログラム(ジョブ)の排他制御や依存関係を組みやすい
  • 大量のジョブが登録されたときに参照・管理がしやすい
  • ジョブの実行状況の可視化が容易
  • ジョブの再試行が実施しやすい

また、実行できない状況についても想定しておきましょう。例えば、サービスの計画メンテナンスが発生する場合、多くの場合はバッチプログラムもその間停止しなくてはなりません。その際、実行できないことでサービスに影響が出るのか、出なかったとしてもメンテナンスが終わるときに改めて実行が必要なのかを検討しておきます。

ログ管理

まず、今、あなたがバッチプログラムのstderr/stdoutを /dev/null に捨てているのなら、今すぐやめてください。自分たちで障害に気づけない可能性が極めて高い、良くてもノーヒントで障害対応を始めなければならないからです。

さて、私があるイベントで聞いて感銘を受けた言葉があります。「計画していないログは出ない」です。障害が起こったときに、原因を探る際にまず読むのがログであることは論を待ちませんが、そのログに何を記録するかは開発者自身が決めなくてはなりません。

何もしていない場合は、多くのプログラミング言語では、エラーが発生するとstderrにスタックトレースが流れ、どの行で問題が発生したかが示されます。もちろんこの情報は再現性が高い問題であれば有用です。しかし、特定のお客様やデータパターンでしか再現しないケースですと、これでは不十分です。

そこで、計画的にログ出力を定義する作業が必要になってきます。以下のように入れてみてください。

  • プリモーテムで障害発生が想定されたプログラム周辺に、エラーとなった場合に記録。
  • エラーの種類を詳細に記録。ケースごとに例外が細分化されていたり、エラーコードが定義されていると、障害の切り分けが行いやすい。
  • どのトランザクションかが一意に判定できるとなお良い。

注意点として、ログに誤ってお客様の個人の情報、例えばメールアドレスなどを、ログに混ぜたりしないようにしなければならないことです。メールアドレスがバリデーションエラーであったことをログに記録する際、そのメールアドレスが記録してしまうと、セキュリティホールになる可能性があります。

どう監視するのか

バッチプログラムの監視について、「監視難易度を理解する」「監視のしかた」そして「通知のしかた」の3点について述べていきます。なお、監視はバッチの実行の観点に絞っていき、メトリックの監視についてはここでは触れません。

監視難易度を理解する

バッチプログラムの監視の難易度は、難しい順に、開始>正常終了>実行時間が突き抜けている>異常終了、です。通知のしかたは後述します。

まず、「開始」の監視は、最も難しいものです。なぜなら、開始のイベント自体が発生しないと、何も通知は起こらず、誰も気づけないからです。例えば、バッチサーバ自体が障害を起こしてバッチプログラムが起動してなかったことをどうやって知るかというのは、なかなか難しい問題です。では起動時に毎度通知を流す方法が良いのかといえば、そうではありません。日に1回ならSlackなどに通知を流し、それを目で追うことはできるかもしれません。しかし、これがより高い頻度で起動していたら、慣れからいつの日かそのログの監視を忘れ、またその後で起動しない障害に遭遇してしまう可能性は否定できません。

次に、「正常終了」はシンプルで、異常なく終了した旨を記録すればよいと思いがちです。しかし、次に続く「実行時間が突き抜けている」を定義できていないと、設計した時間内に正常に終了したかを正しく記録できない可能性があり、注意を要します。

そして、「実行時間が突き抜けている」場合は、あらかじめ実行時間を定義し、それを超過していれば通知を出します。

最後に、「異常終了」は、異常が発生した際に通知すると明確に定義できるため、監視の目安を最も立てやすいと言えます。

監視のしかた

バッチプログラム実行の監視のしかたについて、先の「開始」「正常終了」「実行時間が突き抜けている」そして「異常終了」の順に沿って説明します。

先節で、バッチプログラムの正常実行「開始」の監視が最も難しいことを述べましたが、ではどのように監視すればよいのでしょうか。定期実行されるバッチプログラムの場合で、最も簡単なのが、最後に実行した日時をデータベースに記録し、それを監視システムに登録した監視プログラムが一定時間更新されていないことを検知すると、通知を流す仕組みです。これの変則パターンとして、ある日時以降に更新されるべきレコードが更新されていないと通知を出す監視プログラムを作る方法もあります。

「正常終了」は、ジョブ管理ソフトやログに記録をするのみです。

「実行時間が突き抜けている」かの監視は、ジョブ管理ソフトに実行時間監視の機能がある場合はそれを活用します。そうでなければ、バッチプログラムに、実行時間が突き抜けたときは通知をする仕組みを導入します。ここで気をつけたいのは、ジョブ管理ソフトならハングアップを伴う実行時間の突き抜けが監視できるのに対し、バッチプログラム本体に導入したログだけですとそれが難しいことです。もし、ハングアップを伴う突き抜けを監視したい場合は、やはり実行の履歴をデータベースに取り、それを監視することをおすすめします。

最後に、「異常終了」は、バッチプログラムがexit 0以外のコードで終了し、cronやジョブ管理ツールにstderrの結果を返すケースがほとんどです。ここで、stderrには異常終了の原因を端的に、ログには障害原因を追跡するための記録を保存します。例えば、stderrには、例外のオブジェクト名を返し、ログにはスタックトレースおよびプリモーテムで記録することが適切と想定できたデータを保存します。

通知のしかた

基本は、常にリクエストを受け取るオンラインのシステムと一緒ではあるのですが、バッチプログラムの監視には1点違う点があります。それは、「異常終了はその瞬間であり、継続的に異常状態が続くのではない。」ことです。

ここから、通知のしかたによっては、通知を見落とす可能性があります。

バッチプログラムの障害の通知は、以下の3点を押さえます。

  • 確実に通知できること
  • チームで捕捉できること
  • 状況を端的に理解できる内容を送ること

まず、「確実に通知できること」は、PagerDutyなどの確実にオンコールが受け取れるサービスを利用したり、監視システム/サービスを利用します。Slackのmention/@channelなどで通知してもよいのですが、その通知だけだと多くの場合は1回流れるだけで終わるため、休日や夜間に実行されるバッチプログラムの場合は見過ごす可能性があることを折り込みましょう。

次に、「チームで捕捉できること」は、オンコールが適切に回せていることはもちろんですが、もし当番が拾いきれなくとも、チームの誰かがそれを拾える状況も作っておくとよいでしょう。その際にSlackの@channelは役に立つことがあるかもしれません。実際、私も平日の日中に会議をしている際、同僚に通知を拾ってもらって対応を開始できたことがありました。

最後に、「状況を端的に理解できる内容を送ること」です。業務時間中ならPCの前にいてすぐにログを開ける状況かもしれませんが、夜間・休日のオンコールですと最初に手にとるのがスマホの可能性は十分にあります。そのときに、どこで問題が起きたのかわかりやすい通知があると、PCを開くまでのわずかな間でも、どのように問題に対処すればよいか、考慮する時間が取れます。

おわりに

ここまで、バッチプログラムの運用・監視について述べてきました。異常の原因を探る原則はログを参照することですが、そのためにはプリモーテムを行い、適切なログが出力されている状態を作ることが前提であることをお話しました。

障害が起こることは辛いのですが、その障害自体を起こさない、起こしてもすばやく収束させていくのも、また自分たち自身です。私もがんばります。

それでは皆様、ごきげんよう。