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

当社の開発方法:Cloudflare Radar 2.0を支える技術

2022-11-17

11分で読了
この投稿はEnglish繁體中文FrançaisDeutschPortuguêsEspañol简体中文でも表示されます。

Radar 2.0 Radar 1.0の学習に基づいて構築され、先月のCloudflareのバースデーウィークに完全な製品刷新として発表されました。当社は、ユーザーがインサイトの発見やデータをナビゲートをより簡単できるようにし、また、全体においてこれまで以上に質が高く高速になったユーザー体験を提供したいと考えていました。

How we built it: the technology behind Cloudflare Radar 2.0

当社は目下のところSupercloudの構築に取り組んでいます。Cloudflareの製品は現在、ネットワーキング、セキュリティ、アクセスコントロール、コンピューティング、ストレージなどにおいて数百もの機能を備えています。

本記事では新しいRadarの構築プロセスをエンジニアリングの観点から説明しようと思います。Radarの構築は、厳しい要件と複数のアーキテクチャレイヤーを含む多少複雑なウェブサイトの構築を当社のスタック上に行うことが、誰にでもできてしかも簡単であることを示すことを目的としていました。

他の開発者が従来のソフトウェア・アーキテクチャから転換し、より効率的な最新技術を使ってアプリケーションを構築するきっかけになればと願っています。

ハイレベル・アーキテクチャ

下図はRadar 2.0のアーキテクチャを鳥瞰したものです。ご覧のように大きく3つのレイヤーに分かれています。

  • Coreレイヤーはデータレイク、データ探索ツール、バックエンドAPIを保持する場所です。

  • Cloudflareのネットワークレイヤーは、Radarをホストして実行し、公開APIを提供する場所です。

  • クライアントレイヤーは基本的にブラウザで実行される、その他のすべてのものとなります。私たちはこれをRadar Webアプリと呼んでいます。

ご覧の通り、Cloudflareの製品_はさまざまなところで目にすることができます_。Cloudflare製品はコードを大きなスケールでホストし安全に実行するための基盤となるリソースだけでなく、アプリケーションをエンドツーエンドで実行するために必要な、その他のビルディングブロックも提供しています。

これらの機能はワンクリックと数行のコードの距離で、すぐに利用可能であり、当社のエコシステムとツールに緊密に統合されているため、エンジニアリングチームは何度も土台から再開発する必要がなく、本質的なもの、つまりアプリのロジックに時間がかけられます。

それでは詳しい内容に入りましょう。

Cloudflare Pages

Radar 2.0 は Cloudflare Pagesという開発者向けのウェブサイトホスティングプラットフォームを使用してデプロイされています。初期の頃は静的アセットのみをPagesでホストすることができました。これは、 HugoJekyllGatsby といった静的サイト生成ツールに統合する場合などに役に立ちました。それでも、アプリケーションにサーバーサイドの何らかのコンピューティングや高度なロジックが必要な場合、1つのデプロイメントで解決できるわけではありません。

そこへ、Pagesは最近カスタムWorkerスクリプトを実行するためのサポートを追加しました。関数 で、サーバーサイドのコードを実行し、通常は別の Workerを使って実装するようなあらゆる種類の動的機能を有効にすることができるようになったのです。

Cloudflare Page関数は通常のWorkerと同じように、Durable ObjectsKVR2D1を使用することもできます。これを行う方法に関する優れたドキュメントと、開発者向けドキュメントの詳細もご覧いただけます。さらに、すべての手順を詳細に説明するフルスタックアプリケーションの構築方法に関して書いた記事もあります。

Radar 2.0がサーバーサイドの機能を必要とする理由は2つあります。

  • それはRadarをレンダリングし、Remixのサーバーサイドを動作させること。

  • そしてフロントエンドAPIを実装してサービスを提供することです。

Remixとサーバーサイドレンダリング

当社ではRemix with Cloudflare Pages on Radar 2.0を使用しています。

Remixはサーバーまたはクライアントモデルを採用しており、ユーザーのネットワークは制御できない前提で動作します。そのため、ウェブアプリケーションは、有線を通じて送信するJavascript、CSS、JSONの量を減らす必要があります。そのために、ロジックの一部をサーバーに移します。

