新規投稿のお知らせを受信されたい方は、サブスクリプションをご登録ください:

Workersが社内メンテナンススケジュールパイプラインを強化する方法

2025-12-22

10分で読了
この投稿はEnglish繁體中文한국어简体中文でも表示されます。

このコンテンツは自動機械翻訳サービスによる翻訳版であり、皆さまの便宜のために提供しています。原本の英語版と異なる誤り、省略、解釈の微妙な違いが含まれる場合があります。ご不明な点がある場合は、英語版原本をご確認ください。

Cloudflareは世界330都市以上にデータセンターを擁しており、データセンターの運営を計画する際には、ユーザーが気づかないうちに、いつでも簡単にサービスを停止できると思うかもしれません。しかし、実際のところ、破壊的メンテナンスには慎重な計画が必要であり、Cloudflareが成長するにつれて、インフラストラクチャとネットワーク運用の専門家との間の手動調整を通してこうした複雑さを管理することは、ほぼ不可能になりました。

人間が重複するメンテナンスリクエストすべてを追跡し、顧客固有のルーティングルールすべてをリアルタイムで把握することは不可能です。手動による監視だけでは、世界のある地域における日常的なハードウェアの更新が、別の地域の重要なパスと誤って競合しないことを保証できない時点に達していました。

そして、安全策として機能する一元化された自動化された「頭脳」が必要だと考えました。つまり、ネットワーク全体の状態を一度に確認できるシステムが必要だったのです。このスケジューラーをCloudflare Workers上に構築することで、プログラム的に安全制限を強制する方法を作り、どれだけ迅速に進めても、お客様が頼りにするサービスの信頼性を犠牲にすることはありません。

このブログ記事では、その構築方法と、現在の結果についてお伝えします。

重要なメンテナンス作業のリスクを軽減するシステムの構築

都市圏で運用されている多くのCloudflareデータセンターにパブリックインターネットをまとめて接続する、小規模で冗長なゲートウェイグループの1つとして機能するエッジルーターを想像してみてください。人口の多い都市では、この小さなルーター群の背後にある複数のデータセンターが、ルーターが同時にオフライン状態にされたために遮断されないようにする必要があります。

もう一つの保守上の課題は、当社のZero Trust製品である専用CDNエグレスIPです。これは、お客様がユーザートラフィックがCloudflareから出る特定のデータセンターを選択し、低遅延のために地理的に近い配信元サーバーに送信するものです。(この記事では簡潔にするために、専用CDNエグレスIP製品を「Aegis」と呼びます。これは、以前の名前でした。)お客様が選択したすべてのデータセンターが一度にオフラインになると、遅延が大きくなり、5xxエラーが発生する可能性がありますが、これは回避しなければなりません。

当社の保守スケジューラーは、こうした問題を解決します。当社は、常に特定のエリアで少なくとも1つのエッジルーターをアクティブにすることができます。また、メンテナンスを計画する際に、複数の予定イベントの組み合わせによって、お客様のAegisプールのすべてのデータセンターが同時にオフラインになるかどうかを確認できます。

スケジューラを作成する前は、こうした同時発生による障害が発生すると、お客様にダウンタイムが発生する可能性がありました。現在、スケジューラは内部オペレーターに競合の可能性を通知し、関連する他のデータセンターメンテナンスイベントとの重複を避けるための新しい時間を提案できるようになりました。

当社では、エッジルーターの可用性や顧客ルールなどの運用シナリオを保守制約として定義し、より予測可能で安全な保守計画を可能にします。

メンテナンスの制約

すべての制約は、ネットワークルーターやサーバーリストなど、提案されたメンテナンス項目のセットから始まります。そして、提案されたメンテナンス期間と重複するカレンダー上のすべてのメンテナンスイベントを見つけます。

次に、Aegisのお客様のIPプールのリストなど、製品APIを集約します。Aegisは、お客様が特定のデータセンターIDからエグレスを要求したIP範囲のセットを返します(以下に示す)。

