こんにちは。よっしーです(^^)
今日は、k6におけるしきい値についてご紹介します。
背景
Dockerで構築したWebアプリの開発環境において、k6のしきい値について調査したときの内容を備忘として残しました。
開発環境のソースは下記のリポジトリにあります。
閾値とは
閾値は、テストメトリクスに対して定義する合格/不合格の基準です。テスト対象のシステム(SUT)のパフォーマンスが閾値の条件を満たさない場合、テストは失敗ステータスで終了します。
テスターは、しばしば閾値をSLO(Service Level Objectives、サービスレベル目標)を具体化するために使用します。例えば、以下の期待値の組み合わせの閾値を作成できます:
- エラーが返されるリクエストが1%未満。
- リクエストの95%が応答時間が200ミリ秒以下。
- リクエストの99%が応答時間が400ミリ秒以下。
- 特定のエンドポイントは常に300ミリ秒以内に応答する。
- カスタムメトリクスの条件。
閾値は、負荷テストの自動化にも不可欠です:
- テストに閾値を設定します。
- 実行を自動化します。
- テストの失敗に対するアラートを設定します。
その後、SUTがパフォーマンスの期待値を満たさない場合にテストのことを気にする必要があります。
例: HTTPエラーと応答時間の閾値 このサンプルスクリプトでは、2つの閾値が指定されています。1つの閾値はHTTPエラーの割合(http_req_failedメトリクス)を評価し、もう1つは95%の応答が特定の期間内に発生するかどうかを評価します(http_req_durationメトリクス)。
import http from 'k6/http';
export const options = {
thresholds: {
http_req_failed: ['rate<0.01'], // http errors should be less than 1%
http_req_duration: ['p(95)<200'], // 95% of requests should be below 200ms
},
};
export default function () {
http.get('https://test-api.k6.io/public/crocodiles/1/');
}
言い換えると、閾値を定義する際に、合格基準の式を指定します。テストの最後でその式がfalseに評価される場合、k6はテスト全体を失敗と見なします。
このスクリプトを実行した後、k6は次のような出力を生成します:
✓ http_req_duration..............: avg=151.06ms min=151.06ms med=151.06ms max=151.06ms p(90)=151.06ms p(95)=151.06ms
{ expected_response:true }...: avg=151.06ms min=151.06ms med=151.06ms max=151.06ms p(90)=151.06ms p(95)=151.06ms
✓ http_req_failed................: 0.00% ✓ 0 ✗ 1
この場合、テストは両方の閾値の基準を満たしました。k6はこのテストを合格とみなし、終了コード0で終了します。
もし閾値のどれか一つでも失敗した場合、閾値名(http_req_failed、http_req_duration)の隣にある小さな緑のチェックマーク ✓ は赤いクロス ✗ に変わり、k6はゼロでない終了コードで終了します。
閾値の構文
閾値を使用するには、以下の手順に従います:
オプションオブジェクトのthresholdsプロパティで、閾値を設定したいメトリクスの名前をキーとして設定します。
export const options = {
thresholds: {
/* ... */
}
}
少なくとも1つの閾値式を定義してください。これは2つの方法で行うことができます:
- 短い形式では、すべての閾値式を文字列として配列に入れます。
- 長い形式では、各閾値をオブジェクトに配置し、失敗時に中止するための追加のプロパティを持たせます。
export const options = {
thresholds: {
//short format
METRIC_NAME: ["THRESHOLD_EXPRESSION", `...`],
//long format
METRIC_NAME2: [
{
threshold: "THRESHOLD_EXPRESSION",
abortOnFail: true, // boolean
delayAbortEval: "10s", // string
},
], // full format
},
};
METRIC_NAME1とTHRESHOLD_EXPRESSIONはプレースホルダーです。実際のテキストはメトリクスの名前と閾値式である必要があります。
この宣言は、メトリクスmetric_name1とmetric_name2のための閾値を設定します。閾値が合格または不合格かどうかを判定するために、スクリプトは ‘threshold_expression’ を評価します。
threshold_expressionはtrueまたはfalseに評価されます。閾値式は以下の形式でなければなりません:
<aggregation_method> <operator> <value>
threshold_expressionのいくつかの例は以下の通りです:
- avg < 200 // 平均の所要時間は200ms未満でなければならない
- count >= 500 // カウントは500以上でなければならない
- p(90) < 300 // サンプルの90%は300以下でなければならない
タイプ別の集計方法 k6はメトリクスをそのタイプに応じて集計します。これらの集計方法はthreshold_expressionの一部を形成します。
メトリクスのタイプに応じた集計方法は以下の通りです:
- Counter(カウンター): count および rate
- Gauge(ゲージ): value
- Rate(レート): rate
- Trend(トレンド): avg(平均)、min(最小値)、max(最大値)、med(中央値)、および p(N)(Nは閾値パーセンタイル値を指定し、0.0から100.0までの数値で表されます。例: p(99.99) は99.99パーセンタイルを意味します。値はミリ秒単位です。)
この(少し工夫された)サンプルスクリプトは、すべての異なる種類のメトリクスを使用し、それぞれに異なるタイプの閾値を設定しています:
import http from 'k6/http';
import { Trend, Rate, Counter, Gauge } from 'k6/metrics';
import { sleep } from 'k6';
export const TrendRTT = new Trend('RTT');
export const RateContentOK = new Rate('Content OK');
export const GaugeContentSize = new Gauge('ContentSize');
export const CounterErrors = new Counter('Errors');
export const options = {
thresholds: {
// Count: Incorrect content cannot be returned more than 99 times.
'Errors': ['count<100'],
// Gauge: returned content must be smaller than 4000 bytes
'ContentSize': ['value<4000'],
// Rate: content must be OK more than 95 times
'Content OK': ['rate>0.95'],
// Trend: Percentiles, averages, medians, and minimums
// must be within specified milliseconds.
'RTT': ['p(99)<300', 'p(70)<250', 'avg<200', 'med<150', 'min<100'],
},
};
export default function () {
const res = http.get('https://test-api.k6.io/public/crocodiles/1/');
const contentOK = res.json('name') === 'Bert';
TrendRTT.add(res.timings.duration);
RateContentOK.add(contentOK);
GaugeContentSize.add(res.body.length);
CounterErrors.add(!contentOK);
sleep(1);
}
注記 同じメトリクスに対して同じプロパティ名を繰り返して複数の閾値を指定しないでください。
閾値はJavaScriptオブジェクトのプロパティとして定義されているため、同じプロパティ名を持つ複数の閾値を指定することはできません。
export const options = {
thresholds: {
// don't use the same metric more than once here
metric_name: ["count<100"],
metric_name: ["rate<50"],
},
};
それ以降の閾値は無視されます。同じメトリクスに複数の閾値を設定したい場合は、同じキーに対して配列で指定してください。
閾値がクロスされた場合にテストを中止する
閾値がクロスされた場合にテストを中止したい場合は、abortOnFailプロパティをtrueに設定します。abortOnFailを設定すると、閾値が失敗した時点でテスト実行が停止します。
しかし、テストが早期に閾値を失敗し、テストが重要なデータを生成する前に中止されることがあるかもしれません。これらのケースを防ぐために、delayAbortEvalを使用してabortOnFailを遅延させることができます。このスクリプトでは、abortOnFailは10秒間遅延されます。10秒後、テストはp(99) < 10の閾値に失敗した場合に中止されます。
export const options = {
thresholds: {
metric_name: [
{
threshold: 'p(99) < 10', // string
abortOnFail: true, // boolean
delayAbortEval: '10s', // string
/*...*/
},
],
},
};
名称(TYPE):threshold(文字列) 説明:評価する閾値条件を指定する閾値式文字列です。
名称(TYPE):abortOnFail(ブール値) 説明:テストが完了する前に閾値がfalseに評価された場合にテストを中止するかどうかを指定します。
名称(TYPE):delayAbortEval(文字列) 説明:閾値の評価を遅延させ、いくつかのメトリクスサンプルを収集させるために、相対時間文字列(10秒、1分など)を使用して評価を遅延させる場合に、遅延の時間を指定します。
おわりに
今日は、k6におけるチェック内容をカスタマイズする方法についてご紹介しました。
何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント