Cloudflareはcdnjsを運用しています。cdnjsは、オープンソースプロジェクトで、人気のJavaScriptライブラリとリソースをCloudflare Workersのネットワークを経由して配信することで、Webサイトを加速させます。12月に本格的なアップデートをしてから、スケーラビリティと耐障害性を求めて、cdnjsのモデルチェンジに重点を置きました。本日、Cloudflareがcdnjsを配信する方法を発表できることを嬉しく思います。これはCloudflare Workersと分散キーバリューストアWorkers KVを用いるサーバーレスインフラストラクチャへの移行です。

cdnjsとは?

詳しくない方のためにご説明すると、cdnjsとは「コンテンツ配信ネットワーク(CDN)for JavaScript(JS)」の頭文字語です。CDNは、単にインターネットミームや猫の動画、またはHTMLページなどインターネットコンテンツを提供するサーバーの地理的に分散されたネットワークのことです。当社の場合、CDNはCloudflareのグローバルに分散された200以上のデータセンターのネットワークの拡大を指します。

では、これとみなさまがどのように関わっていくのかをご説明します。まず、ページ読み込み時間が超高速になります。事実上これを含めて、どのWebサイトでも読み込むためにJSライブラリを取得する必要があります。たとえば、シドニーを拠点にするWebサイトを訪れるとします。このサイトは、76.2%のWebサイトで見られる人気ライブラリであるjQueryからのローカルファイルを含んでいます。New Yorkからこのサイトにアクセスした場合、遅延に気づくかもしれません。TLSハンドシェイク を含むラウンドトリップにかかる時間はもちろん、ファイルの取得するために300ミリ秒を軽く超えてしまうからです。しかし、このWebサイトがcdnj.cloudflare.comを使ってjQueryを参照すると、バッファローにある直近のCloudflareデータセンターからファイルを取り出すことができて、レイテンシーが驚きの20ミリ秒まで短縮します。

cdnjsは水面下で稼働していますが、11%以上のWebサイトで使用され、インターネットをはるかに速くし、さらに信頼性の高い場所に変えてくれています。7月、cdnjsはおよそ190億件ものリクエストを処理しました。データの量は3.46PB(ペタバイト)です。

ファイルの保存場所

cdnjsがインターネットのスピードアップさせていますが、もちろん魔法ではありません。

これまでは、Cloudflareのコアデータセンター1か所で負荷分散されたマシンの多くは、cdnjs.cloudflare.comのオリジンとして機能する補助記憶装置から定期的にcdnjsファイルをプルしてきました。新しいファイルがリクエストされると、Cloudflareがキャッシュして、どのデータセンターからでも迅速に取得できるようにしました。

補助記憶装置は、オープンソースのGitHubリポジトリの形式で、JS、CSSや他のWebライブラリのカタログです。ということは、みなさんも含めて、誰でもレビューやその他のプロセスの対象として貢献できるということです。

しかし最近まで、こうした既存の作業は労働集約型で脆弱なものでした。

このブログ記事では、cdnjsの背後にあるインフラストラクチャをより高速で確実、維持が簡単になるように変えたのか、その理由を説明していきます。まず、旧システムに関する問題と懸念を概説して、コミュニティがどのようにcdnjsに貢献したのかを考えます。そして、Workers KVへの移行によってもたらされるメリットを見ていきます。そのあとで、新しいアーキテクチャだけではなく、Webサイトとcdnjs APIへのアップグレードにも触れていきます。最後に、cdnjsの歴史を振り返り、これからどこに向かっていくのかについて、検討していきます。

勘違いしていませんか、PRの方法を

専門家ではない読者の方にとって、プルリクエスト(PR)とは、リポジトリで行った変更とマージするリクエストのことです。従来、cdnjsにJavaScfriptライブラリを含めたいと思ったら、まずGitHub上でパッケージを記述したJSONファイルと、含めたいバージョンの追加ファイルでcdnjs/cdnjsにPRを作成していました。当社の古いボットがPRを承認すると、手動でレビューされ、メ‑ンテナーがマージします。そしてパッケージがcdnjsと統合されます。

