Slide 1

Slide 1 text

Edge Side Frontend という 新領域 mizchi@ ワインと鍋.js#1

Slide 2

Slide 2 text

About Me 主にフロントエンド | Node.js エンジニア 最近はサードパーティスクリプトやパフォーマンスが中心

Slide 3

Slide 3 text

Outline フロントエンドの「正解」 これまでの CDN Edge Worker の何が新しいのか Cloudflare Workers でできること

Slide 4

Slide 4 text

フロントエンドの「正解」

Slide 5

Slide 5 text

近年のゲームチェンジャー : Core WebVitals https://web.dev/i18n/ja/vitals/ ページ速度が Google Search SEO に関与するようになった ページスピードが非機能要件から SEO という準機能要件へ LCP: 最大コンテンツ確定時間 CLS: 累積レイアウト TTI: 操作可能タイミング

Slide 6

Slide 6 text

Core WebVitals への個人的な見解 ページ特性次第で常に正しい指標とは言えない 「Core Web Vitals はWeb を高速化したか?」 https://postd.cc/have-core-web-vitals-made-the-web-faster/ が、ベースラインとして「良いコンテンツ= 高速」は個人的に納得 感はある 何よりパフォーマンスをゲーミフィケーションできて楽しい!

Slide 7

Slide 7 text

LCP 最適化 Lighthouse Pref で 100 点を出すには TTFB から約 450ms ( 経験的 な感覚) 以内に LCP 確定の必要 TTFB: 初期応答時間 LCP: ページ内で最大レイアウト要素が確定したタイミング 雑な肌感 DNS Lookup で 20~80ms 限界まで絞った JS/CSS の Evaluation でも CPU Time: 80~ms 残りの猶予で使える RTT * N はほとんどない 現実には Blocking Waterfall が多発するのでもっと厳しい

Slide 8

Slide 8 text

「正解」 CDN Edge から全部返す アプリケーションサーバー に到達させたら負け つまり アプリケーションか ら html を返す伝統的なスタ イルは既に不利 我々が考えることは、何が どこまで静的コンテンツと して事前に確定するか?

Slide 9

Slide 9 text

Jamstack 再考 構成要素が更新されるたび に静的アセットをCDN に撒 く( だけ) ビルド後は静的アセットな ので運用が楽/ 閲覧が高速 コンテンツが肥大化するに つれてリリース時間が増大 更新が低頻度でアクセスが 多い場合に向いてる ( 人によって定義がブレます)

Slide 10

Slide 10 text

Incremental Static Regeneration Next.js ISR アセット生成サーバーをデ プロイする キャッシュがないときに生 成して期限付きキャッシュ Jamstack より設計が難しい がコンテンツ増加に強い

Slide 11

Slide 11 text

ISR: Next.js Example export async function getStaticProps() { const posts = await fetch('https://.../posts').then(res => res.json()); return { props: { posts }, revalidate: 60 } } 60 秒ごとにキャッシュを破棄して静的コンテンツを再生成する 初回リクエストは遅延を許容して生成しながら返す 再生成中は一つ前のコンテンツを返す( かどうかを制御できる)

Slide 12

Slide 12 text

Jamstack | ISR の課題 ビルド ~ CDN Cache Purge 間は古いコンテンツが表示される 基本的に ユーザーに弱整合を納得してもらうのは困難 ISR: Vercel 以外のネイティブサポートがない( 難しい) ISR でも再生成時間を短くするとコストが増大

Slide 13

Slide 13 text

Zenn のキャッシュ整合性ハック https://zenn.dev/catnose99/articles/8bed46fb271e44 // 上記記事と同様の処理を行う疑似コード const isMine = currentUser.id === props.article.user.id; useEffect(() => { if (!isMine) return; getArticle({ slug: props.slug }).then(setAricle); }, [isMine]); Zenn では著者本人の場合のみ最新のデータをリクエストするよう にしています “ “

Slide 14

Slide 14 text

