TT Lab
はじめる
学ぶ 学習パス コース

Loki — ログを索引しないログストア

400 は直し、429 は待つ

TT Labで続きを見る

一言でいうと

書き込み側の拒否は2種類です。400は直す必要があり、429は待つ必要があります。この2つを同じコードで処理するコレクターは、どちらも誤って扱います。

なぜ必要なのか

新しいサービスをつないだ日に、コレクターのログに拒否が大量に出ました。担当者はリトライのロジックをオンにして帰宅しました。翌朝、拒否はそのままで、そのかわり、コレクターのバッファーがいっぱいになって、他のサービスのログまで滞っていました。

拒否は400でした。ラベル名にハイフンが入っていて、その行は何度送り直しても、永遠に拒否されます。リトライは状況を直せず、キューを埋めただけでした。逆に、同じ週に別のサービスで出た429は、リトライが正解だったのに、そちらはリトライなしで捨てていました。

どう動くのか

Lokiが入ってくるリクエストを拒否する理由は、大きく4つです。

状況 コード 対処
ラベル名が文法に合わない 400 送る側を直します
ラベルの個数が上限を超える(max_label_names_per_series) 400 ラベルを減らします
行が長すぎる(max_line_size) 400 切り詰めるか、分けます
ストリームあたりの速度超過(per_stream_rate_limit) 429 バックオフしてからリトライします

ラベル名はPrometheusと同じルールです。文字かアンダースコアで始まり、文字・数字・アンダースコアだけを使います。ハイフンは使えず、数字で始めることもできません。Kubernetesのラベルをそのまま移そうとして、この壁にぶつかることが多いので、コレクター側で名前を変える設定を、あらかじめ入れておくほうがよいです。

レート制限はストリーム単位です。そのため、同じサービスでも、ラベルの組み合わせが複数あれば、各組み合わせが別々に上限を受けます。逆に、ラベルを減らしてストリームをまとめると、1つのストリームにトラフィックが集中して、429が出ます。カーディナリティを下げることと、レート制限を避けることは、互いに反対方向に引っ張ります。この緊張関係を知らないと、片方だけを直して、もう片方を爆発させます。

バーストが、この図にもう1層加わります。per_stream_rate_limit_burstは、瞬間的に上限を超えてもよい量です。そのため、静かだったあと一気に押し寄せるトラフィックは、最初のしばらくは通過し、そのあとから429を受けます。「最初は通るのに、急にだめになる」という症状の正体は、たいていこれです。

429を受けたときの正しいクライアントは、3つを備えています。指数バックオフ、ジッター、リトライの上限です。ジッターがないと、複数のコレクターが同じタイミングで再び押し寄せて、同じ壁を一斉に叩きます。上限がないと、400を429と取り違えたときに、永遠にリトライします。

現場での姿

最もよくある事故は、上の「400にリトライ」です。コレクターの設定で、リトライの条件をステータスコードで明示し、400番台は隔離キューに送って、人が見るようにするのが解決策です。

2つ目は、長い行です。スタックトレース1つが上限を超えると、そのリクエストがまるごと拒否され、同じバッチのまともな行まで一緒に落ちます。そのため、コレクターで先に切り詰めるか、バッチを小さく取って、被害範囲を減らします。

3つ目は、ラベルの個数です。Kubernetesのメタデータをすべてラベルに移すと、上限を簡単に超えます。必要な5、6個だけを残して、残りは本文に置けば、拒否もなくなり、ストリーム数も減ります。

次のラボですること

サーバーが持っている上限を、/configから直接読んで表にし、間違ったラベルと長すぎる行を実際に送って、400とその本文を受け取ってみます。そのあと、低いレート上限をかけた2つ目のLokiを起動して、本物の429を受け取り、バーストのために最初の数回が通過した理由を、数字で説明します。最後に、バックオフリトライを付けて、1行も失わずにすべて保存するところまで行きます。