400 は直し、429 は待つ
一言でいうと
書き込み側の拒否は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行も失わずにすべて保存するところまで行きます。