最近は Cloudflare Workers が熱くて、週末はずっとその調査しています。この記事はそのまとめです。

注意点として、手元でいろいろなパターンで動かして試してはいますが、プロダクション環境で運用したわけではないです。それを踏まえた上でお読みください。

特に断りが無い限り、引用文は DeepL で翻訳したものです。

Cloudflare Workers とはなにか

Cloudflare Workers | サーバーレスコンピューティング | Cloudflare

一言でいうなら 「ServiceWorker の API が CDN Edge 上で動く JavaScript 処理系」 です。

Technology Radar では、まだ ASSESS(調査) フェーズという扱いです。

Cloudflare Workers | Technology Radar | ThoughtWorks

とはいえ、この説明だけなら新しい Node.js PaaS (FaaS) と思ってしまうかもしれませんが、使ってみた感じ Node の FaaS と捉えると齟齬があり、実態としては CDN で L7 プロキシを JavaScript で書けるもの、と捉えた方が良さそうでした。以下、その理由を説明します。

CDN のおさらい

(わかってる人は読み飛ばしてください)

CDN は、主に静的コンテンツの効率的な送信を目的とした、地球規模のネットワークです。Web サイト訪問者がそれらのコンテンツにアクセスするとき、そのユーザーに一番近いエッジサーバーが応答するようにルーティングされます。またそれらのサーバーは、OS のカーネルレベルでの最適化や各種クラウドへの専用線への乗り入れ、また通信プロコトルの最適化がなされており、普通のサーバーが返すよりも高速なことが多いです。

その性質上、個人や末端のサービス事業者が自力で構築するのは現実的ではなく、既存の CDN 事業者のネットワーク上を借りることになります。Akamai, AWS Cloudfront, Fastly, Cloudflare などが一般的です。

CDN のエッジで JavaScript を実行するとはどういうことか

自力で地理的なルーティングしているのでもない限り、普通は HTTP(S) リクエストは一つの地理的な実体を持つアプリケーション・サーバー(のクラスター)に到達して、そこでリクエストが返却されます。

エッジサーバーで JavaScript が動くということは、アプリケーションサーバーに届く前段でそのリクエストを扱うことができます。ある種の L7 プロキシと言えるかもしれません。そのままアプリケーションサーバーにプロキシしてもよいし、もしキャッシュをもっていれば、アプリケーションサーバーに到達させずにエッジが自力でレスポンスを送ってしまうこともできます。

例えばアプリケーション・サーバーまで地理的に 100ms、エッジまで 15ms だとして、1RTT(ラウンドトリップタイム)はその二倍だとして、170ms を節約できます。

複雑なアプリケーションではアプリケーションレイヤーで 3~5 RTT あるケースもあるので、そうなるともっと効いてくるわけです。

Edge スクリプトで動くサービスは Cloudflare Workers だけではなく、他にも次のようなサービスがあります。

一年前に fly.io の紹介記事を書きました。Edge Worker PaaS の fly.io が面白い - mizchi's blog

またミドルウェアレベルでは Varnish VCL という DSL で、リクエストを制御することができました。Fastly VCL はこれです。

VCL - Varnish Configuration Language — Varnish version trunk documentation

なぜパフォーマンスが大事なのか

10 年代後半のフロントエンドは、開発者体験の向上のためにエンドユーザーの体験を損ねてしまっているところがありました。また、閲覧環境の主流がデスクトップにからモバイルに推移し、デスクトップの光回線より数倍貧弱な 4G ネットワークに合わせたコンテンツの提供が求められています。

今では、如何に速くファーストビューに到達させるかの指標が提案されています。とくに WebVitals と呼ばれる指標は、Google の SEO にも関わってきます。

パフォーマンスチューニングの解の一つとして「HTML のリクエストがアプリケーション・サーバーに到達したら負け」というものがあります。アプリケーションサーバーに到達するということは、大抵は裏で SQL が流れるなどして重いので、そもそも到達させないキャッシュファーストな設計を行い… そのために Edge Side Scripting は、パフォーマンスのために新しい仕組みを提供してくれるレイヤーを提供してくれるわけです。