Zenn のハックをどう解釈するか 仮説 : 人間は「自分が更新したら反映される」という予想から外れると 強い拒否感を示す 自分 ( 自社 ) 以外のコンテンツに対してキャッシュ整合の興味がな いのでは? キャッシュ整合性のアドバンス戦略 : 弱整合静的コンテンツと強整合API の 2 系統用意 認証フラグ等で強整合API を選択的に有効化

Slide 15

Slide 15 text

キャッシュ設計は納得感との戦い キャッシュしたら速くて安いことはエンジニアなら知ってる でも自分( 自社) の納得感のために強整合を要求する( 要求される) 閲覧者は多少の遅延を許容してでも閲覧が速いほうが嬉しいし、提 供側にとっても安いはず… 全体最適のためにコンテンツオーナーだけハックする のは立派な戦 略なのでは? 注意: もちろんコンテンツの性質次第

Slide 16

Slide 16 text

キャッシュ破棄をアプリケーションに織り込む 光を超えるためのフロントエンドアーキテクチャ https://speakerdeck.com/mizchi/guang-wochao- erutamefalsehurontoendoakitekutiya 全キャッシュを前提として、API によるリソース更新にキャッシュ 破棄を仕込む ISR の時間経過以外をトリガーにコンテンツ再生成を実現できる( は ず) ただし、反映速度は CDN の Cache Purge 速度に依存 この点現時点で最強なのは Fastly の Instant Purge (~150ms)

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

ISR | Jamstack でさらにできるといいこと キャッシュ不整合状態を短くした上で多くをキャッシュヒットさせ たい ハイパフォーマンスなフロントエンドの責務は、アプリケーション サーバーに到達させないための、キャッシュ制御が主になってくる キャッシュ制御は CDN Edge 上から細かくやれると嬉しい というのがフリで、そろそろ CDN Edge Worker が欲しくなってきま せんか?

Slide 19

Slide 19 text

これまでの CDN 再考

Slide 20

Slide 20 text

CDN の復習 リクエスト元から地理的に近い場所でレスポンスをキャッシュし、返 却する仕組み。 CDN を使っていない api.twitter.com と skypack.dev で比較する $ ping api.twitter.com PING tpop-api.twitter.com (104.244.42.66): 56 data bytes 64 bytes from 104.244.42.66: icmp_seq=0 ttl=47 time=183.429 ms $ ping skypack.dev PING skypack.dev (104.26.12.82): 56 data bytes 64 bytes from 104.26.12.82: icmp_seq=0 ttl=51 time=6.695 ms 64 bytes from 104.26.12.82: icmp_seq=1 ttl=51 time=10.566 ms

Slide 21

Slide 21 text

頑張れば CDN ( の IP 最適化 ) は自分で作れる https://dev.to/megajakob/how-to-build-your-own-cdn-io1 要約: GeoDNS/GeoIP 技術を使って地理的に近いエンドポイントに リクエストを振り分け ClouDNS, Amazon Route 53, Google Cloud DNS, Cloudflare 等が サービスとして提供 実際難しいのはここから先の分散ファイルシステムの実装

Slide 22

Slide 22 text

これまでの CDN の役割 Akamai, AWS Cloudfront, Cloudflare, Fastly 主に静的アセット配信の最適化 L4 スイッチ相当: 主に TCP / HTTP Header を見てネットワークを振 り分ける CDN 利用する開発者にできるのは、 ヘッダを付与してキャッシュ 破棄間隔を指定する程度 Cache-Control: s-maxage=300,max-age=180

Slide 23

Slide 23 text

Fastly VCL による動的なエッジ処理 (2011?-) CDN はある程度決め打ちなものだったが、Fastly では CDN 利用者で も、 Varnish VCL でリクエストをコントロールできるようになった。 例: origin で feature flag を切り替える if (req.restarts == 0) { set req.backend = F_origin_0; set req.http.tmpOrigUrl = req.url; set req.url = "/response-headers?Flags=group-A,new-header,search-enabled"; } else { set req.backend = F_origin_1; is disabled. This re-enables it. set req.http.Fastly-Force-Shield = "1"; }/ return(lookup);

Slide 24

Slide 24 text

