フューチャー技術ブログ

CDN 入門とエッジでのアプリケーション実行

はじめに

こんにちは。Technology Inovation Group(TIG)所属の小澤です。
春の入門祭り連載 9 日目を担当します。

私は、通常の業務とは別に社員有志で集まるクラウド勉強会に参加しています。まだ経験が浅い(2022年10月新卒入社)こともあり、クラウド勉強会では入門的な内容を中心に発表しています。

この記事では、私がクラウド勉強会で発表した CDN の入門とエッジロケーションでのアプリケーション実行について説明します。

CDN とは?

CDN (content delivery network, コンテンツ配信ネットワーク)は、Web アプリを含めた Web サイトのコンテンツ(HTML/CSS/JS/画像など)を高速配信するための仕組みです。

通常、 Web サイトの静的コンテンツ(HTML/CSS/JS/画像など)は Web サーバーに配置します。この場合、例えば全てのアクセスを単一の Web サーバーで担当するように構成すると、アクセスが集中するような時間帯や、通信する場所によって反応が遅くなってしまうことがあります。

これを解消する 1 つの方法は、Webサーバーの前にロードバランサーを入れて負荷分散を実現することです。 しかし、単にロードバランサーを導入するだけでは、世界中からのアクセスがある場合など、地理的な距離に起因する遅延は解消できません。この課題は、オンプレミスでもクラウドでも同様に生じます。

そこで、サーバーへの負荷分散と地理的な制約による遅延軽減を同時に実現できるのが CDN です。

CDN では、コンテンツが入っている元の Web サーバー(オリジンサーバー)へのアクセス集中を防ぎ、地理的に近いエッジロケーション(エッジサーバー)に誘導します。エッジロケーションはデータセンターの一種ですが、世界中に配置され、クラウドの通常リージョンのデータセンターよりも多く設置されています。CDN の標準的な方法では、エッジロケーションはユーザーからのアクセスがあると、オリジンサーバを参照して次回以降のアクセスのためにコンテンツをキャッシュしておきます。次回以降のアクセスでは、キャッシュしたコンテンツをユーザーに配信します。

こうすることで、本来はオリジンサーバーにアクセスするはずだったリクエストを、エッジロケーションが代理して担当することで、負荷分散と地理的な高速配信を実現できます。

なお、エッジロケーションはオリジンサーバーのコンテンツをキャッシュしているだけなので、当然データの整合性は一時的に取れなります。そのため、通常の CDN サービスでは、キャッシュを保持する期間(TTL, time to live)を設定できるようになっています。 整合性を高く保ちたい場合はキャッシュの保持期間を短くすればよいわけです。 もちろん短くしすぎると、CDN の負荷分散の利点は弱くなるので、要件に応じてバランスを判断する必要があります。

CDN の仕組み

CDN は、地理的に近いエッジロケーションに誘導すると説明しましたが、実際はどのようにこの仕組みを実現しているのでしょうか?

CDN 利用時も、クライアント(Web ブラウザなど)は、通常時と同じくオリジンサーバーに向けたリクエストを送信します。 当然、クライアントはサーバーが CDN を使っているかどうかを意識する必要はありません。クライアントは飽くまでオリジンサーバーに向けたリクエストを発するわけです。

そこで、 CDN の実現のためには、オリジンサーバーに向かっているリクエストを、エッジロケーションに向ける仕組みが必要です。

DNS

その方法として、通常は DNS(domain name system) の仕組みを利用することで、地理的に近いエッジロケーションへのリクエストを実現します。DNS は、クライアントがアクセスしようとしているドメインを IP アドレスに名前解決(変換)する仕組みです。例えば、google.com をドメインを 172.217.31.174 という IP アドレスに変換します。

一般的に、クライアントがリクエストを送るとき、まず組織内や ISP などの DNS サーバーにドメインの名前解決を要求します。 組織内や ISP などの DNS サーバーは通常、DNS キャッシュサーバーと呼ばれ、自分自身での名前解決ができないため、別の DNS サーバーに再問合せをします。 このように問合せを繰り返し(反復的問合せ)、最終的に権威 DNS サーバーにたどり着きます。 権威 DNS サーバーは、対象のドメインと IP アドレスの対応を管理しているので、それを元にクライアントへ IP アドレスを応答できます。

オリジンサーバーのドメインと CDN 用ドメインの紐づけ

CDN を実現するための設定として、通常は CNAME レコードを用いてオリジンサーバーのドメインと CDN 用ドメインを紐づけます。

CNAME レコードは、ドメインと IP アドレスではなく、ドメインと別名ドメインを紐づける仕組みです。