この場合、クライアントブラウザはプリレンダリングされたDOMコンポーネントと、UIのニーズに合わせて適切に調整されたJSON、Javascript、CSSのコードでプリフェッチされたAPI呼び出しの結果を取得することになります。以下、 技術的な説明 に、より詳細な説明があります。

通常、Remixはこれらすべてを行うためにNode.jsサーバーを必要としますが、Cloudflare WorkersとPages上で実行することも可能なのです。

以下はCloudflare Pagesを使って、Workers上でRemixサーバを動作させるためのコードです。

Remixにおいて、routes はユーザーがアプリとやりとりして変更する(たとえば、メニューオプションをクリックする)ときの変更を処理します。Remix のルーティングは loaderactiondefault exportを持つことができます。_loader_はデータを取得するためのAPIコールを処理します(GETメソッド)。 _アクション_はサーバーへの送信(POST、PUT、PATCH、DELETEメソッド)を処理して応答を返します。_default_export はそのルーティングに対して返される React のUIコードを処理します。 _default_エクスポートがないルーティングはデータのみを返します。

import { createPagesFunctionHandler } from "@remix-run/cloudflare-pages";
import * as build from "@remix-run/dev/server-build";

const handleRequest = createPagesFunctionHandler({
  build: {
    ...build,
    publicPath: "/build/",
    assetsBuildDirectory: "public/build",
  },
  mode: process.env.NODE_ENV,
  getLoadContext: (context) => ({
    ...context.env,
    CF: (context.request as any).cf as IncomingRequestCfProperties | undefined,
  }),
});

const handler: ExportedHandler<Env> = {
  fetch: async (req, env, ctx) => {
    const r = new Request(req);
    return handleRequest({
      env,
      params: {},
      request: r,
      waitUntil: ctx.waitUntil,
      next: () => {
        throw new Error("next() called in Worker");
      },
      functionPath: "",
      data: undefined,
    });
  },
};

Remixはサーバとクライアントの両方で動作するため、機能が高まっていきます。何がサーバサイドでプリフェッチされ計算され、何がネットワーク接続を経由しなければならないかを知ることができ、パフォーマンスと応答性のためにすべてを最適化することができるのです。

こちらはOutage Centerのページで、読みやすくするために簡略化した Radar ルートの例です。

その結果がこちらです。

import type { MetaFunction } from "@remix-run/cloudflare";
import { useLoaderData } from "@remix-run/react";
import { type LoaderArgs } from "@remix-run/server-runtime";

export async function loader(args: LoaderArgs) {
  const ssr = await initialFetch(SSR_CHARTS, args);
  return { ssr, };
}

export default function Outages() {
  const { ssr } = useLoaderData<typeof loader>();

  return (
    <Page
      filters={["timerange"]}
      title={
        <>
          <Svg use="icon-outages" />
          {t("nav.main.outage-center")}
        </>
      }
    >
      <Grid columns={[1, 1, 1, 1]}>
        <Card.Article colspan={[1, 1, 1, 1]} rowspan={[1, 1, 1, 1]}>
          <Card.Section>
            <Components.InternetOutagesChoropleth ssr={ssr} />
          </Card.Section>
          <Divider />
          <Card.Section>
            <Components.InternetOutagesTable ssr={ssr} />
          </Card.Section>
        </Card.Article>
      </Grid>
    </Page>
  );
}

RemixとSSRはLighthouseのスコアとSEOにも役立ちます。CLS(累積レイアウトシフト)First Contentful PaintLargest Contentful Paintのようなメトリクスを、サーバからブラウザへのフェッチと情報の移動回数を減らし、DOMをプリレンダリングすることで大幅に改善することができるのです。

Remixにアプリを移植している別のプロジェクトはCloudflare TVです。メトリクスが変更前と変更後にどのように変化したかを示しています。

RadarのDesktop Lighthouseスコアはパフォーマンス、アクセシビリティ、ベストプラクティス、SEOでほぼ100%の結果となりました。

Radar 2.0で多用しているCloudflare製品としては、Speedも挙げられます。ここではその中でもEarly Hints機能の説明をしたいと思います。Early Hints は、新しいWeb標準で、サーバーがウェブページのレンダリングに必要なアセットを、リクエスト中にブラウザへ通知するための新しい HTTP 103 ヘッダーを定義しており、読み込み時間を大幅に改善します。