簡単そうですよね?そのリポジトリをフォークしてクローン、そしてファイルをいくつかコピペするだけですね?

その通りです。もし書き込みにかけられる時間が何時間あったり、大文字と小文字を区別するファイルシステムや300 GBリポジトリをgit clone(リポジトリを複製)するためのディスクの空きスペースが数百ギガバイトもあったりすれば、コントリビュートするのは簡単です。しかし、時間が足りない場合でも大丈夫です。git sparse-checkout(リポジトリの一部をチェックアウト)してこの作業を終わらせることができます。gitをご存知ありませんか?GitHubのUIを使って手動で1回に1つのファイルを手動で追加するだけです。

これで、大事なことはお分かりになったと思います。macOSがデフォルトで大文字と小文字を区別しないことを発見するためだけに、無邪気にリポジトリのクローニングに10時間も費やしたことを確かに覚えています。

しかし、cdnjsの更新はコントリビュータにとってだけでなく、メンテナーにとっても難しいのです。歴史的に、コミュニティはバージョンファイルに直接コントリビュートできていましたが、これは悪意のあるものになる可能性がありました。各ファイルを手動で確認する必要があり、公式ライブラリソースとファイルをdiffし、マルウェアチェックを実行するため、メンテナーの作業を大量に増やしていました。

すでにcdnjsにあるパッケージをどのようにアップデートしたのでしょうか。各パッケージを記述したJSONファイルにライブラリの新バージョンを探す場所をボットに指示する自動更新の定義がオプションであります。存在する場合は、パッケージがnpmまたはGitHubから新バージョンをリリースした時に、ボットがダウンロードし、ファイルをcdnjs/cdnjsに、そして算出されたサブリソース完全性(SRI)のハッシュをcdnjs/SRIにプッシュします。自動更新のプロパティがない場合は、手動PRを作ってcdnjsを新バージョンに更新するのは、みなさんの責任となります。

cdnjsの注意喚起

4月、当社のコアデータセンターの1つでメンテナンス中に技術者が、他のデータセンターとの外部接続すべてを供給するケーブルを切断してしまう事故があり、そのデータセンターが約4時間オフラインになってしまいました。特に、影響を受けたデータセンターがプライマリcdnjsオリジンWebサーバーをハウジングしていたため、この出来事が、cdnjsに対する最初の警鐘となりました。この場合では、外部プロバイダーで実行するバックアップがありましたが、当社を救ったのは、実はCloudflareのグローバルキャッシュでした。キャッシュされていないプロパティだけが読み込みされていなかったため、停止の影響を最小限に抑えることができました。

cdnjsの提供方法をめぐり、信頼性とパフォーマンスの両方をどのように改善できるかを考え始めました。そこで、すぐに目を向けたのが、エッジでの開発に向けた当社独自のプラットフォームであるCloudflare Workersです。そして、Workersに構築された強力なツールの1つがWorkers KVです。高度な読み取りアプリケーションのために最適化され、低レイテンシーでグローバルに分散されたキーバリューストアです。

当社はあれこれと考え合わせ、cdnjs/cdnjsリポジトリをプルし、ディスクからファイルを処理する代わりに、物理的マシンを完全に省き、世界中にデータを分散してエッジから直接ファイルを処理することができると気が付きました。この方法ならば、cdnjsは拡張性を高めつつ、オリジンデータセンターの障害から回復することができます。

Workers KVが救う

一見したところ、Workers KVの利用を決定するのは、簡単なことでした。cdnjsのファイルを変更することは決してありませんが、頻繁な読み取りが必要となるため、Workers KVは完璧でした。

しかし、移行を計画しているうちに、cdnjsにある700万以上のアセットがあると、Workers KVの10 MiB値制限を超えるファイルが間違いなく存在するという懸念が出てきました。調査した後、数百ものcdnjsファイルが大容量で、大半がJavaScriptソースマップであることが明らかになりました。