例えば、example.com というオリジンサーバーに対して、cdn.example.com といった CDN 用ドメインを紐づけます。 ただし、CDN 用ドメインは通常 CDN ベンダーが発行するため、この例のようにシンプルな形式とは限りません。

※example.com は例示に利用できるドメインです。

CDN 用ドメインに対応する IP アドレスの動的な名前解決

CDN を実現する肝となる仕組みがここです。 CDN 用ドメインは CDN ベンダーが管理しているため、権威 DNS サーバーの挙動は CDN ベンダーで決めることができます。

CDN を構成しない場合、権威 DNS サーバーは通常固定の IP アドレスを応答します。 しかしながら、CDN を構成する場合、固定の IP アドレスでは、地理的に近いエッジロケーションにアクセスさせるという要件を達成できません。

そこで、 CDN ベンダーの権威 DNS サーバーは、CDN 用ドメインに対応する IP アドレスを動的に変換して応答します

その際、一般的には送信元の IP アドレスや AS (Autonomous System, ネットワーク群)を読み取り利用することで、最寄りのエッジロケーションを導出します。

例えば、

送信元の IP アドレス(範囲) 最短のエッジロケーション 最短のエッジロケーションの IP アドレス
192.0.2.0 エッジロケーション A 203.0.113.0
192.0.2.1 エッジロケーション A 203.0.113.0
192.0.2.25 エッジロケーション A 203.0.113.0
198.51.100.0 エッジロケーション B 203.0.113.50
198.51.100.33 エッジロケーション B 203.0.113.50

のようなイメージです。

※192.0.2.0/24、198.51.100.0/24、203.0.113.0/24 は例示に利用できる IP アドレスブロックです。 実在する IP アドレスではありません。

以上のように、CDN では

  • オリジンサーバーのドメインと CDN 用ドメインの紐づけ
  • CDN 用ドメインに対応する IP アドレスの動的な名前解決

を用いて、オリジンサーバーへのリクエストを地理的に近いエッジロケーションへのリクエストに振り替るのが一般的です。

CDN サービスの例

ここでは、CDN サービスの例を各クラウドベンダーごとに簡単に紹介します。

AWS - Amazon CloudFront

AWS の提供する CDN サービスは Amazon CloudFront です。

Amazon CloudFront では、CDN のオリジンサーバーとして EC2 や S3、ELB などの他、オンプレミスのサーバーも指定できます。

Google Cloud - Cloud CDN

Google Cloud の提供する CDN サービスは Cloud CDN です。

Cloud CDN は他の CDN サービスと構成が異なり、Cloud Load Balancing に CDN 機能を付けるという方式で設定します。すなわち、Cloud CDN はロードバランサーと CDN が一体化している形です。

現在は CDN 機能付きロードバランサーのバックエンドとして、Cloud Storage やオンプレミスなど、HTTP 対応のあらゆる送信元を指定できるようになっているので、方式の違いをそこまで意識することなく、他の CDN サービスと同様に利用できます。

Cloudflare

Cloudflare はクラウドベンダーのひとつです。

もとは CDN をメインで提供するクラウドベンダーとして知られていましたが、純粋な CDN だけではなく、WAF(Web Application Firewall)などのセキュリティ機能も合わせて提供しています。

モダンな Web アプリケーションと CDN

近年の Web アプリケーションは、Web フロントエンドと Web API サーバーを分離した構成が主流です。 Web フロントエンドは、サーバー側で HTML を動的に生成するサーバーサイドレンダリング(SSR)ではなく、ブラウザの JavaScript が DOM を操作して軽量の HTML を書き換えるシングルページアプリケーション(SPA)の方式が一般的です。

また、SPA のデメリットを克服するために SPA と SSR を組み合わせた方式も主流になってきました。例えば、React ベースのフレームワークである Next.js や、Vue.js ベースのフレームワークである Nuxt.js を使えば、SPA と SSR の組み合わせを簡単に実現できます。

このようなモダンな Web アプリケーションの構成では、ブラウザ側の JavaScript の役割が大きくなり、必然的に JS ファイルのサイズが大きくなります。また静的サイトジェネレータ(SSG)を用いて Web サイトをビルドした場合も、HTML と JavaScript のサイズが大きくなります。

こうしたモダンな構成は、静的コンテンツとしての HTML/JavaScript を高速に配信する CDN との相性が良く、広く使われています。

CDN は意識して設定しなくても使える

SPA からなるモダンな Web アプリケーションを開発する場合、CDN の設定を意識しなくても CDN を適用したデプロイが簡単にできる仕組みが整っています。

Vercel