Fastly VCL の問題 VCL 自体が Fastly | Varnish 前提でほぼ Fastly 専用言語 アプリケーション設計全体が Fastly VCL ありきになり、強いロック インが発生してしまう 自分はこれを懸念して Fastly に賭けられないでいた。新規プロダ クトや転職先で Fastly を選択できる保証がないため… 仮に採用したとして、より良い選択肢が出てきた時に乗り換えら れるか?

Slide 25

Slide 25 text

Cloudflare Workers の登場 (2017-) Cloudflare Workers: https://workers.cloudflare.com/ Cloudflare CDN 上で動く JavaScript(V8) 実行環境 汎用言語(JS/Wasm) で動く CDN Edge Worker を各社も追従 Fastly: Comupute@Edge (WebAssembly) AWS: Cloudfront Function (ES5) Deno: Deno Deploy (Deno) ( 本資料は主に Cloudflare Workers にフォーカスを当てます)

Slide 26

Slide 26 text

CDN Edge Worker の特徴 ユーザーから地理的に近い CDN 上で応答するので低遅延 メモリフットプリントの小さいランタイムを要求 世界中の DC で実行する以上、強いマシンは仕込めなさそう 強めの CPU 制約 | 実行時間制限

Slide 27

Slide 27 text

Cloudflare Workers にみる CPU 制約 (1) https://developers.cloudflare.com/workers/platform/limits/#environ ment-variables 課金するほど制約が緩くなる Free Bundled Unbound CPU Time 10ms/req 50ms/req 30000ms/req Cost 100000/day 10 million / month, +$0.50/million 1 million / month, + $0.15/million

Slide 28

Slide 28 text

Cloudflare Workers にみる CPU 制約 (2) メモリ上限: 128MB コード上限: 1MB ちょっと富豪的な Node アプリは動かない水準。 実際、node_modules そのまま現実的に不可能なので、 フロントエン ドと同等の Bundle + Optimize が必要になる。

Slide 29

Slide 29 text

Cloudflare Workers の制約をどう解釈するか ネットワークエンジニアの発想( 既存の延長の発想) CDN で動く L7 プロキシを JavaScript で記述できるようになった Node.js エンジニアの発想 Unbound を有効にすれば、サーバーサイド JS として十分なので は? ( いずれにせよ High CPU なワークロードは任せない前提) Jamstack|ISR のリソース再生成はほぼ外部 API を読み出して外 にパスしてるだけと考えると、実際 Edge Worker 内でコンテン ツ再生成を任せられるのでは?

Slide 30

Slide 30 text

Cloudflare Workers は誰のためのもの? ネットワークエンジニア CDN Edge 上の富豪的な L7 プロキシ 既存のネットワークの間に挟んでプロトコルの最適化ができる Node エンジニア CPU 制約を受け入れられれば高応答性のアプリケーションサー バーにもできそう Cloudflare も Workers が真の Serverless だとアピール Node + フロントエンドエンジニア (mizchi) 遂にアプリにCDN キャッシュ破棄を織り込む設計ができるぞ!

Slide 31

Slide 31 text

あれ、お前が嫌ってたロックイン問題は? 現在、WinterCG というグループで、ブラウザ外 JavaScript の相互運 用性について仕様策定中 https://wintercg.org/ | https://github.com/wintercg ブラウザ以外で動く JavaScript の標準を決めるためのグループ Cloudflare / Vercel / Deno / Shopify / Bytedance / Igalia が参加 自分のスタンス: 現状はある程度のロックインは許容しつつ、 Cloudflare に学習コストを全振り中

Slide 32

Slide 32 text

Cloudflare Workers がどう 動いているか

Slide 33

Slide 33 text

CDN Edge Worker はスケールするのか? なんで JS なんかの高水準言語でスケールさせようと思ったのか調 べた https://mizchi.dev/202009122126-cloudflare-workers How Workers Works https://developers.cloudflare.com/workers/learning/how-workers- works/ Security Model https://developers.cloudflare.com/workers/learning/security- model/

Slide 34

Slide 34 text