そして、このアイディアが頭に浮かびました。Workers KVに圧縮されたバージョンのcdnjsファイルを保管することができ、容量の大きいファイル問題を解決するだけでなく、ファイルの処理方法も最適化できます。

インターネット料金を支払っている場合、帯域幅が高額になってしまうのはお分かりでしょう。そのため、すべての最新ブラウザで、利用可能なときは常に、圧縮されたWebコンテンツを取得することにします。同様にCloudflare内でも、帯域幅を削減するために、可能な時は常に徹底して圧縮されたコンテンツを処理してオンザフライ圧縮を試行してみました。その結果、Brotli フォームとgzipフォームの両方でWorkers KVに書き込んで、早めにすべてのcdnjsファイルを圧縮することに決めました。そうすることで、レイテンシー要件がなくなり、オンザフライ圧縮よりも圧力レベルを高くすることができました。

つまり今、cdnjsファイルは速くてコンパクトに処理できているということです!

cdnjsの大変身

今日では、cdnjsのJavaScriptライブラリを含めたい場合にまず、GitHub上のPRを当社の新しいリポジトリcdnjs/packageに作成します。リポジトリは50MBで簡単にクローンでき、何千ものJSONファイルで構成されます。JSONファイルはそれぞれcdnjsパッケージとnpmまたはgitからどのように自動更新されるかを記述します。ファイルが(新規ボットによって)自動化されたCIに検証されると、メンテナーがマージし、パッケージは自動的に、自動更新サービスでエンロールされます。

新システムでは、セキュリティと保守性が優先されます。まず始めとして、cdnjsバージョンファイルは新バージョンとマージする際、当社のボットが作成して人によるエラーを最小限に抑えます。cdnjs/packageのJSONファイルを間違えを起こしやすい人間が追加しますが、メンテナーに承認される前に当社のボットで検査します。各ファイルは、自動的にJSONスキーマが検証され、npmまたはGitHubの人気度もチェックされます。

ボットは、新しいリリースを検出すると、Brotliとgzipで圧縮されたバージョンのファイルをWorkers KV内のファイルネームスペースへプッシュします。各エントリで、ボットはWorkers KVのメタデータEtagLast-Modified HTTPヘッダーのために書きます。以前のように、ボットは圧縮されていないファイルのサブリソース完全性(SRI)ハッシュも算出しますが、今ではWorkers KVのSRIネームスペースの代わりにそれをプッシュします。

次に、cdnjs.cloudflare.comから新規ファイルがリクエストされると、Cloudflare WorkerがクライアントのAccept-Encodingヘッダーを検査し、ETagとLast-Modified メタデータをWorkers KVとともに、Brotliまたはgzip圧縮バージョンのどちらかを取得します。圧縮されたファイルがCloudflareを介して戻るため、今後のリクエストと、必要とあれば、圧縮していないオンザフライがキャッシュされます。

現時点では、Workers KVのサイズ制限を超えるファイルがまだ若干あります。したがって、Cloudflare WorkersがWorkers KVからのファイルの取得に失敗すると、オリジナルのgitリポジトリにバックアップされたオリジンから取得されることになります。今後数ヶ月で、徐々にこのインフラストラクチャをなくしていく予定です。

WebサイトとAPIの拡張

コアのcdnjsインフラストラクチャに加えて、他の多くのコンポーネントもアップグレードされました!

cdnjsプロジェクトのホームページでは、Mattが構築したカッコいい新ベータWebサイトがみなさんをお迎えします。VueNuxtで構成されているベータWebサイトは、完全にcdnjs APIで運用されています。結果として、最新パッケージ情報で常に最新のものであり、このサイトの提供で必要とされるリソースの使用量が少なくて済みます。このサイトは、最初のページの読み取り以降、クライアント側で完全に実行されており、cdnjsの終わりのない成長に合わせて、当社が拡張するために役立ちます。

実際に、cdnjs APIもその拡張性を強化し、cdnjsとWorkers KVで見てきたものに近いサーバーレスアーキテクチャから恩恵を受けてきました。

