はじめに
本記事はQmonus Value Streamの投稿キャンペーン記事です。
この記事は、「なぜあなたのアラートは見られないのか」と関連しています。
この記事単体で理解することも可能ですが、概要編、理論編を読むとより深く理解することが可能です。
いいね、ストックをよろしくお願いします!
前提知識
この記事では、補助的な指標について解説します。
指標とは、ソフトウェアの品質について何らかの判定をする仕組みです。
補助的な指標とは、ソフトウェアの品質と緩やかに相関するものの、誤った判断が多い指標です。
この記事では、オブザーバビリティの考え方について解説します。
オブザーバビリティについての詳細はO'reillyのオブザーバビリティエンジニアリングを参考にしてください。
この記事では、直接オブザーバビリティに関連していないように見える部分も多いですが、より高い立場から抽象的にオブザーバビリティという概念を理解するのに役立ちます。
どうして補助的な指標は役に立っていないのか
補助的な指標は、扱いが非常に難しいです。実際、ソフトウェア開発現場で使われている指標の多くは補助的な指標以外の指標です。まずは、どうして補助的な指標が利用されないのかについて分析しましょう。
宗教化する
代表的な補助的な指標として、品質を測定する指標であるカバレッジと、複雑度を測定する指標であるコード行数(LOC)について考えてみましょう。次のような発言や考え方に心当たりはありませんか?
「カバレッジが高いから、安心だな!」
「コード行数が300行だし、そこまで複雑なコードじゃないな」
補助的な指標はあくまで目安であるにもかかわらず、指標が与えられ、それが品質の成功失敗を判定してくれると、どうしても人間はそれを基準に考えてしまいます。
特に、チーム全体が補助的な指標を盲信してしまうと、カバレッジが毎回通知されたり、コードの行数が一定以上を超えると警告がでるようになってしまいます。
そして、通知が来ることによってますます「とりあえずカバレッジさえ上げておけばいい」といった場当たり的で非本質的な思考に陥ってしまいます。
結果、「関数を特に意味なく3つに分割して平均LOCが減ったから理解しやすさが向上した!!」という謎の宗教が誕生するのです。
この現象は、特に信頼性や認知コストなどの定量化しにくい抽象的な概念を測る場合に発生しやすいです。
補助的な指標の宗教化
人間は思考を巡らせて本質的な問題を発見するコストを支払いたがらない。
補助的な指標が思考停止の口実として使えるならば、使ってしまう。
忘れられる
補助的な指標がバグ検出のために使われている次のような場面を見てみましょう。
A「ディスク使用率が90%を超えてますが大丈夫ですか?」
B「深夜の定期バッチでログがS3に送信されるから気にしなくていいよ」
A「e2eテストが落ちているらしいですが、大丈夫ですか?」
B「昔からずっと落ちてるし大丈夫でしょ」
この例では、どちらも指標を完全に無視しています。失敗判定がされても、何の調査も行われていないので、完全に無意味です。
このような状態に陥ったのは、エンジニアが、指標の誤った失敗判定をもとに無駄な調査をさせられたという苦い記憶があるからでしょう。
結果、「こんな値は飾りだよ」「しばらく待ってまた測定したら治ってるから意味ない」と言って無視するようになります。
この現象は、特にシステムのバグなどの具体的に対策するべき内容を測る場合に発生しやすいです。
補助的な指標の忘却
人間は手を動かして問題を解消するコストを払いたがらない。
指標が間違っている可能性が一定以上あれば、全てを指標のせいにしてサボり、ついには指標の存在を忘れる。
モグラたたきの限界
人間は、大量に流れてくるアラートから10%しかない異常例を集中して見つけ続けることは不可能である。
アラートをたくさん出しても、人間の集中力の限界以上の検出はできない。
指標自体の特性
指標があくまで目安だと分かっていても、目安に対して判断を下すのは人間です。そして、人間は通常、できる限り楽な判断を下します。
- バグを見つけて対策をすることに痛みを伴うため、指標の偽陽性を信じる。
- 認識しにくいものを対策するときに、ドメイン知識を考察したり、複雑さの本質を理解することには痛みを伴うため、指標の真陽性を信じる。
自分の都合に合わせて事実を捻じ曲げるのです。
結局、このような難しい仕組みを運用し続け、自然に逆らうには常にメンテナンスが必要です。補助的な指標があくまで目安である以上、文化やプロセスといった方法で対処するしかないです。
オブザーバビリティとモニタリング
さて、ここで話をオブザーバビリティに移しましょう。
オブザーバビリティとは
オブザーバビリティの定義を行います。
オブザーバビリティ
システムの状態を常に計測し、集約することで、システムの任意の時点の任意の状態を事後的に取得できるようにすること
この節の内容は、オブザーバビリティエンジニアリングの第1章で詳しく解説されています。
モニタリングとその限界
モニタリングの特徴
モニタリングとそれに関連する言葉の定義をします。
メトリクス
システムから得られる何らかの情報を示す数値のこと
モニタリング
メトリクスをベースに、特定の異常値になったことを判定する仕組みのこと
例えば、rootログインの失敗回数というメトリクスが、1分間に1000回を超えたとき、システムは何らかのブルートフォースアタックを受けている可能性が高いです。
そのため、モニタリングシステムはそれを通知し、エンジニアは異常の原因を探り、問題に対処します。
モニタリングでは、単に異常を判定するだけでなく、メトリクスベースの様々な手法が開発されてきました。以下はその一部です。
- 統計手法・異常値検出
- グラフ描画
- ダッシュボード
- オンコールローテーション
- エスカレーションポリシー
モニタリングについて詳しく知りたい場合、入門 監視やシステム運用アンチパターンの第4章や第6章を読むのがおすすめです。
モニタリングによって、障害を検知したり、システムの状態を把握したりすることができます。
モニタリングの弱点
-
事前に想定した問題しか検出できない
モニタリングには、事前に想定した内容しか検出できないという原理的な弱点があります。
つまり、未知の問題を検出するようなモニタリングを行うことはできないのです。
複数のコンポーネントが複雑に通信しあって全体を構成するシステムでは、発生確率が高い問題を全て事前に列挙して、対応するメトリクスを準備して、適切な閾値を用意することは不可能です。 -
アラートが誤りやすい
さらに、モニタリングでは、問題そのものではなく、問題の影響を受けやすいメトリクスに対してアラートを出します。そのため、アラートが誤りやすいという弱点があります。
例えば、システムが3つのサーバーで冗長化されたときに、特定のネットワークだけが遅くなったことを示すメトリクスを定義するのは難しいです。
もちろん、事前にそのような障害が発生することを知っていれば別ですが、それは不可能なので、特定の集計値を扱ってモニタリングする妥協を強いられます。 -
問題の原因が分からない
モニタリングでは、障害を幅広く検出するため、大量のメトリクスを集計し、その値をベースに判定を行います。逆に言えば、障害について詳しく調べたくても、集計値しか情報がありません。
つまり、必要なときに必要な情報が得られないのです。
メトリクスはあくまで事前に想定した集計値なので、障害ごとの細かい粒度まで情報を分類して取得することができないです。これにより、モニタリングによるデバッグは困難になります。
モニタリングの限界
モニタリングは事前に想定した範囲の集計値を出すため、その精度は事前に障害をどの程度詳しく予測できるかに依存する。未知の障害には役に立たない。
補助的な指標とモニタリングの類似性
モニタリングの弱点の理論的な説明
ここで、前節で挙げたモニタリングの問題点について考察してみましょう。
- 1つ目の「事前に想定した問題しか検出できない」というのは、指標の言葉で偽陰性が多いと言い換えられる
- 2つ目の「アラートが誤りやすい」というのは、指標の言葉で偽陽性が多いと言い換えられる
つまり、モニタリングの問題点は、補助的な指標の定義と共通しているのです。
モニタリングの指標からみた解釈
モニタリングの問題点は、メトリクスと閾値による障害判定が補助的な指標であることが原因である。
モニタリングが複雑なシステムで通用しなくなってきているのは、システムが複雑になり、予測可能性が下がってきているからです。実際、オブザーバビリティエンジニアリングでも、システムの問題を予測しやすい場合はモニタリングが適していると述べられています。
したがって、問題の本質は、メトリクスと閾値によってつくられる指標の質が、正確な指標から補助的な指標に変化しているからだと説明することができます。
モニタリングの問題点の理論的説明
モニタリングは通知を含むが、複雑なシステムではメトリクスが補助的な指標に近づくため、通知との相性が悪化する。
モニタリングの弱点と指標の粒度
この節では、モニタリングの3つ目の問題点である、「問題の原因が分からない」を指標の観点で解釈します。
この問題は、指標の粒度の原則が守られていないことが原因です。まずは、指標の粒度を定義しましょう。
指標の粒度
指標の粒度とは、指標がソフトウェアのどの程度の範囲に対する品質を判定しているかである。
例えば、システム全体のアップタイムは粒度が大きく、一つのメソッドをテストする単体テストは粒度が小さいということができます。
モニタリングでは、事前に予測して作っておけるような少量のメトリクスで全ての障害と障害の原因を特定するために十分な情報を用意しなければなりません。このような厳しい制約により、なんとか現実的な数のアラートを作ろうとした結果、むりやり集計したメトリクスを使うことになります。
例えば、ディスクの合計使用割合などは粒度の大きい集計値です。
しかし、指標は、粒度が大きいものを少し用意するよりも、粒度が小さいものを大量に用意したほうが効果的です。
指標の粒度の原則
指標は、できる限り細かい粒度で大量に測定すると、品質が悪い部分と良い部分が明確に区別できるためよい。
これは、単体テストの分野ではテストピラミッドという概念として知られています。特定のモジュール自体を検証する単体テストは高速でバグの原因の特定が容易なのでできる限り大量に、システム全体の検証をするe2eテストは不安定な要素が多く低速で問題を特定するのが難しいのでごく一部だけにするという設計原則です。詳しくは、単体テストの考え方/使い方の第4章を参考にしてください。
システム運用の場合、指標の粒度が大きいことは特に問題です。なぜなら、動作しているシステムについての情報は、事前に収集したメトリクスのみだからです。その結果、エンジニアはモニタリングされているメトリクスのみから勘と経験で障害の原因をエスパーすることになります。
オブザーバビリティによるモニタリングの弱点の解決
オブザーバビリティのプラクティス
とにかくデータを生で出力する
オブザーバビリティの基礎は、システムの必要なすべての場所で、集計しない生のデータを出力することです。
処理単位ごとにIDを割り当て、全てが区別できるようにした上でデータを出力します。
オブザーバビリティを理解する際に重要な概念として、カーディナリティとディメンションというものがあります。
カーディナリティ
集合に含まれるデータの一意性。
例えば、UUIDはすべての集合でただ1つしか含まれないので、カーディナリティが高い。
逆に、3つのマシン上で分散実行されているときに、ログのホストIDのカーディナリティは低い。
ディメンション
データ内部のキーおよびその数。例えば、リクエストID、ユーザー名、ホスト名、エラーコードなどは全てキーです。高ディメンションな情報とは、多くのキーを持った情報のことである。
高いカーディナリティと高いディメンションを保ったままデータを出力することがオブザーバビリティの第一歩です。これを達成するためには、いくつかのプラクティスがあります。
データを集計しない
データを集計すると、カーディナリティが下がります。
データを捨てない
データを捨てると、ディメンションが下がります。
全てのデータを構造化する
前節で述べた通り、オブザーバビリティを実現するうえでは、データは生のものを使い、集計しません。したがって、何らかの方法でデータの検索性を高め、自動化しやすい形式にしなければ、扱いづらいデータになってしまいます。この問題を解消するのが、構造化です。
データの構造化
機械による解釈が容易になるように、統一的なスキーマを持った形に情報を整形すること
形式面では、JSONとして出力することが一般的です。
内容面では、構造化データはその作業単位を実行するために必要なものをすべて含むように高いディメンションを持つべきです。
全てのログ・メトリクス・トレースを集約する
複雑なシステムでは、様々な場所で処理が実行されます。例えば、データを保持するシステムだけでもRDBとRedisとS3が使われている環境というのはもはや珍しくありません。
マイクロサービスアーキテクチャが導入されていれば、大量のコンテナがHTTPやgRPCを通じて通信しているはずです。
これらの出力する情報を、ひとつずつ調べていると非効率です。そのため、全てを集約する場所が必要です。
データの集約を実現するために、分散トレースという技術が重要です。
分散トレース
複数のプロセスで強調して実行されているタスクについて、トレースIDという識別子を割り当ててまとめる仕組み
これらのデータを集約し、テレメトリーとしてまとめて扱います。
テレメトリー
アプリケーションから得られる情報一般のこと
- ログ:アプリケーション上での何らかのイベントを記録したもの
- メトリクス:アプリケーション上の何らかの数値を記録したもの
- トレース:複数のプロセス間で相互に関連する一連のイベント
テレメトリーを収集するためには、何らかの処理を追加する必要があります。オブザーバビリティの文脈では、この処理を計装と読んでいます。
計装
アプリケーションが何らかのテレメトリーを送信するようなコードを書くこと
計装の具体的な方法についてはこの記事では触れません。
適切にテレメトリーを計装し、集約することで、特定の場所で全テレメトリーを分析できるようになります。
どうしてオブザーバビリティはモニタリングの弱点を補えるのか
通知 vs 検索
モニタリングでは、異常値を事前に閾値として定義して、異常な場合にアラートを出すという手法で問題と向き合ってきました。しかし、オブザーバビリティは単にシステムの状態を観測可能(Observable)というだけで、その情報を積極的に開発者に押し付けることをしません。そのかわり、検索機能を整備して、必要なときに必要な情報を取り出すことができます。このため、無駄な情報を読むことがありません。
通知より検索
誤っている可能性が高い情報を通知するよりも、開発者が必要に応じて適切な情報を選択し、検索できる方が良い。
予測 vs 検証
モニタリングでは、システムの障害のパターンを網羅し、それぞれをもっともよく示すメトリクスを事前に準備し、適切な閾値を設定する必要がありました。つまり、発生していない問題と原因を事前に予測できるという考え方をしています。
しかし、オブザーバビリティがあるシステムでは、全ての情報が収集され集約されているので、問題が発生したら、発生した問題について確定している情報をもとに、必要なシステムの情報を検証することができます。
予測より検証
発生していない問題についてあれこれ考えるより、発生した問題について現実の値をベースに仮説を立て、それを実際の値で検証したほうが圧倒的に正確で効率が良い。また、検証を使えば事前に想定していない問題についても対応できる。
集計値 vs 個別値
モニタリングでは、平均CPU使用率などの集計値を使います。そうしなければ、閾値の設定の作業量が膨大になりすぎるからです。また、特定のリクエストIDがどうしたからといって、システム全体の問題としてアラートを出すこともできないです。しかし、オブザーバビリティでは、情報は必要に応じて使われるものなので、個別値をそのまま保持することができます。
デバッガを使ってステップ実行をしたことがあるエンジニアならば誰しも知っているように、具体例は重要です。具体的な値を計算して、どの時点で誤りが発生したのかを理解することで、より高いレベルで発生している障害を理解するための足掛かりを得ることができます。
集計値より個別値
テレメトリーを集約することは、個別のプロセスで発生している具体的な問題を包み隠すことにつながり、問題の原因の特定を遅らせる可能性がある。個別値を参照できることは重要である。また、個別値さえ用意しておけば、集計は必要に応じて行うことができる。
オブザーバビリティのあるシステムでのモニタリングの役割
モニタリングだけで対応する場合
オブザーバビリティが無いシステムでは、モニタリングに以下のような過剰な責務を期待していました。
- 障害の検知
- 障害の原因の検知
- システムの状態の把握
従来のシステムでは、障害が起こったことだけでなく、障害の原因を理解するための情報を全て先回りして収集することを期待していました。そのせいで、障害との相関が弱いメトリクスに対しても閾値が設定され、実際には障害を引き起こさないメトリクスの異常値に対して、無意味なアラートが引き起こしてしまったのです。
オブザーバビリティがある場合
オブザーバビリティが適切に確保されていれば、モニタリングに期待する責務は、以下の1点だけに減らすことができます。
- ユーザーに直接損害を与えるような障害の検出
なぜなら、障害を引き起こす原因になっていそうなメトリクスや統計情報は、明らかな目の前の障害に応じて、その場で即座に集計することができるからです。これは、大きなパラダイムの変換です。障害の原因を事前に予測するのでなく、問題から原因を考え、仮説を検証するという自然な方法で問題に取り組めるのです。
モニタリングとオブザーバビリティの役割分担
- モニタリングでは、直接問題になっている障害を検出する
- オブザーバビリティが確保されているため、障害からシステムの状態に関する質問を考え、その場で疑問を解消する
オブザーバビリティの発想を用いて補助的な指標を扱う
ここまでで、以下の2点を解説しました。
- モニタリングの問題は補助的な指標の問題と類似している
- モニタリングの問題をオブザーバビリティにより解消できること
ここから、補助的な指標の問題を、オブザーバビリティの考え方によって解消できるのではないか、という自然な発想がうまれます。
オブザーバビリティの心
オブザーバビリティの発想を利用すれば、単にシステムの情報を分析できるだけでなく、補助的な指標全般を有効に活用することができる。オブザーバビリティの精神は、より広い応用範囲を持っている。
オブザーバビリティの利点の抽象的解釈
まずは、オブザーバビリティの考え方をより抽象的な指標の観点から捉えなおしてみましょう。ここで解説する利点は、オブザーバビリティのモニタリングに比べた利点で述べたものと同じですが、実はこれが指標で説明した問題ときれいに対応しているのです。
通知 vs 検索
モニタリングの通知で解消するという考え方は、誤った通知を引き起こすのでした。つまり、指標の偽陽性が多いということです。
したがって、この利点は、偽陽性が多い補助的な指標と強い通知の相性が悪いという問題を解決しています。
この利点は、最初に補助的な指標の問題点として説明した忘れられることを防ぐことに直結します。
必要なときに必要な情報として指標を使うのであって、補助的な指標側から通知をすると、無視され忘れられるのです。
予測 vs 検証
モニタリングでは、事前に定義した問題しか検出できません。これはつまり、偽陰性が多いということです。
したがって、この利点は、偽陰性が多い補助的な指標で問題の全部を解決したつもりになってはならないという問題を解決しています。
この利点は、最初に補助的な指標の問題点として説明した宗教化を防ぐことに直結します。
問題が発生して、その発生した問題を分析するために指標を使うのであって、指標が先にあるのではない、という順序関係が明確になっているのです。
集計値 vs 個別値
この利点は、指標の粒度についてのベストプラクティスに対応しています。指標は、対応する場所が明確であるほど有用であるため、個別値は重要だと言えるのです。
補助的な指標の扱い方
オブザーバビリティの考え方を取り込んだ補助的な指標の扱い方を4ステップに分けて解説します。この方法を利用すれば、補助的な指標は宗教化もせず、忘れられもせずに、開発者の中で有用に扱われる可能性が高いです。
1. 直接的な指標にのみアラートを出す
オブザーバビリティがあるシステムでは、直接障害を引き起こす部分だけアラートを出せばよかったのと同様に、補助的な指標もアラートを出さなくてよいです。
その代わり、ソフトウェアのクラッシュや開発者のレビューによる複雑性の増加の指摘など、より直接的な指標に対してアラートを出しましょう。
認知負荷など、測定が難しい品質は、自明な指標によって機械的に問題をアラートすることが難しいです。そのため、人間がレビューを行い、積極的に指摘するようにするしかありません。
大切なのは「コード行数が300行を超えたから複雑だ」ではなく「レビューで考えた結果、複雑だ」になることです。
アラートを出す基準
アラートでは、偽陽性を許さない。明らかな問題は機械で検出し、その他は人間のレビューを活用して問題を検出する。
2. 補助的な指標を参照させる
補助的な指標でアラートを出さないため、何らかの方法で補助的な指標を参照してもらう必要があります。
そのためのベストプラクティスは、オブザーバビリティからヒントを得ることができます。
オブザーバビリティのためにテレメトリーを係争したり構造化したり集約したのと同じように、補助的な指標も常にバックグラウンドで計測し、構造化し、集約しておくことが重要です。補助的な指標は補助的なので、それを調べるのに手間がかかるとすぐに無視されます。すべてをよく知られた単一の場所に保持しておきましょう。
アラートのメッセージやwikiの手順書に、どのような補助的な指標を利用するのが望ましいかを記述するのもいい手段です。ただし、これは強制するのでなく、あくまで性質だけを示して、開発者が必要だと思ったときに参照されるべきです。
補助的な指標の利用を促進する
補助的な指標は開発者の手間をかけずに自動で測定し、集約し、統一的なインターフェースで利用できるようにし、利用方法を分かりやすく示すことでようやく有用に使われるようになる。
3. カーディナリティの高い補助的な指標を検索させる
補助的な指標に限らず、指標全般は対応する範囲が狭いほど理解が容易です。そのため、補助的な指標もなるべく細かい単位で集計するようにします。
そして、必要に応じて細かい指標を取り出せるように検索性を高めます。
指標は多岐にわたるので、関数単位、ファイル単位、時間単位など様々な区切り方があるので、検索性の高め方は様々です。適切に構造化してメタデータを付与することで、開発者の検索性を高めましょう。
指標の粒度を小さくして精度を高める
粒度を高めることで、補助的な指標の曖昧さを多少緩和することができる。
粒度が小さくなると、代償として検索性が下がるため、メタデータを適切に設定して検索性を高める。
4. 抜本的な解決を行う
補助的な指標を使って問題を理解したら、問題の原因を考え、抜本的に解決しましょう。
ここで重要なのは、改善後に補助的な指標がどうなっていようと気にしないということです。補助的な指標はアラートにも、作業の成功の保証にもなりません。あくまで、どのような問題が発生しているかを理解し、分析するためのツールなのです。
改善後には適切にレビューを行って、本質的に問題が解決しているかを話し合って判断してください。
これはオブザーバビリティでも同じです。大量のテレメトリーで問題を理解したら、それをベースに抜本的に解決するだけで、問題を理解するときに参照した個別のデータがどうこうなったところで解決したとはみなしません。
解決時には補助的な指標を忘れる
補助的な指標で改善を判定するのは科学ではなく宗教です。問題を適切に理解するのは人間であり、解決を判断するのはレビューであることを忘れないでください。
おわりに
長い記事を読んでいただき、ありがとうございました。
この記事では、オブザーバビリティという具体的な実践とその背後の考え方を理解することで、補助的な指標という抽象的な概念一般を効果的に活用するための方法を導き出すことができました。
抽象的な理論について考えるときでも、地に足をつけた実践から学ぶことを大切にしたいものです。