Servicesの紹介:Cloudflare Workersに構成可能な分散型アプリケーションを構築

まず、Workerのスクリプトです。これは、シンプルかつエレガントなものでした。わずか数行のコードで、HTTPリクエストを書き換えたり、ヘッダーを追加したり、Webサイトを素早く修正することができます。

ですが、Workersでアプリケーション全体を構築したい場合はどうでしょうか?開発者用ツールボックスには、さらに多くのツールが必要になります。このような理由から、当社の分散キーバリューストアであるKV などのWorkersプラットフォーム、強固な一貫性があるオブジェクト指向データーベースのDurable Objectsへの拡張を導入してきました。まもなくエグレスのないオブジェクトストレージR2への拡張も予定しています。これらのツールで、より堅牢なアプリケーションを構築することができますが、多くのアプリケーションやサービスを構成するシステムアーキテクチャの構築に関しては、いまだにギャップがあります。

APIへのリクエストを認証する認証サービスを構築したとします。そのロジックを他のすべてのサービスで再利用したい、さらに、その認証サービスに変更を加える際には、運用中の他のサービスに影響を与えないように管理された環境でテストしたいと考えるでしょう。ですが、もう想像する必要はありません。

Servicesの紹介

Servicesは、Cloudflare Workersにアプリケーションをデプロイするための新しいビルディングブロックです。スクリプトとは異なり、一つのServiceはコンポーザブル(組み立て可能)であるため、Services同士の連携が可能です。また、Servicesは複数の環境をサポートしているため、プレビュー環境で変更をテストし、動作に問題がなければ本番環境に移行することができます。

Servicesへのシームレスな移行を可能にするために、すべてのスクリプトが1つの「本番」環境で一つのServiceになるように自動的に移行しました。

多数の環境を備えたServices

各Serviceには、本番環境と、何十ものプレビュー環境を作成またはクローン化する機能が付いています。コード、環境変数、そしてKVネームスペースのようなリソースまで、環境のあらゆる側面がオーバーライド可能です。ダッシュボードを数回クリックするだけで、環境を作成したり切り替えたりすることができます。

各環境は、環境の作成や名前の変更時に自動的に生成される、固有のホスト名で解決できます。デプロイ後の待ち時間はありません。DNSレコードやSSL証明書など、必要なものはすべて数秒後にはすぐに使用できます。さらに高度なセットアップをご希望の場合は、ドメインから環境にカスタムルートを追加することもできます。

プレビュー環境で変更内容をテストしたら、次は本番環境にプロモートできます。コードをリビルドしたり、アップロードしたりすることなく、ある環境から別の環境にコードを非常に簡単にプロモートすることができます。また、環境はコードと設定を別々に管理するため、ステージング環境から本番環境へのプロモート時に環境変数を手動で編集する必要はありません。

Servicesのバージョン管理

Servicesへの変更はすべてバージョン管理され、監査されます。ミスは起こりえますが、そのような場合には迅速にロールバックし、昔からある質問「誰が、いつ、何を変更したか?」に答えるためのツールを持つことが重要です。

Service内の各環境には、それぞれのバージョン履歴があります。コードが変更されたり、環境変数が更新されたりするたびに、その環境のバージョン番号がインクリメントされます。また、各バージョンには、git commitやdeployment tagなどのメタデータを追加することができます。

Service同士が通信可能

Serviceはコンポーザブル(組み立て可能)であり、あるServiceが別のServiceと通信することができます。これをサポートするために、Service間の通信を促進する新しいAPI、サービスバインディング(束ねる)を導入します。

サービスバインディングとは、インターネットを経由せずに、他のServiceにHTTPリクエストを送信することができる機能です。つまり、自分のコードから直接、他のWorkersを呼び出すことができるのです。サービスバインディングは、新しいコンポーザビリティの世界を開きます。以下の例では、リクエストは認証Serviceによって検証されます。

export default {
  async fetch(request, environment) {
    const response = await environment.AUTH.fetch(request);
    if (response.status !== 200) {
      return response;
    }
    return new Response("Authenticated!");
  }
}

サービスバインディングは、標準の fetch APIを使用しているので、既存のユーティリティやライブラリを引き続き使用することができます。また,サービスバインディングの環境を変更することもできるので、Serviceの新しいバージョンをテストすることもできます.次の例では、リクエストの1%がServiceの「カナリア」デプロイメントにルーティングされます。カナリアへのリクエストが失敗すると、本番用のデプロイメントに送られ、再度試行することができます。

export default {
  canRetry(request) {
    return request.method === "GET" || request.method === "HEAD";
  },
  async fetch(request, environment) {
    if (Math.random() < 0.01) {
      const response = await environment.CANARY.fetch(request.clone());
      if (response.status < 500 || !canRetry(request)) {
        return response;
      }
    }
    return environment.PRODUCTION.fetch(request);
  }
}

Service間のインターフェースはHTTPですが、ネットワークはそうではありません。実際、ネットワークは存在しません。一般的な「マイクロサービスアーキテクチャ」では、サービスがネットワークを介して通信し、遅延や中断の影響を受けることがありますが、サービスバインディングはゼロコストで抽象化されています。Servicesをデプロイする際には、そのサービスバインディングの依存関係グラフを作成し、それらのServicesをすべて1つのデプロイメントにパッケージ化します。あるサービスが別のサービスを呼び出すとき、ネットワークの遅延はなく、リクエストは直ちに実行されます。

このゼロコストモデルにより、遅延やパフォーマンスを犠牲にすることなく、チームが組織内でコードを共有・再利用できるようになります。複雑なYAMLテンプレートや、サービスをオーケストレーションするための指数関数的なバックオフの時代は終わりました。ただコードを書くだけで、すべてがつながります。

今、未来を試す!

本日からServicesをご利用いただけます。すでにWorkersをご利用の方は、各スクリプトが1つの「本番環境」でServiceにアップグレードされていることにお気づきかと思います。ダッシュボードとすべての既存のCloudflare APIは、引き続きServicesで「機能」します。

また、オープンベータの開始に伴い、複数の「プレビュー」環境にコードを作成してデプロイすることもできます。サービスバインディングやバージョニングについてはまだ検討中ですが、使用可能になり次第、最新情報をお知らせします。

Servicesの詳細については、以下の資料をご覧ください。