Workers KVに移行する前に、cdnjs APIは約300MBのメタデータを生成する定期的なプロセスに依存してきました。cdnjs APIのバックエンドでは、この巨大な「package.min.js」ファイルをメモリーに取得し、それを用いてAPIを操作しました。もし気になるのであれば、ファイルはまだこちらでホストされていますので、どうぞ。ただし、ひとこと言っておきますが、みなさんのブラウザに遅れが出るかもしれません!同様に、SRIはcdnjs/SRIへとプッシュされ、ローカルでAPIにクローンされ、SRI応答に提供されました。

すべてのcdnjsファイルが(許可されたサイズ制限内で)Workers KVへと移された後、こうしたレガシープロセスは持続不可能となり、何百万もの読み取りとあり得ないほど長い時間が必要となりました。そのため、当社はWorkers KVで見つかったすべてのメタデータをアップロードすることにしました。メタデータを4つのネームスペースに分けました。1つ目はパッケージレベルのメタデータで、2つ目はバージョン固有のメタデータ、3つ目は集約された1つのメタデータ、最後はファイルSRIです。

cdnjsのサーバーレスデザインのように、Cloudflare Workerはmetadata.speedcdnjs.com上にあり、いくつものパブリックエンドポイントを使って、Workers KVからデータを提供します。現在のところ、cdnjs APIは完全にこうしたエンドポイントと統合され、cdnjsの拡張にしたがって、明確なソリューションを提供します。

cdnjsの透明性と将来性

2011年1月の誕生以来、cdnjsは常に透明性に深く根ざし、コミュニティから強さを引き出しています。たとえcdnjsが大規模になって、2011年7月に創設者のRyan KirkmanとThomas Davisが当社と手を組んだ時でも、プロジェクトは完全にGitHubでオープンソースのままであり続けました。

時が経つにつれて、創設者が積極的に活動するのが難しくなり、サポートをコミュニティに頼るようになりました。ほとんど存在しない予算とリポジトリへのわずかなアクセスで、コアcdnjsのメンテナーは毎日プロジェクトを進めるために駆け回りました。

昨年、これが創設者に連絡を取るきっかけとなりました。2人ともプロジェクトの支援を受けることを喜んでくれました。Cloudflareの役割が大きくなり、cdnjsはこれまで以上に安定し、活発に参加するメンバー達がCloudflareとコミュニティの両方から集まりました。

しかし、レガシーシステムへの依存を排除し、ファイルをWorkers KVへと保存するようになると、cdnjsが専有になるのではないかという懸念の声が上がりました。しかし、どうぞご心配なく。cdnjsがこれまで通りの透明性を維持し、限りなくオープンソースであることを保証するために当社は懸命に作業してきました。コミュニティが監査の更新をWorkers KVでできるように、新規リポジトリのcdnjs/ログがあります。これはボットがすべてのWorkers KV関連イベントを記録するために利用するものです。さらに誰でも、cdnjs APIからSRIを取得して、cdnjsファイルの整合性を検証することができます。

まとめ

概して、この1年間はcdnjsにとって波乱の年でしたが、その弱点すべてが危険信号として機能し、当社がより良いシステムを構築する手助けとなっています。最近では、1か所で物理的なマシンに依存することのリスクを軽減でき、Workers KVでファイルが保管されるサーバーレスインフラストラクチャへと移行しました。

現在、cdnjsは何も心配することなく、どこかに消えてしまうことはありません。特にメンテナーのSvenMattにはこのプロジェクトに勢いを提供し、cdnjsの拡張からこのブログ記事の編集まで、すべてをしてくれたことに感謝の気持ちを送ります。

将来に向けて、cdnjsの透明性を可能な限り高めるために尽力します。cdnjsが向上するにしたがって、さらにブログ記事を通して、コミュニティに最新情報をリリースしていきます。関心がおありの場合は、当社のブログをサブスクライブしてください。結局、cndjsを可能にしているのは、コミュニティです!積極的に活動するGitHubコントリビュータのみなさん、cdnjsコミュニティフォーラムのメンバーのみなさん、これからもよろしくお願いします!