パフォーマンスが大事か?と問えばみんな YES と応えるとは思うのですが、実際にはそのために何らかの施策を取れている人は少ない、という肌感があります。

Cloudflare Workers の何が嬉しいか

他社のものと比較しながら、今の自分は Cloudflare Workers が一番良いと考えています。その理由は…

  • 応答性がダントツに良い
  • 安い。特に初期費用が \$5/month
  • 仕様やツール周りが整っていて開発体験が良い

なぜ、これが実現できているかは、その技術詳細を追うことで理由がわかったので解説していきます。

最初に、Cloudflare Workers の制約を知ることで、そのアーキテクチャと活用法を考えられると思ったので、そこから深堀りしていくことにします。

Workers の制約と Lambda@Edge との比較

何ができて、何ができないかは、 制限とプライシングを見るのが一番手っ取り早いです。

https://developers.cloudflare.com/workers/platform/limits#worker-limits

Bundled は \$5/month から使える有料プランです。初期費用が安いのは嬉しいですね…

目を引くのは CPU runtime が有料でも 50ms とかなり短いです。そしてメモリの 128MB 制限も結構気になるところです。(注:V8 の同期処理の時間で、非同期処理を待っている間はカウントされないので、外部の DB を叩いたりリバースプロキシみたいな用途なら問題になりません)

128MB のメモリ制限も、大きなデータを扱う一度にメモリに載せるのではなく Stream で扱うことが奨励されています。

Using Streams · Cloudflare Workers docs

例えば、React の巨大 SPA を SSR させると 60ms 掛かったりすることがあります。そもそもそういう用途だとメモリ 128MB のクオータを超えそうな気がするので、CPU ヘビーな用途は怪しい気がします。ここでアプリケーションを動かしたい場合、アプリケーションの設計そのものを再考する必要がありそうです。

次に Lambda Edge とのパフォーマンス比較を見てみましょう。

Serverless Performance: Cloudflare Workers, Lambda and Lambda@Edge

大事な部分を翻訳します。


これは、各サービスへのリクエストのうち、指定されたミリ秒数よりも速いリクエストの割合を示すグラフです。これは、過去 12 時間の間に世界中から均等にサンプリングされた何千ものテストに基づいています。95 パーセンタイルでは、Workers は Lambda Funcion よりも 441%速く、Lambda@Edge よりも 192%速くなっています。
(中略)
50 パーセンタイルの Workers の速度は 13ms で、パケットがバージニア州までの半分の距離を通過するよりもはるかに速いです。95 パーセンタイルでは、Lambda で 882ms、Lambda@Edge で 216ms、Workers で 40ms となります。あなたのユーザーの 5%のユーザーは、可能な限り単純な Lambda の応答をほぼ 1 秒待っていることになり、応答性の高いアプリケーションを構築することが不可能になります。

Cloudflare の発表資料なので、多少割り引いてみる必要がありますが、かなり良好なパフォーマンスと言えますね。

自分で使ってみた感想としても、cloudflare workers はデプロイが早く、常にレスポンスが良好な印象を受けました。とくにデプロイ直後が顕著で、AwsLambda/CloudFunction はとくにアクセスが少ない時や常時アクセスが来ないアプリでのはスピンアップが遅く体験が悪いのですが、Cloudflare Workers はそういう点が一切ありませんでした。

制限と合わせて考えるに、 Cloudflare Workers は serverless を名乗っていますが、serverless でよく使われる AWS Lambda や Cloud Function と比較するべきではなく、省メモリで応答性が良いことから CDN Egde で動く JavaScript で、 Varnish VCL のようなコントローラーを書けるもの と認識したほうが良さそうです。

https://www.cloudflare.com/ja-jp/products/cloudflare-workers/ の npm 社の採用事例では、次のような Laurie Voss の賞賛のコメントが載っています。

「Workers を使うとエッジまでルーティングやキャッシングをすることができ、数百万の開発者が利用する npm のパフォーマンスを改善しながらもさらなる拡張を行えました。 VCL からの移行で、大好きな JavaScript と過ごす時間が増えました。」

npm 社の CTO なので JS が好きなのはそうとして、 Varnish VCL から移行で Node.js エンジニアがその能力を Edge Side で発揮できる、というのが大きな採用理由のように見えます。

