CloudflareWorkersとは


エイチームフィナジーのアドベントカレンダー3日目は、sakupa80 が担当します。

はじめに

きっかけとしては、ただコンテンツをキャッシュさせる為のCDNサービスとしてCloudflareを使い始めましたが、Cloudflareの利便性に感銘を受け、今ではその他の様々なCloudflareサービスを利用しております。
今回はその中でもCloudflare Workersについてまとめてみました。

公式ドキュメント: https://developers.cloudflare.com/workers/

Cloudflare Workersとは

エッジサーバーでスクリプトを実行してくれるサーバーレスのサービスです。
JavaScript(V8)を実行することが可能です。
ServiceWorkerが動くので、FetchAPI、CacheAPIなどを使うことが出来ます。
Cloudflare Workersの名前もService Workerから取ったそうな。

Service Worker API: https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API

では、エッジサーバーで動くJavaScript、ServiceWorkerAPIを使ってどのようなことが出来るのでしょうか。

Cloudflare Workersで出来ること

エッジサーバーにてコードを実行するので、オリジンサーバーに到達する前に実行したい独自処理を得意とします。
例として下記のようなことが可能です。

  • キャッシュコンテンツに対する独自コントロール
  • User-Agentごとにリダイレクト
  • 全く異なる2つのバックエンド間でA/Bテストを実現
  • 独自のアクセスフィルタリング
  • 独自のフェイルオーバーの実装
  • ログの収集

などなど。活用の仕方次第で色々出来ることでしょう。
Cloudflare Workersの活用方法については、公式サイトにてexamplesがあるので、いくつか紹介しようと思います。
https://developers.cloudflare.com/workers/examples

Redirect

https://developers.cloudflare.com/workers/examples/redirect

const destinationURL = "https://example.com"
const statusCode = 301

async function handleRequest(request) {
  return Response.redirect(destinationURL, statusCode)
}

addEventListener("fetch", async event => {
  event.respondWith(handleRequest(event.request))
})

こちらはシンプルにリクエストを受け取って、301リダイレクトを返す例となります。
event.requestでリクエストデータを受け取ることが可能です。
addEventListenerでfetchイベントにフックして処理を行います。

Debugging logs

https://developers.cloudflare.com/workers/examples/debugging-logs

// Service configured to receive logs
const LOG_URL = "https://log-service.example.com/"

function postLog(data) {
  return fetch(LOG_URL, {
    method: "POST",
    body: data,
  })
}

async function handleRequest(event) {
  let response

  try {
    response = await fetch(event.request)
    if (!response.ok) {
      const body = await response.text()
      throw new Error(
        "Bad response at origin. Status: " +
          response.status +
          " Body: " +
          //Ensure the string is small enough to be a header
          body.trim().substring(0, 10),
      )
    }
  } catch (err) {
    // Without event.waitUntil(), our fetch() to our logging service may
    // or may not complete.
    event.waitUntil(postLog(err.toString()))
    const stack = JSON.stringify(err.stack) || err

    // Copy the response and initialize body to the stack trace
    response = new Response(stack, response)

    // Shove our rewritten URL into a header to find out what it was.
    response.headers.set("X-Debug-stack", stack)
    response.headers.set("X-Debug-err", err)
  }
  return response
}

addEventListener("fetch", event => {
  //Have any uncaught errors thrown go directly to origin
  event.passThroughOnException()
  event.respondWith(handleRequest(event))
})

こちらはサードパーティのロギングサービスにエラーログを送る例です。
Cloudflareはログの収集方法としてCloudflare Logsというサービスがありますが、こちらは現在エンタープライズプランでないと使用することが出来ません。
Cloudflare Logs: https://www.cloudflare.com/products/cloudflare-logs/

ですが、例のようにCloudflare Workersを利用して自前でロガーを実装すれば、その代替とすることが可能となります。

Return HTML

https://developers.cloudflare.com/workers/examples/return-html

const html = `<!DOCTYPE html>
<body>
  <h1>Hello World</h1>
  <p>This markup was generated by a Cloudflare Worker.</p>
</body>`

async function handleRequest(request) {
  return new Response(html, {
    headers: {
      "content-type": "text/html;charset=UTF-8",
    },
  })
}

addEventListener("fetch", event => {
  return event.respondWith(handleRequest(event.request))
})

ただの静的なHTMLを返すだけならCloudflare Workersだけで完結可能です。
例えば、ドメイン全てに対して一時的に適用したいページなどがあれば、この要領で簡単に実装できますね。

ちなみにNext.jsをモデルに作られた「Flareact」というOSSも最近公開されました。
これで上記の例のようなシンプルな静的HTMLだけでなく、Reactアプリをエッジサーバーにてレンダリング可能です。
Rendering React on the Edge with Flareact and Cloudflare Workers:
https://blog.cloudflare.com/rendering-react-on-the-edge-with-flareact-and-cloudflare-workers/

エッジサーバーからReactアプリを配信、さらにCloudflareキャッシュと組み合わせれば、高いページパフォーマンスが出せるサイトをサーバーレスに作ることが出来るでしょう。

このようにユーザーからのリクエストをプロキシする形で様々な独自処理を行うことが可能です。
ログイン無しで使用することが出来るプレイグラウンドも用意されています。
https://cloudflareworkers.com/

Cloudflare Workersの強み


https://workers.cloudflare.com/

Cloudflare Workersを採用する理由として、高いパフォーマンスと拡張性があげられます。
それらの強みを少し説明していきます。

高速デプロイ

wranglerというRust製のCLIを使って簡単にデプロイが可能です。
さらに公式ブログでは全てのエッジサーバーに対して30秒以内にデプロイが完了すると書かれています。
プレビューモードでの開発時も、高速デプロイのおかげで動作確認の際のストレスもありません。

高速実行

エッジでのスクリプト実行となると気になるのは応答速度だと思いますが、Cloudflare Workersでは0msコールドスタートが可能で、サーバレスサービスによく見られるコールドスタート時の実行が遅い問題が発生しません。

これはCDNの強みにもなりますが、エッジサーバーで実行されるということはユーザーから近いサーバーにて実行されるので、Flareactなどを使ってエッジサーバーにてReactアプリの実行を可能とする設計(エッジサーバーにフロントを寄せる設計)にしておけば、高いサイトパフォーマンスを発揮することが出来ます。

終わりに

紹介したCloudflare Workers以外にも、WAF、ネットワーク最適化、RateLimit機能などの手軽に設定できて、サイトをより良くしてくれるサービスがCloudflareにはあります。
自分がエッジコンピューティングの素晴らしさを知ることが出来たのは、Cloudflareのおかげでした。
ただ、最近でも新たなCloudflareのサービスが出てきている一方、まだまだネット上での日本語の情報が少なく、追いついていない印象を受けるので、日本でもっと利用者が増えてくれることを願っております。

参考

https://workers.cloudflare.com/
https://blog.cloudflare.com/introducing-cloudflare-workers/
https://blog.cloudflare.com/cloudflare-workers-unleashed/