小規模なスクレイピングのジョブは、動くか動かないかのどちらかであり、それはすぐにわかる。大規模なジョブは、そのどちらの状態にもならない。常にトラフィックの一定割合が失敗しており、運用上有効な問いは「プロキシは動いているか」ではなく「このうちどの部分が劣化しているのか、それは自分たちの問題なのか、プロバイダーの問題なのか、それともターゲットの問題なのか」である。
有効な答えを得るには、独立して失敗しうる各次元に沿って計測を行う必要がある。これは単にメトリクスを増やすこととは違う。以下では、何を計測すべきか、帯域を浪費せずにどのようにプローブするか、そして3つの失敗要因をどう区別するかを説明する。
Health is per route, not per proxy
最初に修正すべき点がある。プール型のゲートウェイでは、監視すべき「プロキシ」というものは存在しない。出口アドレスを自分で選んだことはなく、それを保持することもなく、一度失敗したアドレスは時間をかけて追跡できる存在ではない。追跡できるのはルート、つまり自分が制御できる要素の組み合わせ、すなわちターゲット、国、そして必要に応じて都市やASNである。
So the unit of health is the route. amazon-de、serp-us-chicago、marketplace-jp。それぞれが独自の成功率、レイテンシのプロファイル、失敗の内訳を持ち、他が完璧なままで一つだけ劣化することもある。単一のグローバルな「プロキシのヘルス」という数値は、まさに必要な信号を平均化して消し去ってしまう。全体で95パーセントという数字は、20個のルートが99パーセントで1個のルートが0パーセントという状態でも成立し、対応が必要だと教えてくれるのは後者の読み方だけである。
第2の、より短命な単位としてセッションのヘルスも加える。チャレンジを引き起こすようになったスティッキーセッションは、再利用するのではなく引退させて置き換えるべきであり、その判断はプロキシマネージャーの構築で説明されているように、セッションを配布するのと同じコンポーネントが担うべきものである。
Passive first: every real request is a health check
最も安価な監視は、すでに送信しているトラフィックそのものである。本番の各リクエストは結果を生み出し、それをルートに紐づけて記録すれば、追加の帯域コストなしに継続的なヘルスデータが得られる。
重要な点は、何を成功とみなすかである。200ではない。チャレンジページ、汎用的あるいは空の結果、切り詰められたリスティング、ランディングページへのリダイレクトはすべて200を返すが、いずれも収集が失敗したことを意味する。したがって、ステータスコードだけを数える監視は、データセットが劣化していても健全だと報告してしまう。結果を記録する前に、ブロックや偽コンテンツの検出で述べられている手法にしたがい、レスポンスボディをターゲットごとの期待値に対して検証すべきである。この一点の変更が、問題を捉えるヘルスダッシュボードと、自分の思い込みを確認するだけのダッシュボードを分ける。
最低限、リクエストごとに次を記録する: ルート、検証済みの結果、レイテンシ、バイト数、失敗した場合はその失敗クラス。これだけで以下のすべてを計算するのに十分である。
Classify failures, because the class is the diagnosis
失敗を数えることは、何かが間違っていることを教えてくれる。それを分類することは、何が間違っているかを教えてくれる。ほぼすべてを5つのクラスで説明できて、それぞれが異なる方向を指し示す。
認証失敗は、認証情報の誤りやターゲティングフラグの不正な形式を意味し、これは自分側の設定の問題であり、407エラーと認証エラーの修正にあるとおり、再試行しても解決しない。マッチなし失敗は、その時点でゲートウェイにフィルタに合うアドレスが存在しないことを意味し、何かが壊れているのではなくターゲティングが狭すぎることを示す。都市から国へと範囲を広げれば解消する。レート信号、つまり429などは、そのターゲットに対してペーシングが強すぎることを意味し、これはスロットリングの問題である。ブロック信号、つまりチャレンジや持続する403は、ターゲットがそのアイデンティティを拒否したことを意味するので、セッションを引退させ、ヘッダーやフィンガープリントが本当の原因ではないかを検討する。トランスポート失敗、つまりタイムアウトやコネクションリセットは、あいまいなクラスであり、それ自体の調査に値する。リクエストがタイムアウトする理由には、ターゲット側の遅さ、不健全なルート、そして自分自身の並行度が高すぎることが含まれるからである。
合計よりも内訳のほうが重要である。成功率80パーセントのルートで、その失敗がレート信号で構成されている場合はペーシングを遅くする必要がある。同じ80パーセントでもブロック信号で構成されている場合は異なるアイデンティティ戦略が必要である。マッチなしエラーで構成されている場合はターゲティングを広げる必要がある。同じ数字でも、対処法は3つとも異なる。
Active probes, used sparingly
パッシブな監視には一つの盲点がある。現在使用しているルートしかカバーできないため、03:00に実行予定のルートについて、22:00の時点でそれが壊れているという警告は得られない。少数のアクティブプローブがこの隙間を埋めるが、それには帯域のコストがかかるので、安価で目的の明確なものにとどめておく。
実行する価値があるのは2種類である。国ごとの接続性と地理位置のプローブは、アドレスとその位置情報を返す小さなエンドポイントにアクセスし、ゲートウェイに到達可能であること、そしてリクエストした国と実際に得られる国が一致していることを確認する。これは最も頻繁に実行するプローブなので、軽量に保つ。重要なターゲットごとのターゲットカナリアは、既知の安定したページを1つ取得して検証することで、その特定のターゲットが正常に応答しているかどうかを教えてくれる。これはターゲットの問題とプロキシの問題を区別するためのチェックである。
import requests
def geo_probe(country):
proxy = f"http://customer-USERNAME-country-{country}:PASSWORD@p.shifter.io:443"
try:
r = requests.get("https://ipinfo.io/json",
proxies={"http": proxy, "https": proxy}, timeout=15)
d = r.json()
return {"ok": d.get("country","").lower() == country,
"got": d.get("country"), "org": d.get("org")}
except Exception as e:
return {"ok": False, "error": type(e).__name__}
実際に使用している国について、地理位置プローブは低い頻度で実行し、カナリアは沈黙した失敗がどれだけのコストになるかに応じた頻度で実行する。プローブが1回失敗しただけでアラートを出すのではなく、繰り返し失敗した場合にアラートを出す。ローテーティングプールにおける単発の失敗は通常のノイズである。
Telling the three failure sources apart
これはインシデントの際に実際に問われる質問であり、その答えは単一のメトリクスからではなく、複数の信号を比較することから得られる。
自分側の問題は、無関係な多数のターゲットで同時に失敗が発生する形で現れ、通常は何かがデプロイされた瞬間に始まる。あらゆる場所での認証失敗、特定のワーカープールからのトランスポートエラーの急増、あるいは誰かがフィルタを厳しくした後のマッチなしエラーの増加は、いずれも自分側の内部を指している。手がかりは範囲の広さである。自分自身のバグはターゲットの境界を尊重しないことが多い。
プロバイダーの問題は、多数のターゲットにわたって失敗が発生するが、ネットワーク層に限定される形で現れる。接続性プローブが失敗する、地理位置プローブが間違った国を返す、通過できたターゲットカナリアは有効なページを返しているのにトランスポートエラーが増加する、といった形だ。ここで公開ステータスページが役立つ。自分側の落ち込みとプロバイダーのインシデント履歴を対比させれば、その問いにすぐ答えが出る。何が保証されるかを規定する段階についてはプロキシのSLAとアップタイム保証にある。
ターゲットの問題は、他のすべてのルートが健全なまま、失敗が1つのターゲットに限定される形で現れ、そのターゲットのカナリアが失敗する一方で地理位置プローブは通過する。すべてのリージョンから同時に失敗する場合は、そのサイト自体が問題を抱えている可能性が高い。1つのリージョンからのみ失敗する場合は、地域特有のブロックやリージョンのエッジの問題を見ていることになる。
これらの比較が半日ではなく1回のクエリで済むように計測しておく。同じ失敗クラスと結果を、ルート、リージョン、ワーカーでタグ付けしておけば十分である。
What to alert on
ダッシュボードは調査のためのものであり、アラートは誰かを呼び起こすためのものである。アラートの数は少なく保ち、それぞれを行動可能なものにする。
グローバルなしきい値ではなく、ルート自身の変動するベースラインを下回った検証済み成功率にアラートを出す。敵対的なターゲットに対して通常70パーセントで動いているルートは、70パーセントでは健全であり、40パーセントでは壊れているからである。失敗の内訳の変化にアラートを出す。成功率を維持していても、レート信号がブロック信号に置き換わっているルートは、トラブルを予告する形で性質が変化している。リトライ比率の上昇にアラートを出す。これは成功率が下がる前に上昇するため、得られる中で最も早い警告となる。カバレッジにアラートを出す。つまり、スケジュールされたルートが自身の直近の履歴と比べて実質的に少ないレコードしか生成していない状態であり、これは成功率では見えない静かな縮小を捉える。そして、依存しているリージョンでプローブが繰り返し失敗した場合にアラートを出す。
単発の区間ではなく、持続的な偏差を条件とし、変動するベースラインと比較する。これらの背後にあるメトリクスの定義はプロキシのKPIにあり、パイプラインレベルの計測はWebスクレイピングパイプラインの監視にある。
Make the system act on it
グラフを出すだけの監視は、機械が処理すべき問題に人間を関与させ続けてしまう。同じ信号は自動的な振る舞いを駆動すべきである。ブロック信号を蓄積したセッションを引退させる、成功率が崩壊したルートでサーキットブレーカーを開いて応答していないターゲットへの送信を止める、マルチリージョンパイプラインにおけるフェイルオーバーにあるとおり、1つのリージョンが劣化したときに別のリージョンへ作業をシフトする、レート信号が現れたときに自動的にバックオフする、といったことだ。人間には判断が必要なことについてアラートを出すべきであり、ルールで済むことについてアラートを出すべきではない。
The bottom line
大規模な運用では、ヘルスはプロキシの属性ではなく、実行している各ルートの属性である。したがってターゲットとリージョンごとに計測し、セッションのヘルスは独自の短命な信号として扱う。すべての結果をステータスコードではなく検証済みのボディで判断する。これが監視と自己欺瞞との違いである。失敗を分類する。内訳が、速度を落とすべきか、アイデンティティを変えるべきか、ターゲティングを広げるべきか、それとも自分側の設定を修正すべきかを教えてくれる。現在実行していないルートをカバーし、プロバイダーの問題とターゲットの問題を分けるために、薄い層の地理位置プローブとターゲットカナリアを追加する。そして各ルート自身のベースラインからの持続的な偏差、失敗内訳の変化、リトライ比率にアラートを出し、同じ信号を自動的な引退、バックオフ、フェイルオーバーに組み込むことで、誰かが呼び起こされる前にシステム自体が対処できることを対処できるようにする。
その基盤となる層がレジデンシャルプロキシであり、国や都市のターゲティングとセッション制御はリクエストごとに指定されるため、ルート単位のヘルスは統合プロジェクトではなく、自分自身のリクエストにタグを付けるだけの問題になる。GB単位の価格設定により、無駄のないプローブ戦略のコストは、収集そのものに比べてほぼゼロに近い。