How Workers Works - Cloudflare V8 は Isolate をオーケストレートします: 変数をグループ化して、 その変数を変異させることが許されたコードを含む軽量なコンテ キストです。 Isolate は、関数を実行するための「サンドボック ス」と考えることもできます。 “ “ Isolate のメモリは完全に分離されているので、各コードはランタ イム上の他の信頼されていないコードやユーザーが書いたコード から保護されています。また、Isolate は非常に迅速に起動できる ように設計されています。各関数のために仮想マシンを作成する のではなく、既存の環境内に Isolate を作成します。このモデル は、仮想マシンモデルのコールドスタートを排除します。 “ “

Slide 35

Slide 35 text

V8 Isolate on Cloudflare Workers 大雑把に言うと CPU: 128MB 割り当てた v8::Isolate を仮想コンテナ として決め打ちして、大量にスケールさせてる

Slide 36

Slide 36 text

ここで V8 の復習 V8: Google Chrome の JavaScript Engine v8::Context: V8 Isolate を動かすためのサンドボックス v8::Isolate: 実行単位。 V8 Snapshot に実行状態をダンプできる v8::Snapshot: バイナリシリアライズされたIsolate の中間状態 例えば Chrome なら DOM API が Binding された Snapshot Node.js / Deno でもその環境の API が構築済みの Snapshot Cloudflare Workers もおそらく同様の Snapshot がある ( 極論: Node や Deno のコードを読むと理解できます)

Slide 37

Slide 37 text

Ryan Dahl 曰く「 V8 はコンテナ」 https://tinyclouds.org/javascript_containers Linux OS, Docker, に続くコンテナこそが V8 Isolate であるという主張

Slide 38

Slide 38 text

Ryan Dahl の主張 (1) JavaScript は世界共通のスクリプト言語です。JavaScript の普遍 性のために、サーバーを単純化する新しいコンテナのような抽象 化が出現しています。 “ “ 私はLinux コンテナがなくなると主張しているわけではありませ ん。その抽象化のレベルは常に有用です。ただ、人々が書く「ビ ジネス・ロジック」の多くには、むしろ低レベルすぎます。ウェ ブサイトを作るとき、systemd のコンフィギュレーションなどは 定型的なものです。 “ “

Slide 39

Slide 39 text

Ryan Dahl の主張 (2) Web サービスの大部分は、Linux コンテナではなく、JavaScript コ ンテナの観点から考えることで、簡略化できるかもしれません。 “ “ ウェブは人間のためのものであり、実行環境がスクリプティング 言語であることは理にかなっています。 “ “

Slide 40

Slide 40 text

Ryan Dahl の意見をどう捉えるか v8 がコンテナというのには同意だが、技術的中立性を欠く 同コンセプト実行モデルを持つの他の言語( 例: dart) でも同等のこ とができそう High CPU 処理を切り分けるために Fastly Comupte@Edge のように WebAssembly Runtime も選択肢としてほしい。だが Ry の言うよ うに人間の生産性を注力するなら v8 の選択肢は妥当

Slide 41

Slide 41 text

Cloudflare Workers どう動くか : まとめ V8 Isolation はセキュリティサンドボックス付きの JS 実行モデル Cloudflare Workers は V8 Isolation をコンテナとして決め打ちして CDN Edge でユーザコードを評価 Ry も「V8 はウェブと人間のためのコンテナ」と主張してる

Slide 42

Slide 42 text

Cloudflare Workers で何が できるか

Slide 43

Slide 43 text

自分の Edge Worker を評価する視点 Jamstack | ISR を進化させられるか 部分的に Node を置き換えることができるか | 運用を楽にできるか コストが安く着くか

Slide 44

Slide 44 text

Cloudflare Workers 周辺の機能群 手元の弾を把握する DurableObjects: 強整合の CDN Edge 上のステートマシン Workers KV: Regional Cache Cloudflare R2: Amazon S3 互換のオブジェクトストレージ Cloudflare D1: CDN Edge 上で動く sqlite

Slide 45

Slide 45 text

Workers KV 弱整合で高速な KeyValueStore インバリデーションは実測 5 秒程度だが、ワーストで60 秒かかる( ら しい) await KV.put(key, value, { metadata: { someMetadataKey: "someMetadataValue" }, });

Slide 46