[
    {
      "cidr": "104.28.0.32/32",
      "pool_name": "customer-9876",
      "port_slots": [
        {
          "dc_id": 21,
          "other_colos_enabled": true,
        },
        {
          "dc_id": 45,
          "other_colos_enabled": true,
        }
      ],
      "modified_at": "2023-10-22T13:32:47.213767Z"
    },
]

このシナリオでは、データセンター21とデータセンター45が互いに関連しています。Aegisの顧客である9876がCloudflareからのエグレストラフィックを受け取るために、少なくとも1つのデータセンターがオンラインである必要があるからです。データセンター21と45を同時にダウンさせようとすると、コーディネーターは、その顧客のワークロードに意図しない結果が発生する可能性があることを警告します。

当初は、すべてのデータを1つのWorkerに読み込むような単純なソリューションでした。これには、すべてのサーバー関係、製品設定、および製品とインフラの健全性に関するメトリクスが含まれ、制約を計算することができました。概念実証段階でも、「メモリ不足」エラーの問題に直面しました。

Workersプラットフォームの制限をより明確に認識する必要がありました。この方法では、制約のビジネスロジックを処理するために絶対に必要な分だけのデータを読み込む必要がありました。ドイツのフランクフルトでルーターのメンテナンスリクエストが来た場合、地域間の重複がないため、オーストラリアで何が起きているかはどうでもよいことです。したがって、ドイツの隣接するデータセンターにのみデータをロードする必要があります。当社では、データセット内の関係をより効率的に処理する方法が必要でした。

Workers上でのグラフ処理

制約を検討するうちに、それぞれの制約がオブジェクトとアソシエーションという2つの概念に集約されるパターンが出現しました。グラフの理論では、これらのコンポーネントはそれぞれ垂直とエッジと呼ばれます。オブジェクトはネットワークルーターで、関連付けはルーターがオンラインである必要があるデータセンター内のAegisプールのリストです。FacebookのTAOに関する調査論文を参考に、製品とインフラストラクチャのデータを基にグラフインターフェースを構築しました。APIは以下のようなものです:

type ObjectID = string

interface MainTAOInterface<TObject, TAssoc, TAssocType> {
  object_get(id: ObjectID): Promise<TObject | undefined>

  assoc_get(id1: ObjectID, atype: TAssocType): AsyncIterable<TAssoc>
}

核となるインサイトは、関連付けが入力されていることです。例えば、制約はグラフインターフェースを呼び出してAegis製品データを取得することなどがあります。

async function constraint(c: AppContext, aegis: TAOAegisClient, datacenters: string[]): Promise<Record<string, PoolAnalysis>> {
  const datacenterEntries = await Promise.all(
    datacenters.map(async (dcID) => {
      const iter = aegis.assoc_get(c, dcID, AegisAssocType.DATACENTER_INSIDE_AEGIS_POOL)
      const pools: string[] = []
      for await (const assoc of iter) {
        pools.push(assoc.id2)
      }
      return [dcID, pools] as const
    }),
  )

  const datacenterToPools = new Map<string, string[]>(datacenterEntries)
  const uniquePools = new Set<string>()
  for (const pools of datacenterToPools.values()) {
    for (const pool of pools) uniquePools.add(pool)
  }

  const poolTotalsEntries = await Promise.all(
    [...uniquePools].map(async (pool) => {
      const total = aegis.assoc_count(c, pool, AegisAssocType.AEGIS_POOL_CONTAINS_DATACENTER)
      return [pool, total] as const
    }),
  )

  const poolTotals = new Map<string, number>(poolTotalsEntries)
  const poolAnalysis: Record<string, PoolAnalysis> = {}
  for (const [dcID, pools] of datacenterToPools.entries()) {
    for (const pool of pools) {
      poolAnalysis[pool] = {
        affectedDatacenters: new Set([dcID]),
        totalDatacenters: poolTotals.get(pool),
      }
    }
  }

  return poolAnalysis
}

上記のコードでは、2つのアソシエーションタイプを使用しています:

  1. DATACENTER_INSIDE_AEGIS_POOL:データセンターがあるAegis顧客プールを取得します。

  2. AEGIS_POOL_CONTAINS_DATACENTERは、Aegisプールがトラフィックに対応する必要があるデータセンターを取得します。