Next.js 開発元の Vercel 社が提供するクラウドサービスの Vercel は、Next.js をはじめとするモダン Web アプリケーションのデプロイ先として優れています。

GitHub 等のリモートリポジトリに Next.js アプリがコミットされるだけで、自動デプロイされ Next.js の機能をサーバーの設定なしに使用できます。その 1 つの機能として、CDN も自動で構成されます

AWS Amplify

AWS の提供する AWS Amplify は、モダンな Web アプリケーションを高速に開発しデプロイするための一連の機能を集めたサービスです。 AWS Amplify は、フルマネージド型のウェブアプリケーションホスティングサービスとして利用できます。

AWS Amplify は、利用されているフロントエンドフレームワークを自動で判別して CDN を構成します。 Vercel と同様、リモートリポジトリを指定すれば CI/CD も面倒な設定なしに構成できます。

AWS Amplify でデプロイした Web フロントエンドと、Amazon API Gateway や AWS Lambda と組み合わせてサーバーレスな Web アプリケーションを構成できます。

CDN の活用とビジネス形態

CDN は、その特徴から

  • 地理的に離れた場所にユーザーがいる
  • 厳しい低遅延性が求められる

という場面に有益です。

このような場面にまず当てはまるのが、toC の Web サービスでしょう。世界的なサービスはもちろんのこと、日本国内向けのサービスであったとしても CDN は広く利用されています。テック系スタートアップ企業においても、現在は資金が少ない時点で柔軟にクラウドリソースを利用できるため、CDN サービスを活用して UX を高めることは有効です。

他方で、フューチャーのような IT コンサルティング企業やいわゆる SI 企業では、業務アプリケーション開発の割合が高いでしょう。 toB の業務アプリケーションでは、そもそもインターネットアクセスを遮断することがあり、また toC サービスとは求められる非機能要件が異なる場合も多く、CDN が有益な場面は少なそうに思えます。

しかしながら、現在は業務アプリケーションであってもインターネットアクセスを前提とするものが増えています。例えばグローバル企業の業務システムで拠点が全世界にあるような場合は、CDN を用いた配信が有益になります。 さらには、BtoBtoC の開発や自社サービスの開発を行うことも少なくないため、CDN は私たちのビジネスにも重要な技術要素です。

以下では、これまでの CDN とは異なる新しい活用法の可能性を紹介します。

CDN のエッジサーバーでアプリケーションを実行する

ここまで、CDN は静的コンテンツ(HTML/CSS/ブラウザで動く JavaScript など)を配信する仕組みとして紹介してきました。

しかし、近年はこの CDN のエッジサーバーでアプリケーションを実行するサービスが複数登場しています

さきほど説明した React ベースの Web フロントエンドフレームワークである Next.js には Web API を実現する API Routes という機能が含まれています。Next.js アプリケーションを Vercel にデプロイした場合、通常の API Routes は、Vercel が提供する関数実行環境(データセンター)で実行されます。しかし近年、新たに Edge API Routes という機能が追加され、エッジサーバー上で関数を実行できるようになりました。これは Vercel の Edge Runtime というエッジ環境で動作します。

このようにエッジサーバーでアプリケーションを実行するサービスには、次のようなものがあります:

  • Vercel Edge Functions
  • Cloudflare Workers
  • AWS CloudFront Functions
  • AWS Lambda@Edge

なお、下 2 つの AWS サービスは似ていますが、CloudFront Functions は AWS における「エッジロケーション」、Lambda@Edge は AWS における「リージョンエッジ」で実行されます。そのため、CloudFront Functions の方がクライアントに近い場所で実行されます。

これらのサービスは、例えば次のようなユースケースをエッジサーバー上で利用できる(すなわち、通常のクラウドより低遅延である)ことが強みとされています:

  • ヘッダの書き換え
  • トークンの検証と認可
  • デバイス判定
  • A/B テスト
  • IP ブロック
  • リダイレクト

Cloudflare Workers とは?

ここでは、エッジでアプリケーションを実行する環境としてシンプルで試しやすい Cloudflare Workers を紹介します。

Cloudflare Workers は Cloudflare 社が提供するサーバレスのサービスです。通常の AWS Lambda や Google Cloud Functions などとは異なり、データセンターではなく CDN のエッジサーバーでコードを実行することができます。

Cloudflare Workers の特徴として

  • 0ms cold starts のサポート
    • すなわち、通常のサーバーレス環境で発生するコールドスタートが発生しない
    • Cloudflare workers では内部的にコンテナではなく、isolate と呼ばれる環境を使用しているため実現可能
  • CPU runtime の 10ms 制限
    • そのため、重い処理は実行できない

を挙げることができます。