Cloudflare Pages with Early Hintsを利用することができます。

API

Radarには2つのAPIがあります。データソースに直接アクセスできるバックエンドと、インターネット上で利用可能なフロントエンドです。

バックエンドAPI

バックエンドAPIは、PythonPandasFastAPIを使用して記述され、Cloudflare AccessJWTトークン、  、認証されたオリジンプル(AOP)構成によって保護されています 。Pythonを使用するとチーム、エンジニア、データサイエンティストの誰もが簡単に共同作業ができるようになり、APIの改善と拡張に貢献できます。当社のデータサイエンスチームは、データ探索ワークフローの一部としてJupyterHubJupyter Notebooksを使用しているので、コード、アルゴリズム、モデルのプロトタイピングと再利用が特に簡単かつ迅速になります。

その後、StrawberryベースのGraphQLサーバーを介して、上流のフロントエンドAPIと接続します。GraphQLを使用すれば、複雑なクエリーを簡単に作成でき、社内ユーザーやアナリストが当社の膨大なデータコレクションを使ってレポートを作成する際に必要となる柔軟性を得ることができます。

フロントエンドAPI

私たちはRadarのフロントエンドAPIをCloudflareWorker上に構築しました。このworkerには主に2つの機能があります。

  • GraphQLを使ってバックエンドAPIからデータを取得し変換する。

  • 公開されているRadarを含むREST APIを提供し、誰でも利用することができるようにする。

コアAPIの前でworkerを使うことで、マイクロサービスの追加や分離が容易になり、それと同時に次のような注目すべき機能が追加されます。

  • CloudflareのCache APIでは、何をどれくらいの期間キャッシュするかをより細かく制御でき、POSTリクエストとカスタマイズ可能なキャッシュコントロールヘッダをサポートしており、当社でも使用しています。

  • R2を使ったステイルレスポンス。バックエンド APIが何らかの理由でリクエストに応答できず、かつキャッシュされた古い応答がある場合、R2から直接応答されるので、エンドユーザはより良いエクスペリエンスを得ることができます。

  • CSVJSON出力形式です。CSV形式は使い勝手がよく、データサイエンティストやアナリストなどのAPI使用や、他のツールから直接当社のAPIデータ利用が容易になります。

OpenAPI 3スキーマジェネレータとバリデータのオープンソース化

フロントエンドAPIの最後の特徴として、OpenAPI 3のサポートがあります。OpenAPIのスキーマを自動的に生成し、ユーザーの入力を検証します。これは当社開発のitty-router上に構築したカスタムライブラリによって行われ、私たちもを使用しています。こちらは現在オープンソースとなっています。

itty-router-openapiでは、Cloudflare Workers 用の勘弁かつコンパクトなOpenAPI 3スキーマ生成および検証ツールが使用できます。 GitHubリポジトリ で詳細な情報や使い方をご確認ください。

開発者ドキュメント

本日、Radar APIの開発者向けドキュメントページも公開しました。当社のデータライセンス、基本概念、利用開始方法、利用できるAPIメソッドに関する詳細な情報をご覧いただけます。Cloudflare RadarのAPIは無料となっており、学者やデータ研究者、その他のウェブ愛好家が、当社のグローバルネットワークからのデータに基づいて、世界中のインターネット利用状況の調査ができるようになりました。

APIを使いやすくするために、Colab Notebookテンプレート も用意しましたので、コピーして、ユースケースに合わせて拡張して使ってみてください。

Radarアプリ

Radarアプリはブラウザ上で動作するコードです。ここまでRemixの話をしてきましたが、それ以外に使用しているものについても触れたいと思います。

Radarは多くのデータ可視化に依存しています。チャートやマップのようなものは、私たちにとって不可欠なものです。当社では他の2つのフレームワークの上に、再利用可能な視覚化コンポーネントのライブラリを構築することにしました。「React用の表現力豊かな低レベルの視覚化プリミティブのコレクション」であるvisx、データに基づいてDOMを操作するための強力なJavaScriptライブラリであるD3、オープンソースの地図視覚化スタックであるMapLibreがあります。

ここではビジュアライゼーションコンポーネントの1つをご紹介します。当社ではこれを「PewPewマップ」と呼んでいます。