アソシエーションは、互いに矛盾する状態を示すものです。アクセスパターンは以前とまったく同じですが、グラフの実装により、クエリするデータ量をより細かく制御できるようになりました。以前は、すべてのAegisプールをメモリにロードし、制約のあるビジネスロジック内でフィルタリングする必要がありました。現在は、アプリケーションにとって重要なデータだけを直接取得することができます。

グラフの実装によって、ビジネスロジックを複雑にすることなく、舞台裏でパフォーマンスを向上させることができるため、インターフェースは強力です。これにより、WorkersとCloudflareのCDNのスケーラビリティを利用して、内部システムから非常に迅速にデータを取得することができます。

フェッチパイプライン

新しいグラフ実装の使用に切り替え、より的を絞ったAPI呼び出しを送信します。一晩で応答サイズは100倍減少し、ほんの数件の大規模リクエストの読み込みから多数の小さなリクエストに切り替えました。

これにより、メモリに過剰に読み込む問題は解決しますが、いくつかの大規模なHTTPリクエストではなく、桁違いに小さなリクエストを送信するため、サブリクエストの問題が発生します。一晩で、サブリクエストの制限をすばやく突破するようになりました。

この問題を解決するために、グラフ実装とfetch APIの間にスマートミドルウェアレイヤーを構築しました。

export const fetchPipeline = new FetchPipeline()
  .use(requestDeduplicator())
  .use(lruCacher({
    maxItems: 100,
  }))
  .use(cdnCacher())
  .use(backoffRetryer({
    retries: 3,
    baseMs: 100,
    jitter: true,
  }))
  .handler(terminalFetch);

Goに慣れている人なら、以前にシングルフライトのパッケージを見たことがあるかもしれません。このアイデアから着想を得るや、フェッチパイプラインの最初のミドルウェアコンポーネントは、実行されるHTTPリクエストを消去するため、同じWorkerで重複したリクエストを生成するのではなく、すべて同じPromiseを使用してデータを待機します。次に、軽量のLeast Recently Used(LRU)キャッシュを使用して、以前にすでに確認されたリクエストを内部にキャッシュします。

これらの両方が完了したら、Cloudflareのcaches.default.match機能を使用して、Workerが実行されている地域のすべてのGETリクエストをキャッシュします。パフォーマンス特性が異なる複数のデータソースがあるため、有効期限(TTL)の値を慎重に選択します。例えば、リアルタイムデータは1分間しかキャッシュされません。比較的静的なインフラストラクチャデータは、データのタイプに応じて、1~24時間キャッシュできます。電力管理データは手動で頻繁に変更される可能性があるため、エッジでより長い期間キャッシュすることができます。

これらのレイヤーに加えて、当社では標準的な指数関数的バックオフ、リトライ、ジッターがあります。これにより、ダウンストリームのリソースが一時的に利用できない可能性のあるフェッチコールの無駄を減らすことができます。わずかに後退することで、次のリクエストが正常に取得される可能性が高まります。逆に、Workerがバックオフなしで常にリクエストを送信すると、オリジンが5xxエラーを返し始めた時に、簡単にサブリクエスト制限を突破してしまいます。

総合的に、キャッシュヒット率は最大99%でした。キャッシュヒット率は、Cloudflareの高速キャッシュメモリから提供されたHTTPリクエスト(「ヒット」)と、当社のコントロールプレーンで実行されるデータソースへの低速なリクエスト(「ミス」)に対する割合であり、(ヒット / (ヒット+ミス))として計算されます。レートが高いほど、HTTPリクエストのパフォーマンスが向上し、コストが低くなります。これは、Workerでキャッシュからデータをクエリする方が、別の地域の配信元サーバーからフェッチするよりも桁違いに速いためです。設定を調整した後、メモリとCDNキャッシュのキャッシュヒット率が劇的に上昇しました。当社のワークロードの多くはリアルタイムであるため、1分あたり少なくとも1回は新しいデータをリクエストしなければならないため、ヒット率が100%になることはありません。