Cloudflare Workers のストレージ

Cloudflare Workers では、エッジロケーションでストレージを利用できます:

  • Cloudflare Workers KV
    • Key-Value ストレージ
    • 結果整合性
  • Durable Objects
    • Key-Value ストレージ
    • 強い整合性

RDB サービスの Cloudflare D1

また、エッジロケーションで RDB が動く Cloudflare D1 も登場しています

D1 は SQLite をベースに構築されています。

ストレージと同様に、Cloudflare Workers のロジックから操作することができます。

Service Worker

Cloudflare Workers では、 Service Worker の API が利用できます
※ここでいう「API」は「Web API」ではありません(「ライブラリ」に似た本来の広い意味です)。

Service Worker とは、Web ブラウザがメインの JS の処理とは別のスレッドで、JS をバックグラウンド実行する仕組み(Web Worker)の一つです。Service Worker は、ブラウザとサーバーの間のプロキシサーバーのように利用することができます。
(本記事とは別の文脈で、Service Worker のハンズオン記事を執筆予定です。)

Cloudflare 独自の API ではなく、Service Worker API という標準的な記法をすべて使えるのが利点です
もちろん Service Worker がクライアント(Web ブラウザ)の中で実行されるのに対し、Cloudflare Workers はエッジサーバーで実行される点が異なります。

Cloudflare Workers の開発例

Cloudflare Workers の開発例を試してみましょう。

注意 Cloudflare は、他のクラウドサービスと同じく、アプリケーションやその他のリソースをインターネットに公開できるサービスです。個人環境以外で試す場合は、所属組織のルールを確認してください。

基本的な手順

Cloudflare Workers の開発では、Wrangler(ラングラー)と呼ばれる Cloudflare Workers のための CLI ツールを使います。

Node.js のインストールされたローカル環境で

npx wrangler init my-project
cd my-project

を実行すればひな形が作成されます。my-project は自由に変えてください。
なお、Would you like to use TypeScript? に対して Yes を返せば、TypeScript の準備が自動で完了し、すぐに使えるようになります!

src/index.js または src/index.ts が Cloudflare Workers のロジックを実装するファイルです。
TypeScript を選択したとして、シンプルな文字列を返す Web API を次のように実装します:

src/index.ts
export default {
async fetch(request: Request): Promise<Response> {
return new Response("Hello World!");
},
};

ローカル環境の起動は

npx wrangler dev --local

で行い、動作確認できます。 localhost:8787 に Hello World と表示されたら成功です。
通常の Web API と同じく、ブラウザや curl コマンド等で確認してください。

src/index.js
async fetch(request: Request): Promise<Response> {
return new Response("Hello World!");
},

この fetch が Service Worker API の 1 つです。

fetch イベント(クライアントからの HTTP リクエスト)を検知するとこのメソッドを実行します。fetch の引数には Request 型で、HTTP リクエストの bodyheadersmethod などのプロパティが含まれています。こうしたプロパティを使えば、通常の Web API と同じようなロジックを記述することができます。

例えば、GET リクエストのクエリパラメータの message 属性でメッセージを送り、それを読み取ってレスポンスを返す処理を書いてみましょう。

src/index.ts
export default {
async fetch(request: Request): Promise<Response> {
switch(request.method) {
case 'GET':
const { searchParams } = new URL(request.url)
const message = searchParams.get('message')
return new Response(
`送信されたメッセージは「${message}」です。`,
{status: 200}
)
default:
return new Response(
'許可されていないHTTPメソッドです。',
{status: 405}
)
}
}
}

クエリパラメータに message=こんにちは を含めてリクエストすると、正しくメッセージが読み込まれ表示されました。

Wrangler では、デプロイも次のコマンドで簡単に行えます(インターネット公開されるので十分注意してください)。

npx wrangler publish

このように数行のコマンドで、ローカルの動作確認とエッジへのデプロイが完結します。

様々な活用例

他の実装例は、公式ドキュメントに記載されています。

  • JSON の取得
  • リダイレクト
  • A/B テスト
  • CORS ヘッダープロキシ
  • トークンの検証と認可
  • Basic 認証
  • ストレージや DB へのアクセス

など、このページが実質的にユースケースのカタログのようになっています。エッジのアプリケーション実行の可能性を模索し、ぜひ新しい使い道を見つけていきましょう!

おわりに

この記事では、社内のクラウド勉強会での発表をもとに、CDN の入門的解説と、エッジでのアプリケーション実行の紹介を行いました。

春の入門祭り連載はまだまだ続きます! 引き続きどうぞよろしくお願いいたします。

次は石野さんの初心者による初心者のためのGit入門です。