そして、これをページ内で使用する必要が際のRemix Reactのコードは以下の通りです。

SVG

<Card.Section
    title={t("card.attacks.title")}
    description={t("card.attacks.description")}
  >
    <Flex gap={spacing.medium} align="center" justify="flex-end">
      <SegmentedControl
        label="Sort order:"
        name="attacksDirection"
        value={attacksDirection}
        options={[
          { label: t("common.source"), value: "ORIGIN" },
          { label: t("common.target"), value: "TARGET" },
        ]}
      onChange={({ target }: any) => setAttacksDirection(target.value)}
      />
    </Flex>

    <Components.AttacksCombinedChart
      ssr={ssr}
      height={400}
      direction={attacksDirection}
    />
  </Card.Section>

Radarのもうひとつの変更点は画像やグラフィックアセットを Scalable Vector Graphicsに切り替えたことです。SVGは本質的に宣言的なグラフィックス言語であるため非常に優れており、ベクトル情報を持つXMLテキストファイルです。そしてもちろん、どんなサイズでもレンダリングでき、どんなデバイスや解像度でも、美しく鮮明な結果を生み出します。

また、SVGはビットマップ形式に比べてサイズが非常に小さく効率的で、 国際化に対応していて、他の言語への翻訳(ローカライズ)が容易になるため、さらに優れた アクセシビリティが実現します。

レーダーバブルチャートの例です。SVGコードと文字列が埋め込まれているのがわかるかと思います。

Cosmos

React Cosmosは、UIコンポーネントを分離して開発およびテストするための「サンドボックス」です。このプロジェクトに最適だろうと考え、Radar 2.0でCosmosを使用することにしました。

  1. ビジュアルコンポーネントを数多く持ち、中には複雑でさまざまな設定オプションや機能があるものも存在します。

  2. このコンポーネントは異なるコンテキスト、異なるデータの複数のページで高い再利用性を発揮します。

  3. 当社には多職種からなるチームがありますが、誰もがプルリクエストを送り、フロントエンドのコードを追加したり変更したりすることができます。

Cosmosはシンプルなボタンから複雑なチャートまで、すぐに使えるビジュアライゼーションやウィジェットのパレットを見ることができるコンポーネントライブラリとして機能し、リアルタイムでそのオプションを操作して何が起こるかを確認することができるのです。デザイナーやエンジニアだけでなく、それ以外のプロジェクト関係者にも可能です。そのおかげでチームのコミュニケーションが効果的に改善され、貢献とイテレーションが迅速に行えるようになりました。

当社でCosmosを使用したときの画面がこちらです。

継続的インテグレーションと開発

継続的インテグレーションは最新のソフトウェアを開発するすべてのチームにとって重要事項です。Cloudflare Pagesには直接アップロードを使用するCIツールと連携するための複数のオプションがあります。 ドキュメントと例 GitHub Actions、CircleCI、Travis でそれを行う方法を載せていますが、それだけではありません。

当社では社内でBitBucketとTeamCityを使ってリリースのビルドとデプロイを行っています。ワークフローは承認されたPRとフォローアップ・マージに基づき、Radar 2.0を数分以内に自動的にビルド、テスト、デプロイを行います。

ユニットテストは Vitest で行い、E2Eテストは Playwrightで行います。Visual Regressionテストが計画中で、 Playwrightもそのサポートに活用できます

さらに本番稼動前にリリースのステージングとテストをするための環境を複数あります。CI/CDの設定により、ある環境から別の環境への切り替えや、望ましくないデプロイメントのロールバックが簡単に行えます。

またCloudflare Pagesでは、Preview deployments、aliases、Branch build controlsを使用すればそれも簡単です。通常のWorkerでもEnvironmentsを使えば同じことが言えます。

高速プレビューと通知

かつてのRadar 1.0はCI/CDのスピードがそれほど速かったわけではありません。クイックフィックスでもコードをコミットしてからデプロイするまで30分ほどかかることもあり、悔しい思いをしました。

そこで私たちは新しいCIが速く、効率的で、猛烈な勢いで行われるよう心血を注ぎました。