フェッチングレイヤーの改善についてお話しましたが、オリジンのHTTPリクエストをどのように高速化したかについては触れませんでした。当社の保守コーディネーターは、ネットワークの劣化やデータセンター内の機器の故障にリアルタイムで対応する必要があります。当社の分散型Prometheusクエリエンジン、Thanosを使って、エッジからコーディネーターへ高性能なメトリクスを届けます。

Thanosでリアルタイムに

グラフ処理インターフェースの使用という選択がリアルタイムクエリにどのように影響したかを説明するために、例を挙げながら説明します。エッジルーターの正常性を分析するために、次のクエリを送信します。

sum by (instance) (network_snmp_interface_admin_status{instance=~"edge.*"})

当初、Prometheusメトリクスを保存するThanosサービスに、各エッジルーターの現在の正常性ステータスのリストを提供し、Worker内のメンテナンスに関連するルーターを手動でフィルタリングする予定でした。これは、多くの理由から最適とは言えません。例えば、Thanosは、デコードとエンコードに必要なマルチMBのレスポンスを返しました。Workerは、特定のメンテナンスリクエストを処理している間、データの大部分をフィルタリングするためだけに、こうした大きなHTTPレスポンスをキャッシュしてデコードする必要もありました。TypeScriptはシングルスレッドで、JSONデータの解析はCPUバウンドであるため、2つの大きなHTTPリクエストを送信すると、一方がブロックされることは、解析が完了するのを待つことになります。

代わりに、グラフを使用して、エッジルーターとスパインルーター間のインターフェースリンクなどの対象となる関係を見つけるだけで、EDGE_ROUTER_NETWORK_CONNECTS_TO_SPINEと表されます。

sum by (lldp_name) (network_snmp_interface_admin_status{instance=~"edge01.fra03", lldp_name=~"spine.*"})

結果は、複数のMBではなく、平均1Kb、または約1000倍小さいものです。デシリアル化のほとんどをThomasに任せるため、Worker内部で必要となるCPUの量も大幅に削減されます。以前説明したように、これは、これらの小さなフェッチリクエストをより多く作成する必要があることを意味しますが、Thanosの前のロードバランサーは、リクエストを均等に分散して、このユースケースのスループットを向上させることができます。

グラフの実装とフェッチパイプラインは、何千もの小さなリアルタイムリクエストの「大量の攻撃」を制御することに成功しました。しかし、履歴の分析によっては、異なるI/Oの課題が浮き彫りになっています。小さく特定の関係を取得するのではなく、数か月のデータをスキャンして、競合するメンテナンス期間を見つける必要があります。以前は、ThanosはオブジェクトストアであるR2に大量のランダムリードを発行していました。パフォーマンスを失うことなく、この膨大な帯域幅のペナルティを解決するため、今年、Observabilityチームが社内で開発した新しいアプローチを採用しました。

過去のデータ分析

当社のソリューションが正確で、Cloudflareネットワークの成長に合わせて拡張できるかどうかを判断するには、過去のデータに頼らなければならないメンテナンスユースケースが十分にあります。インシデントを引き起こしたくないですし、提案された物理的なメンテナンスが不必要にブロックされることも避けたいと考えています。この2つの優先事項のバランスをとるために、2か月前、あるいは1年前に発生した保守イベントに関する時系列データを使用して、保守イベントがどの程度の頻度で当社の制約の一つに違反しているかを把握することができます。エッジルーターの可用性やAegisなどです。今年初め、私たちはThanosを使用して、ソフトウェアをエッジに自動的にリリースおよび元に戻すことについてブログを書きました。

Thanosは主にPrometheusにファンアウトしますが、Prometheusのリテンションがクエリーに応答するには不十分な場合、オブジェクトストレージ(この場合はR2)からデータをダウンロードする必要があります。Prometheus TSDBブロックはもともとローカルSSD用に設計されたもので、ランダムなアクセスパターンに依存していますが、オブジェクトストレージに移行するとボトルネックになります。スケジューラが矛盾する制約を特定するために、数か月分の過去のメンテナンスデータを分析する必要がある場合、オブジェクトストレージからのランダムな読み取りには多額のI/Oペナルティが発生します。これを解決するために、これらのブロックをApache Parquetファイルに変換する変換レイヤーを実装しました。Parquetは、ビッグデータ分析にネイティブな列形式で、データを行ではなく列ごとに整理し、豊富な統計と併せて、必要なデータだけを取得することができます。

