はじめに
こんにちは! Microservices Platform Network チーム の hatappi です。
メルカリでは、2023年からCDNプロバイダーを Fastly から Cloudflare へと段階的に移行してきました。現在、ほぼすべての既存サービスのトラフィック移行が完了しており、新規サービスについては全て Cloudflare を使用しています。
この記事では、CDNプロバイダーの比較ではなく、移行プロセスに焦点を当て、スムーズに移行するために実施したアプローチを解説します。また、移行が私たちの最終的なゴールというわけではありません。その先の取り組みの一環として、社内向けの「CDN as a Service」についても紹介します。
背景
メルカリでは、これまでに開発環境および本番環境を合わせて数百のFastlyサービスが存在しており、これらは私たちNetworkチームによって管理されてきました(メルペイのサービスに関してはFintech SREチームが管理しています)。私たちのチームは、GCP VPCのようなクラウド・ネットワーキングやデータセンター・ネットワーキングも管理しています。そのため、限られた時間の中でスムーズに移行を進める方法を考える必要がありました。
移行ステップ
準備
FastlyとCloudflareはどちらもCDNプロバイダーですが、全く同じ挙動をするわけではありません。たとえば、キャッシュの挙動について見ると、FastlyではオリジンのVaryヘッダーを考慮してキャッシュを分けますが、Cloudflareは現時点では画像に対してのみ対応しています。このように、移行対象のサービスがFastlyでどのような機能を使用しているか、そしてその機能をCloudflareではどのように実現するかを調査する必要がありました。
移行機能を検討する際に重視したのは、現状の挙動を大きく変更しないことです。移行を始めることで、改善点を加えたり新しい機能を試したくなることもあります。そのようなアプローチは、数サービスの移行であれば許容されるかもしれませんが、数百のサービスに対して行うと移行完了に途方もない時間が必要になります。そのため、移行範囲を広げすぎないというこの方針は、移行をスムーズに進めるために重要でした。また、この方針は後のステップでも効果を発揮します。
実装
Cloudflareの管理にはTerraformを採用し、公式から提供されているTerraformプロバイダーを使用しました。Terraformのリソースは、各サービスごとに個別に使用するのではなく、Terraformモジュールを作成し、そのモジュールに必要な機能を実装することで、今後のサービス移行時にも再利用できるようにしました。
Fastlyでは、自分たちが実装したロジックやFastlyが提供するロジックが最終的に一つのVCL(Varnish Configuration Language)としてまとめられます。移行の初期段階では、各VCLを個別に確認し、CloudflareのTerraformリソースへ手作業で実装していました。このため、少なくとも実装には30分以上かかっていました。
しかし、各サービスの移行が進むにつれて、VCLのロジックの中でも移行が必要なものと無視できるものがパターン化してきました。そこで移行の後半では、Go を用いて移行スクリプトを作成し、VCLを元にTerraformモジュールの設定を自動化できるようにしました。そして、自動で設定できなかったロジックは、移行検討が必要なものとして出力するようにしました。これにより、シンプルなサービスであれば、数分で実装が完了するようになりました。
テスト
ほとんどのサービスには開発環境と本番環境があるため、まず開発環境でテストを行い、その後本番環境の移行を行います。しかし、トラフィックが多いサービスやミッションクリティカルな機能を提供するサービスの移行時には、事前に挙動をテストするためのコードを書きました。準備段階で述べたように、Fastlyと大きく挙動を変えていないため、Fastlyサービスの挙動を基準として比較するテストを書くことができました。これにより、自信を持ってトラフィックの移行を開始することができました。
トラフィックの移行
テストをどれだけ重ねても、本番のトラフィックを流す際には慎重に行う必要があります。特に、問題が発生した際には迅速にロールバックすることが求められます。
そこで私たちは、DNSレイヤーでこれらの要件を満たすアプローチを採用しました。メルカリではAmazon Route 53やGoogle Cloud DNSを使用しており、どちらもWeighted Routingをサポートしています。これにより、少しずつトラフィックをFastlyからCloudflareへ切り替えることができます。何か問題が発生した際には、CloudflareへのWeightを0%にするだけでロールバックが可能となり、手順もシンプルです。
移行中のモニタリングにはDatadogを使用し、いくつかのメトリクスを確認しました。
まず、意図したトラフィック率になっているかを監視します。以下の画像は、FastlyとCloudflareのリクエスト比率から見たCloudflareのトラフィック率を示しています。
次に、以下の画像はCloudflareへの全リクエストから見た、2xxステータスコード以外のリクエスト比率を示しています。トラフィックの増加に伴い、これらの値が増えないかを確認することも重要な指標となります。
また、クライアント側から見たFastlyサービスとCloudflareの挙動には大きな変更がないはずなので、それぞれのキャッシュ率やリクエスト数や使用帯域の比較も行いました。
すべてのサービスの移行が完全に無障害で終わったわけではありませんが、これらのアプローチにより大規模な障害を回避し、問題が発生した際には影響範囲を最小限に抑えることができました。
CDN as a Service
移行の次のステップとして、Networkチームが集中管理していたCDNサービスの運用をセルフサービス化し、開発者自身が開発・運用できるようにする「CDN as a Service」を目指しています。
今回は、「CDN as a Service」に向けた2つの取り組みを紹介します。
CDN Kit
移行の際に触れたTerraformモジュールに私たちは「CDN Kit」という名前をつけています。開発者はCDN Kitを利用することで、1つ1つTerraformリソースを定義する必要がなく、自分が実現したいことを手軽に達成できます。また、私たちPlatformチームとしては、全体に提供したいベストプラクティスを各サービスごとに変更するのではなく、モジュール内に含めることで一箇所で提供できます。
例えば、オリジンへのアクセスをCloudflareを通じて行うというシンプルな要件であれば、開発者は以下のようにCDN Kitを使用するだけで済みます。
module "cdn_kit" {
source = "..."
company = "mercari"
environment = "development"
domain = "example.mercari.com"
endpoints = {
"@" = {
backend = "example.com"
}
}
}
開発者から見るとシンプルな定義ですが、CDN Kitを利用することで、さまざまなリソースが自動的に作成されます。以下はその一例です。
- BigQuery へのログ送信
- Cloudflareが提供するログをBigQueryに格納する際は、通常Cloud Functionsを使用します(ドキュメント)。しかし、これらを各サービスごとに作成するのは手間がかかるため、CDN Kit内で必要なリソースを自動的に作成しています。
- Datadog モニターの作成
- ドメインに応じた自動更新される SSL/TLS 証明書の発行
権限付与システム
Cloudflareのダッシュボードは、インタラクティブにアクセス分析を行える強力なツールです。しかし、開発者にダッシュボードを公開するためには、以下の課題を解消する必要がありました。
- 退職者管理
- 権限付与の自動化
1つ目の退職者管理は、CloudflareのダッシュボードでSSOを有効にし、アイデンティティプロバイダーとしてOktaを利用することで解決しました(ドキュメント)。メルカリではOktaを使用しており、退職者の管理はITチームが担当しています。そのため、退職者処理の一環でOktaからアカウントが削除されると、Cloudflareのダッシュボードへのアクセスも自動的にできなくなります。このため、私たちNetworkチームは退職者管理を考慮する必要がありません。
2つ目の権限付与の自動化については、社内の既存のシステムと連携して動作する仕組みを実装しました。以下はその概要図です。
※ Team Kitとは、開発者グループの管理を行うためのTerraformモジュールです。
開発者チームを管理するTerraformモジュールであるTeam Kit、およびCloudflareを管理するCDN Kitは、GitHubのリポジトリで管理されています。これらのモジュールの更新を自動的に検知するGitHub Actions Workflow を作成しました。このWorkflowは、更新を検知すると、以下に示すような権限管理用のマニフェストファイルを生成し、リポジトリにコミットします。
account_id: [Cloudflare Account ID]
zone_id: [Cloudflare Zone ID]
zone_name: [Cloudflare Zone Name]
teams:
- team_id: [ID of Team Kit]
roles:
- Domain Administrator Read Only
users:
- email: [email address]
roles:
- Domain Administrator Read Only
次にマニフェストファイルの変更を検知して、別のGitHub Actions Workflowが動作し、マニフェストをもとにCloudflareの各権限を設定します。
Team KitとCDN Kitの変更を検知して動作するGitHub Actions Workflowで、Cloudflareの権限を直接変更しない理由は、マニフェストファイルを保持することで宣言的にCloudflare の権限を管理できるようにするためです。これにより、例えば手動で権限が変更された場合であっても、いつでもマニフェストに基づいて正しい状態に戻すことが可能となります。
この権限付与システムによって、開発者はNetworkチームに権限を依頼する必要なくダッシュボードを見ることができるようになりました。すでに、開発者自らがダッシュボード上で問題を発見し、解決する事例も観測されており、「CDN as a Service」への取り組みがすでに効果を発揮していることを嬉しく思います。
おわりに
この記事では、CDNプロバイダーの移行におけるアプローチを紹介し、その後のステップとして社内向けに提供する「CDN as a Service」の取り組みとしてCDN KitというTerraformモジュール、権限付与システムについて説明しました。