メルカリにおけるCDN利用の現在とこれから

こんにちは。メルカリ ネットワークチームの@kanemaruです。
メルカリのネットワークチームは、パブリッククラウドのネットワークをはじめとした様々なレイヤに関するコンポーネントの設計・開発・運用を担当しています (参考記事1, 2)。そうしたコンポーネントのうちのひとつが、Content Delivery Network (CDN)です。

メルカリは他の多くのウェブサービスと同様に、CDNを利用しています。現時点ではコンテンツのキャッシュやセキュリティの担保といった、基本的ながら重要な機能に重点を置いて利用していますが、そうした中で見えてきた運用上の課題もあります。この記事では、メルカリにおけるCDNの現在の状況と最近の取り組みを振り返りながら、今後の展望についてご紹介します。

なぜCDNを使うのか?

CDNの分野では、コンテンツのキャッシュ機能をはじめとした多彩な機能を様々なプロバイダーが提供しています。そうした中、メルカリでは主に2つの目的でCDNを利用しています。

  1. コンテンツのキャッシュによる体験向上とコスト軽減
  2. ネットワークに関連したセキュリティ上の脅威の緩和

コンテンツのキャッシュによる体験向上とコスト軽減

メルカリのアプリとサーバの間では様々なデータのやりとりを行いますが、データサイズの観点から見るとその大部分を占めるのが商品の画像です。したがって、商品画像のダウンロードにかかる時間が短ければ短いほど、商品の閲覧をスムーズに行えます。

これを実現するために、商品画像をはじめとした静的オブジェクトをCDNにキャッシュしています。オブジェクトがCDNのエッジサーバにキャッシュされていれば、オリジンサーバからオブジェクトを取得する場合と比較してダウンロードが高速化されることが期待でき、よりよい体験をお客さまに届けることができます。

CDNを利用したキャッシュは金銭的コストの観点でもメリットがあります。メルカリの商品画像はAmazon S3に格納されているため、商品画像のオブジェクトをS3から取得するたびに、データ転送量に応じた課金が発生します。一般に、あるオブジェクトをクライアントへ配信するのにかかるコストは オブジェクトストレージ >>> CDN です。したがって、より多くのオブジェクトをキャッシュしてキャッシュヒット率を高めることができれば、コンテンツ配信にかかる総コストを抑えることができます。

メルカリでは、CDNプロバイダーとして主にFastlyを利用しています。それに加えて、Fastlyとオリジンサーバの間でImageFluxを利用することで、画像データの動的変換による転送量削減を行っています (参考記事)。

ネットワークに関連したセキュリティ上の脅威の緩和

メルカリのAPIや画像データのエンドポイントは、ネットワーク経由の様々な脅威に常に晒されています。そこで、CDNプロバイダー (Fastly) の提供する防御機能を活用しながら、攻撃への対策を独自に実装しています。

DDoS攻撃やアプリケーションの脆弱性を狙った攻撃などがその典型例ですが、特にTCP/UDP floodingやHTTP floodingといった大量の無意味なトラフィックを送信するタイプの攻撃に対しては、それらがアプリケーションサーバに到達するより前に十分な帯域幅のあるレイヤで遮断することが効果的です。そしてCDNはその性質上非常に大きな帯域幅を保有しているため、緩和策を実装する場所として非常に適しています。

具体的には、攻撃を受ける前に防御を設定しておくことで攻撃の影響を軽減するレートリミットに加え、攻撃を受けている間に発見した疑わしいトラフィックをパターンベースで逐次遮断する仕組みなどを実装しています。

また、SQLインジェクションをはじめとした疑わしいリクエストを検知・遮断することを目的として、Web Application Firewall (WAF) を一部で導入しています。

CDN利用の現状と最近の取り組み

前節で紹介した2つの目的を達成するために、ネットワークチームは各マイクロサービスの開発チーム (プロダクトチーム) と協力しながら、CDN利用時のサービスアーキテクチャ検討や設定変更といったタスクを日々遂行しています。ここでは、そうした現状にまつわる3つのトピックを紹介します。

Infrastructure as Code (IaC)