最終的にはコード・リポジトリにプッシュされたすべてのコミットに高速プレビュー・リンクを付けるという最高の結果を得ることができたのです。ビルド時のインテリジェントなキャッシュと、コミットが通常のリリースブランチの外にある場合の非同期テストの組み合わせにより、デプロイ時間を数秒に短縮することが可能となりました。

これは誰かが任意のブランチにコードをプッシュしたときに、チャットに表示される通知です。

誰でもチャットで特定のブランチのスレッドを追うことができ、新たな変更があった場合には通知が届きます。

ビルドの高速化、プレビューリンク、通知機能はゲームチェンジャーと言えるでしょう。エンジニアはアイデアやクイックフィックスから始まり、その結果をプロダクトマネージャーや他のチームメンバーにリンクで提示できます。リンクをクリックすれば、誰でもすぐに完全に動作するエンドツーエンド版のRadarの変更を確認できるのです。

アクセシビリティとローカライゼーション

Cloudflareではウェブアクセシビリティに取り組んでいます。最近、CloudflareのDashboardをアップグレードした方法についての発表がありました。業界のアクセシビリティ標準に準拠したものですが、この前提は当社の全プロパティに当てはまります。ローカライゼーションについても同様です。2020年、当社はダッシュボードを国際化し、新しい言語とロケールのサポートを始めました。

アクセシビリティとローカライゼーションは両立するものであり、どちらも重要ですが、同時に別種のものでもあります。 Web Content Accessibility Guidelines とコントラスト、タグ、SVG、ショートカット、ジェスチャー、その他多くの使用など、アクセシビリティに関するさまざまなベストプラクティスを定義しています。A11Yプロジェクトページ はさらに学習するための優れたリソースです。

L10nとも呼ばれるローカリゼーションは、新しいプロジェクトを開始する際に必要な技術的な要件と言えます。それはエンジニアリングの依存関係やコードの書き換えなしに、新しい翻訳を簡単に追加できるようなライブラリやフレームワークのセットを適切に選択することです。

Radarには両方の面で優れたパフォーマンスを発揮して欲しいと考えていました。当社のデザインシステムはCloudflareのデザインとブランドガイドラインに真剣に取り組み、できるだけ多くのA11Yグッドプラクティスを加え、アプリはページとUIコンポーネント全体でローカライズ文字列を完全に考慮しています。

新しい言語の追加は1つのJSONファイルを翻訳するのと同じくらい簡単な作業です。以下はデフォルトのアメリカ英語の文字列を含むen-US.jsonファイルのスニペットです。

近々、他の言語でもRadarをリリースする予定ですので、ぜひご期待ください。