たとえば、単純な場合でも、アクセスごとの AB テストのコンテンツ出し分け、Canary リリース、 HTTP ヘッダの locale から i18n の多言語の出し分け、みたいな処理が考えられるでしょう。

アクセスごとの料金は、 Workers $0.50/million requests. に対して、 Lambda Edge は $0.60 per 1 million requests です。基本料金や他のプライスも色々あるので、ここでは踏み込みませんが、全体的に安めに感じました。

もちろん、JavaScript で省メモリなプログラミングを心がければ、すべてのアプリケーションロジックを cloudflare workers に乗せることも可能だと思います。その場合、コンパイラやアプリケーション・サーバーをフルで持つというより、next.js の Static Export のような静的サイトジェネレーター+α の制御が良さそうです。

なぜこれが実現できているか

正直な所、最初は新プロダクトを普及させるために、普及まで格安なプライシングを提供しているのでは?と邪推したのですが、しかし次の記事を読む限り、V8 Isolate の工夫で実現しているようです。

How Workers Works · Cloudflare Workers docs

V8 は Isolate をオーケストレートします: 変数をグループ化して、その変数を変異させることが許されたコードを含む軽量なコンテキストです。 Isolate は、関数を実行するための「サンドボックス」と考えることもできます。

1 つのランタイムで何百、何千ものアイソレートを実行し、それらをシームレスに切り替えることができます。各アイソレートのメモリは完全に分離されているので、各コードはランタイム上の他の信頼されていないコードやユーザーが書いたコードから保護されています。また、アイソレートは非常に迅速に起動できるように設計されています。各関数のために仮想マシンを作成するのではなく、既存の環境内にアイソレートを作成します。このモデルは、仮想マシンモデルのコールドスタートを排除します。

言語ランタイムのインスタンスを実行するコンテナ化されたプロセスを使用する他のサーバーレスプロバイダとは異なり、Workers はエッジコンテナの起動時に JavaScript ランタイムのオーバーヘッドを一度だけ支払います。Workers プロセスは、Workers 関数呼び出しごとにアイソレートを作成することで、個々のオーバーヘッドをほとんど発生させずに、本質的に無制限のスクリプトを実行することができます。任意のアイソレートは、コンテナや仮想マシン上の Node プロセスの約 100 倍の速度で起動することができます。特筆すべきは、起動時のアイソレートのメモリ消費量が桁違いに少ないことです。

自分も、 V8 Isolate は node, deno, Chrome のコードを読んでる時に Isolate の用途を覚えたのですが、 V8 には V8 Snapshot といってメモリダンプを保存して、それを呼び起こせるような仕組みを持っています。これにより生の V8 の状態(ほぼプレーンな EcmaScript のみ)からブラウザコンテキスト, node, deno の実行の初期状態を作り、実行のたびにそれを呼び起こしてからユーザーコードを注入する仕組みになっています。

Cloudflare Workers はその Isolate の仕組みを CDN Edge の上で 128MB ごとのヒープで大量に確保して、実行のたびにプロセスに割り当てているのだと予想できます。そしてこの Isolate はセキュリティ上のサンドボックスにもなっています。

たしかにこの仕組なら、JavaScript という言語に限定されてしまってはいますが、軽量かつ、他の Serverless にあるようなコールドスタートを排除できます。デプロイもユーザーコードを Cloudflare の CDN で静的アセットとして配るだけになりますね。

WebAssembly も動いたので、エントリポイントは javascript ですが、WASM バイナリを実行することもできそうでした。Fastly の Compute@Edge は WASM バイナリを専用のコンパイラで最適化するのに対し、 Cloudflare は V8 Isolate を大量に用意しておくというアプローチの違いが面白いですね。

参考

(この辺の技術を PublicKey の jniino さんが熱心に追ってるようです…)

グローバルキャッシュ: Workers KV

Edge Side にロジックを寄せたい理由は、ユーザーの地理的なロケーションに対して最適化するのと同時に、各 Edge でキャッシュを持ちアプリケーション・サーバーに到達させないようにする、というパフォーマンス的なものです。