Slide 46 text

Durable Objects https://blog.cloudflare.com/ja-jp/introducing-workers-durable- objects-ja-jp/ CDN 上で動く強整合の Actor Model 現在の Connection に応じて地理的に Edge Location が再配置され る 各ドキュメントを読む限り、 DurableObjects が他を成立させるた めの基幹プロダクトっぽい扱いをされてる ( 完全に勘だけど R2 や D1 もこの中で動いてそう)

Slide 47

Slide 47 text

Durable Objects: コード例 export class Counter { constructor(state, env) { this.state = state; // `blockConcurrencyWhile()` ensures no requests are delivered until // initialization completes. this.state.blockConcurrencyWhile(async () => { let stored = await this.state.storage.get("value"); // After initialization, future reads do not need to access storage. this.value = stored || 0; }); } // Handle HTTP requests from clients. async fetch(request) { // use this.value rather than storage } }

Slide 48

Slide 48 text

Cloudflare D1 CDN Edge で read replica がばら撒かれる sqlite おそらく強整合ではないが、sqlite を同期する https://litestream.io/ や https://github.com/rqlite/rqlite と似た技術 を CDN 間で行ってると予想される 参考: Cloudflare D1 がヤバい https://zenn.dev/mizchi/articles/cloudflare-d1

Slide 49

Slide 49 text

そもそも Edge で何がやりたかったんだっけ? 静的アセットを CDN に当てたい R2 + KV で実装可 https://github.com/cloudflare/worker-template-static CDN Edge 内で処理を完結させて高速に応答したい 参照系 API を D1 で完結して構築する( ことができるか?) 動的コンテンツの不整合な時間を最小にしたい KV.delete(...) をアプリケーションロジックに入れる

Slide 50

Slide 50 text

Edge Worker 用フレ ームワークを考える フロントエンドの表示に関 わるデータをD1 に集約 更新系 API は D1 に書き込 みつつ選択的に KV.delete() する キャッシュがない時: 動的な 静的アセット再生成と再キ ャッシュ(ISR)

Slide 51

Slide 51 text

Next.js 風 API // api/update-post.ts export default (req) => { await env.db.exec([/*...*/]); kv.delete(req.params.id).catch(console.error); req.json({ok: true}); } // pages/posts/[uuid].tsx export const getStaticProps = async (ctx) => { const id = ctx.params.uuid; const posts = await env.db.get('select * from posts where id=$id', {$id:id}); return { // このページを破棄するタグ cacheKeys: ['@app', '@posts', id], props: { posts } } };

Slide 52

Slide 52 text

誰が Edge Worker を触る必要があるか ウェブアプリケーションは Edge First で考える時代が来るだろう エコシステム枯れ具合だけが問題で、まだ未成熟 パフォーマンスとコストというわかりやすいメリットがあるの で、普及は時間とリソース投資の問題 Edge Worker は既存のクラウドを置き換えるものではない あくまでユーザーと直接通信する末端の最適化で、今までのスタ ックが不要という話ではない とはいえ高コストなクラウドリソースを Edge Cache でアクセス 頻度を下げるという発想が主になる

Slide 53

Slide 53 text

Edge Side Engineer という職域 Frontend Engineer から派生した Edge Side Engineer という職域が 発生する 既存の Frontend の延長というより、 Frontend Ops と Node.js のサ ーバーサイド技術の組み合わせ SPA 職人の頃と同じで、最初は高級品扱いだが、Next.js のように 時代とともに陳腐化する

Slide 54

Slide 54 text

おまけ : Cloudflare 以外の Edge Worker への気 持ち Fastly Compute@Edge: Wasm に全振りは面白いけど Cloudflare の V8 のが好き Cloudflare と比較して開発者支援が貧弱 Deno Deploy: 簡易な Cache がない。Dynamo や Firebase 使えと言われても… Cloudfront Function: ES5 水準だし CPU 制約が多すぎて本当にL7 プロキシ。順次機能 開放されると思うが…

Slide 55

Slide 55 text

まとめ キャッシュしたら速くてうまい 人間の納得感のためにはキャッシュパージの速度が必要