{
  "abbr.asn": "Autonomous System Number",
  "actions.chart.download.csv": "Download chart data in CSV",
  "actions.chart.download.png": "Download chart in PNG Format",
  "actions.chart.download.svg": "Download chart in SVG Format",
  "actions.chart.download": "Download chart",
  "actions.chart.maximize": "Maximize chart",
  "actions.chart.minimize": "Minimize chart",
  "actions.chart.share": "Share chart",
  "actions.download.csv": "Download CSV",
  "actions.download.png": "Download PNG",
  "actions.download.svg": "Download SVG",
  "actions.share": "Share",
  "alert.beta.link": "Radar Classic",
  "alert.beta.message": "Radar 2.0 is currently in Beta. You can still use {link} during the transition period.",
  "card.about.cloudflare.p1": "Cloudflare, Inc. ({website} / {twitter}) is on a mission to help build a better Internet. Cloudflare's suite of products protects and accelerates any Internet application online without adding hardware, installing software, or changing a line of code. Internet properties powered by Cloudflare have all web traffic routed through its intelligent global network, which gets smarter with every request. As a result, they see significant improvement in performance and a decrease in spam and other attacks. Cloudflare was named to Entrepreneur Magazine's Top Company Cultures 2018 list and ranked among the World's Most Innovative Companies by Fast Company in 2019.",
  "card.about.cloudflare.p2": "Headquartered in San Francisco, CA, Cloudflare has offices in Austin, TX, Champaign, IL, New York, NY, San Jose, CA, Seattle, WA, Washington, D.C., Toronto, Dubai, Lisbon, London, Munich, Paris, Beijing, Singapore, Sydney, and Tokyo.",
  "card.about.cloudflare.title": "About Cloudflare",
...

Radar ReportsとJupyterノートブック

Radar Reportsはデータ探索とストーリーテリングを駆使して、特定のテーマを深く分析したドキュメントです。レポートによっては更新をしばしば行う傾向があります。Radar Reportsの例としては、四半期ごとの DDoS攻撃動向や、IPv6採用があります。

これらのReportのソースはJupyter Notebooksです。データサイエンスチームは社内ツールJupyter Hubを使って、他のステークホルダーと一緒にユースケースやテーマに取り組んでいます。すべてのイテレーションと探索が行われて作業が終了すると、ノートブックが作成されます。

Jupyter NotebookはJSONドキュメントテキスト、ソースコード、画像やチャートなどのリッチメディア、その他のメタデータを含むものです。データサイエンス・プロジェクトを表現するためのデファクトスタンダードであり、すべてのデータサイエンティストが使用しています。

Radar 1.0ではJupyterノートブックからRadarページへの変換は、多くのエンジニアリングとデザインリソースを含む長時間の手動プロセスで、関係者に多大なフラストレーションを与えていました。すでに公開されているノートブックを更新するだけでも、問題が発生することがよくあります。

Radar 2.0がこの状況を一新しました。現在ではJupyter Notebookを取り込んで、完全に自動化されたプロセスを実現しています。簡単なルールと社内ガイドラインに沿ってデザインされていれば、自動的に変換し、出来上がったHTMLとアセットをR2バケットにホスティングし、Reportページで公開することができます。

HTMLへの変換は当社のデザインシステムとUIコンポーネントを考慮して行われます。その結果、通常は長い形式の美しいドキュメントが作成され、Radar のルック・アンド・フィールに完全に一致します。

このツールはいずれオープンソース化して誰でも使えるようになる予定です。

Cloudflareの機会を増やして、心配を減らす

ビジネスやロジックの枝葉を省いて、次世代アプリを構築するためにCloudflareの製品や機能を活用する例を紹介してきました。それでもカバーしきれなかったこともあります。

アプリを稼働させたら悪質なトラフィックや悪意ある行為から保護する必要があります。CloudflareならばDDoSWAFBot Managementの保護をクリック一回で行えます。

セキュリティルールの一例をご紹介します。これはCloudflareが検出し、当社のルールに従って処理するため、アプリでは心配する必要のないトラフィックです。

もう一つ気にかけなくてもいいのは、旧サイトから新サイトへのリダイレクトです。CloudflareにはBulk Redirectsという機能があり、ダッシュボードで直接簡単にリダイレクトリストを作成することができます。

また、ダッシュボードを使用して何ができるかについて話をするときには、実際には、CloudflareのAPIを使用してまったく同じことができるのだと説明するのも重要です。当社のダッシュボードはCloudflareのAPI上に完全に構築されています。そして、あなたがコードのような人としてのインフラストラクチャであるとするならば、当社がお応えします。つまり、Cloudflare Terraformプロバイダーを使用できるということです。

Worker、R2バケット、ページサイトのデプロイと管理もちゃんとスクリプトが可能です。 Wranglerはそれ以上のことを行うためのコマンドラインツールであり、デプロイ前にコンピューター上でスタックをエミュレートして、アプリ全体をローカルで実行できるようにします。

最後に

Radarチームの本記事を楽しんでいただき、Supercloud上に次のアプリを構築するヒントを得てもらえたら嬉しいです。当社はRadar 2.0の新機能の改善とイノベーションを続け、私たちの発見を共有するとともに、ツールをオープンソースとして使ってもらいたいと思っています。

同時にRadar roomを当社のDevelopers Discord Server上に開設しました。ぜひルームに参加していただき、質問も残してもらえたらとお思います。フィードバックを受け、ウェブテクノロジーについて議論することをチーム一同楽しみにしています。

またTwitterのアカウントをフォローしていただくと、さまざmなRadarの最新情報を入手することができます。

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

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

より良いインターネットの構築支援という当社の使命について、詳しくはこちらをご覧ください。新たなキャリアの方向性を模索中の方は、当社の求人情報をご覧ください。
Developer WeekRadarCloudflare WorkersCloudflare Pages開発者Developer Platform

Xでフォロー

Celso Martinho|@celso
Cloudflare|@cloudflare

関連ブログ投稿