そこで必要になるのは Edge Side での Regional Cache, そして全体で協調するための Global Cache
で、Lambda@Edge では Edge キャッシュを DynamoDB の Global Replication で実現しますが、 Workers では Workers KV というキーバリューストアが最初からインテグレーションされています。

How KV Works

Workers KV は一般的に、書き込み頻度は比較的低いが、高速かつ頻繁に読み込む必要がある場合に適しています。このような高読取アプリケーション向けに最適化されており、データを頻繁に読み込んでいる場合にのみ、その性能が最大限に発揮されます。読み込み頻度の低い値は中央に保存され、人気のある値は世界中のデータセンターに保存されています。

KV は、最終的に一貫性を保つことでこのパフォーマンスを実現しています。変更が伝搬するまでに最大 60 秒かかる場合があります。ワーカー KV は、アトミック処理のサポートが必要な場合や、1 回のトランザクションで値を読み書きする必要がある場合には理想的ではありません。

How the Cache works · Cloudflare Workers docs

強整合ではないので運用は難しいですが、それを理解しさえすればかなり強力なツールです。

事例: cdnjs を workers kv に移行

じゃあ実際どういう用途で使っているのでしょうか。例えば cdnjs で cloudflare 社自身が workers を使った事例が紹介されています。

https://blog.cloudflare.com/migrating-cdnjs-to-serverless-with-workers-kv/

私たちは、cdnjs を提供する方法の信頼性とパフォーマンスの両方を向上させるにはどうすればよいかを考え始めました。私たちは、エッジで開発するための独自のプラットフォームである Cloudflare Workers に直行しました。Workers に組み込まれている強力なツールの 1 つが Workers KV で、低レイテンシでグローバルに分散されたキーバリューストアで、高読み取りアプリケーション向けに最適化されています。
私たちは、cdnjs/cdnjs リポジトリを引っ張ってきてディスクからファイルを提供する代わりに、物理マシンを完全にカットして、世界中にデータを分散し、エッジから直接ファイルを提供することができることに気づきました。そうすれば、cdnjs はあらゆるオリジンのデータセンターの障害から回復することができ、スケーラビリティも向上します。

しかし、移行を計画しているうちに、700 万以上の資産がある cdnjs には、Workers KV の 10MiB 値の制限を超えるファイルが間違いなく存在しているのではないかと心配になりました。調査した結果、数百個の cdnjs ファイルが特大サイズであることを発見しました。
そこで思いついたのです。圧縮されたバージョンの cdnjs ファイルを Workers KV に保存することで、特大ファイルの問題を解決するだけでなく、ファイルを提供する方法を最適化することができるのです。
インターネットの料金を払っている人は、帯域幅が高いことを知っているでしょう! このため、すべての最新のブラウザは、利用可能なときはいつでも圧縮されたウェブコンテンツを取得しようとします。同様に、Cloudflare では、帯域幅を削減するためにその場で圧縮を試み、圧縮されたコンテンツが受け入れられたときには常に圧縮コンテンツを提供しています。その結果、すべての cdnjs ファイルを事前に圧縮し、最適な Brotli 形式と gzip 形式の両方で Workers KV に書き込むことにしました。そうすることで、待ち時間の要件がなくなるため、オンザフライ圧縮に比べて圧縮レベルを上げることができました。

開発者体験の良さ

Cloudflare workers は主に wrangler という CLI ツールで開発します。これに scaffold 機能がついており、チュートリアルをなぞることで様々なパターンを学習することができました。

Starters · Cloudflare Workers docs

wrangler dev という手元で動くエミュレータがあるのも印象が良いです。特に workers kv が手元で動くのがいいですね。余談ですが GCP や AWS のツールはローカルエミュレータが不出来なのが、開発体験を悪化させています。

AWS Lambda や Cloud Function などを触った人がおそらく一番感動するのは、デプロイがとにかく高速で数秒で終わります。間違ったコードをデプロイしてしまった際のロールバックが高速というのは安心感に繋がります。