さらに、TSDBブロックをParquetファイルに書き換えているため、数回の大きな連続チャンクでデータを読み取れるような方法でデータを保存することもできます。

sum by (instance) (hmd:release_scopes:enabled{dc_id="45"})

上記の例では、タプル「(__name__, dc_id)」をプライマリソートキーとして選択し、名前「hmd:release_scopes:enabled」と「dc_id」の同じ値が近くにソートされるようにします。

Parquetゲートウェイは、クエリに関連する特定のカラムのみをフェッチするために、正確なR2範囲のリクエストを発行するようになりました。これにより、ペイロードがメガバイトからキロバイトに削減されます。さらに、これらのファイルセグメントは不変であるため、Cloudflare CDNで積極的にキャッシュすることができます。

これにより、R2は低遅延のクエリエンジンになり、長期的な傾向に対して複雑な保守シナリオを即座にバックテストすることができ、元のTSDBフォーマットで見られたタイムアウトや高いテール遅延を回避できます。下のグラフは最近の負荷テストを示しています。Parquetは、同じクエリーパターンで旧システムと比較して、最大15倍のP90パフォーマンスを達成しました。

Parquetの実装の仕組みをより深く理解するには、PromCon EU 2025でのこの講演「Beyond TSDB: Unlocking Prometheus with Parquet for Modern Scale」をご覧ください。

拡張を想定して構築する

Cloudflare Workersを活用することで、メモリ不足のシステムから、データをインテリジェントにキャッシュし、効率的な可観測性ツールで製品とインフラのデータをリアルタイムで分析できるシステムへと移行することができました。ネットワーク成長と製品パフォーマンスのバランスをとる保守スケジューラーを構築しました。

ただし、「バランス」は動く標的です。

日々、世界中にハードウェアが追加され、お客様のトラフィックを妨げずに保守を行うために必要なロジックは、製品や保守作業の種類が増えるにつれて指数関数的に困難になっています。これまでは一連の課題を乗り越えてきましたが、今はこの大規模なスケールでしか現れない、より微妙で複雑な課題に直面しています。

難しい問題を恐れないエンジニアが必要です。当社のインフラストラクチャチームに参加して、私たちと共に構築しましょう。

Cloudflareは企業ネットワーク全体を保護し、お客様がインターネット規模のアプリケーションを効率的に構築し、あらゆるWebサイトやインターネットアプリケーションを高速化し、DDoS攻撃を退けハッカーの侵入を防ぎゼロトラスト導入を推進できるようお手伝いしています。

ご使用のデバイスから1.1.1.1 にアクセスし、インターネットを高速化し安全性を高めるCloudflareの無料アプリをご利用ください。

より良いインターネットの構築支援という当社の使命について、詳しくはこちらをご覧ください。新たなキャリアの方向性を模索中の方は、当社の求人情報をご覧ください。
Cloudflare Workers信頼性Prometheusインフラストラクチャ

Xでフォロー

Cloudflare|@cloudflare

関連ブログ投稿

2026年4月20日

Building the agentic cloud: everything we launched during Agents Week 2026

Agents Week 2026 is a wrap. Let’s take a look at everything we announced, from compute and security to the agent toolbox, platform tools, and the emerging agentic web. Everything we shipped for the agentic cloud. ...

2026年4月20日

The AI engineering stack we built internally — on the platform we ship

We built our internal AI engineering stack on the same products we ship. That means 20 million requests routed through AI Gateway, 241 billion tokens processed, and inference running on Workers AI, serving more than 3,683 internal users. Here's how we did it. ...

2026年4月16日

Building the foundation for running extra-large language models

We built a custom technology stack to run fast large language models on Cloudflare’s infrastructure. This post explores the engineering trade-offs and technical optimizations required to make high-performance AI inference accessible....