メルカリではインフラに関連する設定の大部分をコードで管理しており、CDNもその対象のひとつです。

メルカリで主に利用しているFastlyでは、一般的なRESTful API経由での設定に加え、Fastly VCLというスクリプト言語でのカスタマイズにも対応しています。後者を利用すると、複雑な要件であっても柔軟に実装することができます。そこで、これら2つを設定箇所ごとに使い分けることで、設定定義の見通しの良さと柔軟性の両立を図っています:

  • Terraform (RESTful API): 公開するエンドポイントのドメインやバックエンド (オリジン) の定義
  • Fastly VCL: リクエストパスごとの細かな制御、セキュリティ関連ロジックの実装

CDN設定を新たに作成したり変更したりする時は、その内容を含むGitHub PRを作成し、マージするとCloud Buildのジョブが実行されてFastly側に変更内容が反映されます。この仕組みにはメルカリ内の他のマイクロサービスと共通の基盤を使用しており、クレデンシャルの機密性を保ったまま安全にデプロイができるようになっています (参考記事)。

また、CDN設定を新たに作成する時は、ドメイン名やオリジンのホスト名といった情報からTerraformおよびVCLの雛形を自動的に生成するツールを利用しています。これにより、毎回設定をゼロから作成する手間が省けるほか、チーム内でのタスクの属人化を防ぐようにしています。

日々のオペレーションのプロセス

プロダクトチームが何らかのCDN設定の作成・変更を実施したいとき、それを実現するフローには2つのパターンがあります:

  1. プロダクトチームの担当者は希望する内容を記載したチケットを作成し、その内容に応じてネットワークチームの担当者はPRを作成のうえ適用する
  2. プロダクトチームの担当者は直接GitHub PRを作成し、ネットワーク担当者はそれをレビューしたうえ適用する

大部分のケースでは前者のフローで設定を実施しますが、CDNの扱いに慣れているプロダクトチームの場合は後者のフローで設定することもあります。

CDNは非常に便利で強力な反面、CDNプロバイダー固有の設定方法や仕様が存在するため、設定の際には注意が必要です。そうした点に配慮しつつ最適な方法で実装するために、いずれの場合においても、ネットワークチームの担当者が変更内容をレビューしたうえでプロダクション環境に適用しています。

また、システムアーキテクチャに関わるものや重要度の高い変更については、プロダクトチームが作成するdesign docのレビューに早い段階から参加することで、適切な手段を選定するようにしています。

攻撃への備え

前節で紹介したように、CDNのレイヤではレートリミットやパターンマッチングによって疑わしいトラフィックを遮断ないし緩和する仕組みを実装しています。

しかし、どんなにこうした対策を事前に実装したとしても、それらが完璧に攻撃を遮断することを期待するのは難しいものです。現実的には、攻撃が開始された後にそのパターンを分析し、適応的にルールを更新しながら攻撃を緩和することになります。一方で、小規模な攻撃自体は日常的に発生しているものの、能動的な対応が必要なものはそう多くはありません。したがって、いざ大きな規模の攻撃が発生した際には、「普段あまり慣れていない対応」を、「攻撃の影響をいち早く緩和するために素早く実施する」ことが求められます。

これを実現に近付けるため、以下のようないくつかの事前準備を行いました:

  • 疑わしいトラフィックパターンの検出を支援するツールの準備: アクセスログを分析し特徴的なトラフィックパターンを抽出するためのダッシュボードを作成しています。
  • 疑わしいトラフィックを一括で遮断するツールの準備: 発見したトラフィックパターンを遮断するためのルールをFastlyに素早く適用するためのツールを作成しています。
  • 対応手順をまとめたドキュメント (playbook) の準備: こうしたツールの使い方を含め、攻撃への対処フローをまとめたドキュメント (playbook) を作成しています。加えて、攻撃の兆候を検知するDatadogモニターのメッセージにこのplyabookへのリンクを貼ることで、すぐに参照できる状態にしています。

今後の展望

ここまでCDNにまつわる現状を紹介しましたが、こうして見てみると特異な点は無く、基本的なことを忠実に実施しているのがお分かりいただけるかと思います。