アプリケーションコードレベルでも、 Service Worker の API を踏襲しているのが、メタファとして非常に優れていると感じました。これはブラウザとサーバーの間の合法 MITM という感じの API なのですが、エッジで MITM してるという実情と非常にマッチしています。うまく工夫すれば、ServiceWorker と Workers 両方で動くライブラリが設計できる気がしてます。

不満

それなりに不安はあって…

  • wrangler に webpack が組み込まれているのは正直おせっかいだと感じました。ビルド過程に関与せずに指定した JS を丸投げしてほしいです。そのオプションが分かりづらかったです。
  • 既存のサンプルの wrangler.toml を読まないとわからないオプションもたくさんありました。あと workers kv の preview モードが動く条件がわかりにくいです。思いつくパターンを全部試して、とりあえず動くものを使っています…。
  • 出てくるサンプルのほとんどが cloudflare 自身のものなので、本当に使われているんだろうか? と不安になってしまいます。もっとコミュニティ発のものを紹介してほしいです。
  • 面白半分でプロジェクトを作ってたら、プロジェクト 30 個制限に引っかかりそうになって、古いやつを上書きして再利用してます。これが理由で諦めたプロダクトもあるので、なんらかのプライシング込みでいいから上限解放してほしいところ。

これらを踏まえた発展型フレームワークを考える

ここまで Workers の良さを語ってきましたが、実際にはよほどユースケースがマッチしない限り、今のコードを捨ててまで採用する理由は弱いのではないか、とも思っています。これは Edge Side Scripting という発想が現在の開発者のエコシステムに織り込まれていないからです。

現在では一部の PaaS でしか動かないものなので、OSS のエコシステムがそれを受容するまでに数年掛かる気がしています。Technolgy Radar で ASSESS レベルなのもそれが理由でしょう。

現状先発の Lambda@Edge がそこまで普及していないのは、その扱いづらさの割にメリットを感じにくいからでしょう。その点でいうと、 Cloudflare Workers はその開発体験の良さ、そしてパフォーマンスの良さから乗り越えていけるポテンシャルがあると思っています。

そのためにアーキテクチャを根本的に考え直すとして、いずれも自分の記事ですが、次のような話が叩き台になるのではないでしょうか。

これらを踏まえ、プロキシというより、自身がアプリケーションとなる CDN Edge First なフレームワークを実装可能と考えています。next.js のようなハイブリッドな静的サイトジェネレータをベースに、省メモリなチューンをして、必要に応じて 50ms 未満の処理を行い、そして可能な限りキャッシュに当てる設計です。

next.js をみるに、速いアプリケーションサーバーというより、動く静的サイトジェネレータという方が、伸びしろがありそうに感じています。 ServiceWorker / そのフォールバックで Workers と多段で動くと尚良さそう。

…実は手元で試作しているんですが、next.js の 実装の真似をするのが、思ったより面倒臭くて後回しになっています。仕事が忙しくなくなったら作りたい…

最後に: cloudflare の技術広報について

なんか cloudflare の回し者みたいなブログになってしまったのですが、それは workers のポテンシャルに対して、国内国外問わず cloudflare の技術広報があんまり上手くいってないという印象を受けています。

日本国内だと cloudflare の名前は、漫画村の件であんまり印象よくない名前になってしまってますが、僕みたいなウェブ開発者は cloudflare がインターネット全体においてどういう位置づけなのかをちゃんと理解していると思うので、もっと発信していってほしいと願っています。Qiita にリンク集を張って満足してほしくはないです。

最近 cloudflare の日本オフィスができたっぽいんですが、とくに Workers の開発者ブログの翻訳含めもうちょっと頑張ってほしいところ。

Cloudflare Workers は何かしらの機会で仕事にねじ込みたいとは考えていますが、まだ知名度が低いので不安がられてしまい、どうしても使い慣れてる GCP / AWS に寄せてしまう、というのがすでに何度かあったので、その辺踏まえて頑張ってほしい気持ちがあります。なのでこのブログを書きました。

Cloudflare 以外のサービスもまだまだ調査してるので、他のサービスも頑張って欲しいところではあります。とくに Fastly には期待しています。

Cloudflare への愚痴はともかく、アーリーアダプターとしてなかなかいいおもちゃなので、みなさんも触れてみてはいかがでしょうか。

History