プロビジョニングAPIの誤解を招く指標を修正する
目標
SLIの分母を宣言し、失敗が抜け落ちる計装コードを直し、実際のHTTPレスポンスとメトリクスを突き合わせます。収集の重複とリトライを区別したうえで、独立した3つの観測ウィンドウのエラーバジェットで、通常の機能デプロイを判断します。
なぜ重要なのか
成功の経路だけを数える計装は、失敗が増えるほど、より良い成功率を示すことがあります。正しいPromQLも、誤って集めた生データを直してはくれません。このラボは、アラートルールを書く前に、何を1件として数えるのかと、抜けた証拠がないかを検査します。関数名を合わせるだけでは通過せず、さまざまな入力・実際のHTTP実行・自分で設計した反例を確認します。
このラボは、Pod内のPythonサーバーを実際に実行します。Kubernetes APIやKWOKのRunning状態を、実行の証拠として使いません。外部システム、実際のGPU、本番のPrometheusの収集、実際のデプロイは扱いません。レイテンシはサーバーの処理時間であり、クライアントのネットワーク往復時間とは異なります。30日ウィンドウの数字は、学習用の仮想データです。
作業パスは/root/cnpe-sliです。Pythonは標準ライブラリだけを使います。セッションが終わるとファイルと診断記録は消えるので、必要な出力物は別に保管してください。ヘルパーは/opt/fixtures/cnpe_sli_workbench.pyで、採点のたびにファイルを読み、一時プロセスを終了します。
ステップ
- scope.jsonに、単位・分母・目標を宣言します。
- classifier.pyのclassifyで、ステータスコードとパスを分類します。
- 同じ関数に、遅い成功レスポンスの分類を完成させます。
- 実際のHTTPを再現して、http-receipt.jsonを保存します。
- summarize.pyで、収集の重複を取り除きつつ、実際のリトライは別に数えます。
- budget.pyで、観測の空白・端数のバジェット・枯渇の境界を処理します。
- report.pyとreport.jsonで、3つのウィンドウの可用性・レイテンシの判断を合わせます。
- counterexamples.jsonで、5種類の誤った実装を区別します。
参考
- ステップのカードに、正確な関数シグネチャとJSONフィールドの契約があります。ファイルの例は形式だけを示すもので、正解ではありません。
- 各ステップは、
python3 /opt/fixtures/cnpe_sli_workbench.py grade /root/cnpe-sli 단계번호(プレースホルダーはステップ番号です)で直接検査できます。 exercise /root/cnpe-sliは、一時ポートを使って、正常・1.1秒の遅延・503・504・400・404を実際に発生させます。X-Lab-Scenarioは学習用の障害選択ヘッダーであり、本番の機能ではありません。- この課題に限って、2xx・5xxを有効な試行と定めます。本番で429や4xxを無条件に除外すると、サービスの責任の失敗を隠すことがあります。実際の有効なリクエストの契約を合意する必要があります。
- SLI設計・計装の原則・エラーバジェットポリシーの例を、合わせて読んでください。
分母から契約として残す
/root/cnpe-sli/scope.jsonを作成します。routeは/provision、unitはattempt、eligible_classesは[2,5]、latency_limit_msは1000、availability_targetは0.999、latency_targetは0.99、no_dataはinvestigateです。この2つの目標は、ラボの仮想のポリシーです。
ユーザー作業1件とHTTP試行1件は違います。この課題は試行単位です。観測できなかったウィンドウは、成功率1で埋めません。
成功の経路だけを数えるバグをなくす
classifier.pyにclassify(path, status, latency_ms)を実装します。戻り値は、eligible・available・fastのbool 3つのフィールドです。クエリ文字列を除いたパスが/provisionで、ステータスコードが2xxまたは5xxのときだけ、eligible=Trueです。availableは、そのうち2xxです。fastは、このステップではFalseのままでもかまいません。/healthz・ほかのパス・4xxは除外します。
503・504は成功ではありませんが、分母には入ります。URLクエリのユーザー値は、パスの分類やメトリクスのラベルに含めないでください。
遅い201を良いレスポンスとして数えない
classifier.pyのfastを完成させます。available=Trueで、かつlatency_ms <= 1000の場合だけTrueです。1000は含み、1000.1は除外します。入力のレイテンシは、有限の0以上のミリ秒です。eligible・availableの契約は維持し、文字列や0・1の代わりに、実際のboolを返してください。
速い503も、良いレイテンシのレスポンスではありません。このSLIは、すべての有効な試行のうち、速くて成功したレスポンスの割合であり、成功したリクエストだけを分母にしません。
レスポンス7件とメトリクス4種類を突き合わせる
ヘルパーのexercise /root/cnpe-sliの出力を、http-receipt.jsonに保存してください。実際のステータスコードは、順に200,201,201,503,504,400,404で、遅い201のサーバー処理時間は1000ms以上です。observed=7、eligible=4、available=2、fast=1である必要があります。現在のclassifierでもう一度実行した結果も、同じである必要があります。
classifierの単体テストが合っていても、HTTPの経路では抜けることがあります。保存された数字だけを見ず、失敗レスポンスが分母に含まれるかどうかを、現在のコードで再現してください。
収集の重複とリトライを分ける
summarize.pyにsummarize(events)を実装してください。各イベントは、attempt_id・path・status・latency_msのフィールドを持ちます。同じattempt_idで内容も同じ重複は、1回だけ分類し、duplicatesを増やします。同じIDで内容が異なる場合は、ValueErrorです。別のIDのリトライは、それぞれ数えます。戻り値のフィールドは、eligible・available・fast・excluded・duplicatesの5つの整数です。classifier.classifyを再利用してください。
失敗した試行aと成功したリトライbを、最終的な成功1件で覆うと、試行ベースのSLIが変わります。逆に、コレクターが同じイベントを2回送ったものは、分母に2回入れません。
0.1件のバジェットと観測の空白を扱う
budget.pyにdecide(eligible, good, target, covered)を実装します。0 <= good <= eligibleのintと、0 < target < 1の数、bool型のcoveredだけを受け取り、誤っていればValueErrorです。covered=Falseまたはeligible=0なら、sli・allowed_bad・remaining_bad・consumedをnull、decisionをinvestigateで返します。残りは、sli=good/eligible、allowed_bad=eligible*(1-target)、remaining_bad=allowed_bad-(eligible-good)、consumed=(eligible-good)/allowed_badです。sli・consumedは小数6桁に丸めます。バジェットは整数に丸めません。残りのバジェット <= 0ならfreeze、そうでなければshipです。
100回で目標99.9%なら、許容される失敗量は0.1です。整数の0にしないでください。Decimal(str(target))は、0.999を2進浮動小数の誤差に変えずに、バジェットの境界を比較する方法の1つです。boolがPythonではintのサブタイプであることにも注意してください。
良い可用性が悪いレイテンシを隠さないようにする
ヘルパーのwindows出力をwindows.jsonとして保存し、report.pyのbuild(windows)を実装してください。入力は、name・eligible・available・fast・coveredを持つ独立したウィンドウのリストです。戻り値は、ウィンドウ名をキーとし、availability=decide(eligible,available,0.999,covered)、latency=decide(eligible,fast,0.99,covered)、decisionを持つオブジェクトです。統合した判断は、investigate優先、次にfreeze、最後にshipです。ウィンドウ名が重複していればValueErrorです。この結果をreport.jsonに保存してください。
steadyはship、slowは可用性が良くてもレイテンシのためfreeze、gapは数字が完璧に見えてもinvestigateです。出力ファイルだけでなく、関数も新しいウィンドウの入力に合わせて計算する必要があります。
自分のテストが誤った実装を捕まえるか確認する
counterexamples.jsonに、5–24個の{op,args,expected}のケースを書いてください。opはclassify・summarize・decideで、argsは該当の関数の位置引数のリストです。expectedは正確な戻りオブジェクトで、ValueErrorを期待する場合は{"error":"ValueError"}です。3つの関数のケースをすべて含め、5xxの欠落・遅い成功をfastとして処理・重複の二重カウント・バジェットの整数丸め・観測の空白の承認という5つの誤りを、すべて捕まえる必要があります。
正常な場合だけを見るテストは、誤った実装も通過させてしまいます。関数ごとに、元の実装と誤った実装の出力が変わる入力を選んでください。期待値を間違えて書いて赤ランプを作ることも、検証ではありません。