CDNサービスの世界では魅力的な新機能が日々開発・提供されていますが、これまではそうした新機能をどんどん導入して利用するというよりは、便利な機能を利用しつつも安定的に稼動することに重点を置いてきました。しかし、安定的に稼動することはもちろん重要ではあるものの、いくつかの運用上の問題を抱えていることもまた事実です。

ここでは代表的な課題と、それらに対して検討中のアイデアをご紹介します。

ネットワークチームをボトルネックにしないために: 抽象化と移譲

前節で触れたように、CDNに関する設定の大部分はネットワークチームの担当者が実施しています。このフローはメリットもある反面、ネットワークチームがボトルネックになりやすく、社内のプロダクトチームからするとCDN設定待ち時間が発生してしまいます。この問題に対するひとつの解決案として、プロダクトチームの担当者が直接CDN設定を実施できるようにする、というアプローチがあります。しかし、CDNベンダー固有の設定方法や仕様を理解する必要があるため、そう簡単ではありません。

そこで必要なのが、適切な抽象化の実装と権限移譲です。メルカリではこれまで、プラットフォームレイヤーの設定や機能を抽象化したうえでプロダクトチームに公開することで、「適切な権限移譲」「低い学習コスト」「高い安全性」を実現してきました。過去に紹介したstarter-kitteam-kit, k8s-kitなどがまさにその例ですが、たとえばk8s-kitでは、アプリケーションワークロードの定義をCUEで記述することで、適切なバリデーションを行いながら必要なKubernetesマニフェスト (YAML) の自動生成を実現しました。

こうした事例と同様に、CDN設定を抽象化する仕組み、いわば "cdn-kit" を実装することで、CDN設定にまつわる問題を解決できることを期待しています。

実装の断片化を防ぐために: エンドツーエンドでの一体管理

この記事の冒頭で触れた通り、私たちのチームはエッジネットワークからクラウド上のネットワークに至るまで広い範囲に責任を持っており、全体的な設計から日常的な設定までを一つのチームで担当しています。このデータパス上にはCDNを含め様々なコンポーネントがありますが、コントロールプレーンの観点から見ると各々は完全に独立していて、個別に設定を行っています。

この状態は何が問題なのでしょうか? たとえば、「プロダクトチームAが管理するGCPプロジェクト上のAPIエンドポイント https://mercari.example.com/api/foobar について、100リクエスト/分/IPアドレスのレートリミットを設定したい」という要求があるとします。この設定を実装するコンポーネントの選択肢は様々です – CDN、アプリケーションサービス自体、あるいは中間のロードバランサーやサービスメッシュに実装するアプローチもあります。このとき、設定の実装がコンポーネント毎に完全に異なるため、結果的に元々の要求の意図と実装が密結合してしまいます。そこで、設定の意図 – ここでは100リクエストのレートリミット – を抽象化された情報として保持しておけば、その時々に応じて適切な実装を選択しやすくなります。また、プロダクトチームの観点から見ると、インフラの実装の詳細を気にする必要を無くせるでしょう。

このように、データパス上のコンポーネントを組み合わせて一体的に管理しながら最適状態の実現を目指せるのは、エンドツーエンドで責任を持っているチームだからこそ取り組めることであると言えます。

おわりに

この記事では、メルカリにおけるCDN利用のこれまでと今後について簡単にご紹介しました。
これまでは、CDNサービスが提供するコンテンツのキャッシュとセキュリティの機能を活用しながら、IaCの実現や脅威に対する独自の備えなどを行ってきました。今後はそうした基礎部分を維持・強化しつつ、より高い運用性の実現に取り組んでいこうとしています。

近年、CDNの世界では様々なベンダーが積極的に商品開発を行っており、魅力的な機能が次々と登場しています。そうした動向に目を配りつつ、CDNのみならずエンドツーエンドでの最適なネットワークを実現し、最終的にお客さまによりよい価値を提供すること – それがネットワークチームのミッションです。

  • X
  • Facebook
  • linkedin
  • このエントリーをはてなブックマークに追加