$30 off During Our Annual Pro Sale. View Details »

コンテンツの主権を守るため(?)、高機能画像CDNからAWS自前対応に乗り換えた話

 コンテンツの主権を守るため(?)、高機能画像CDNからAWS自前対応に乗り換えた話

2024年11月15日(金)、Media JAWSでの発表資料です。高機能画像CDNサービスからコンテンツ内容への(軽い)注意を受けたことをきっかけに、メディア企業としての自主性を担保する目的で、必要な機能だけを自前で実装し、AWSからの配信に乗り換えた経緯と方法論をご紹介しています。

Yoichiro Nagao

November 24, 2024
Tweet

Other Decks in Programming

Transcript

  1. コンテンツの主権を守るため(?)、 高機能画像CDNからAWS自前対応に 乗り換えた話 長尾 洋一郎 Yoichiro Nagao 株式会社講談社 IT戦略企画室 テクノロジーラボ部長

    KODANSHAtech合同会社 ゼネラルマネージャー 小島 貴之 Takayuki Kojima KODANSHAtech合同会社 エンジニアリングマネージャー
  2. ABOUT US 長尾 洋一郎 Yoichiro Nagao 株式会社講談社 IT戦略企画室 テクノロジーラボ部長 KODANSHAtech合同会社

    ゼネラルマネージャー 小島 貴之 Takayuki Kojima KODANSHAtech合同会社 エンジニアリングマネージャー
  3. 開発の経緯① • これまで SaaS の画像・動画配信の仕組みを利用していた • 今回 FRIDAY で規約上配信しないでほしいと言われるコンテン ツがあった

    • 当然ですが日本の法律上問題ないものを配信しているわけで、 我々から見ると問題ない内容 (週刊誌に載っているような内容) • 最終的にそのコンテンツは配信することができた
  4. AWS を選択した理由 • 一つは単純に我々が管理しているシステムの多くはインフラに AWS を採用しているという点 • もう1点は、これまでの仕組みでは、一度 S3 にアップロードし

    てから SaaS 側にアップロードする構成としていたので、元 データが S3 にある状態だった • そのまま元データを再利用できると移行がしやすかった
  5. 機能要件② - 画像 • 画像変換機能 • 入力ファイルは jpeg / png

    • 1枚の元画像から複数のサイズへの変換 • 画像の切り抜き • 複数フォーマットでの出力 (jpeg / webp) • ぼかし画像の生成 • 画像の複合 • 顔検出 • 人物にズームしたサムネイルの生成
  6. 機能要件③ - 動画 • 入力ファイルは mp4 • HLS 形式への変換 •

    サンプル動画の生成(動画の一部抽出+ぼかしをかける) • → 今回は見送り • 動画からサムネイル画像の生成
  7. 事前調査による AWS での実現性の確認 • CDN → CloudFront • ユーザごとに有効期限を設定 →

    署名付き URL or Cookie • 画像変換 → Lambda • AWS のサイトでも紹介されている Sharp を使うことを検討 • https://aws.amazon.com/jp/solutions/implementations/serverless-image- handler/ • 顔検出 → Rekognition • 動画変換 → Elemental MediaConvert
  8. 画像 - 発生した問題①-1 - Rekognition のエラー • 5MB 以上の画像を Rekognition

    に投げるとエラーになる • 大きい画像のテストで発覚 • 上限が5MB なのは仕様 • https://docs.aws.amazon.com/ja_jp/rekognition/latest/dg/limits.ht ml • ファイルサイズではなく、バッファに展開した後のサイズが 5MB を超えるとエラーとなる(pixel 数に依存する) • 運用上、5MB 以上のファイルもアップロードされることがあ るので対策が必要
  9. 画像 - 発生した問題①-2 - Rekognition のエラー 対策 • 5MB 以下に収まるまで画像を縮小してから渡すことで解決

    • Rekognition の顔検出で返却される座標は [0,1] の範囲で返る ので、仮に画像を 1/2 などに縮小したとしても結果は変わらな かったので対応しやすかった • 縮小すると精度は落ちるが、そもそも大きい画像なので、実際 には縮小しても検出結果は変わらなかった
  10. 画像 - 発生した問題②-1 - Lambda がメモリ不足でエラー • Lambda の設定は 1GB

    • Lambda は短期間にアクセスすると実行環境が再利用されるこ とがある • https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda- runtime-environment.html • 通常は問題ないが Sharp はデフォルトで内部的にキャッシュを 持っていて、連続して呼ぶと使用メモリが積み上がりエラーに なっていた • 例)9500x6000 pixel の場合 • 展開後のバッファサイズは 170 MB 程度 • 1回の変換で 700-800 MB ほど使う
  11. 画像 - 発生した問題② -2 - Lambda がメモリ不足でエラー 対策 • sharp.cache

    の無効化 • これで安定した • 大きい画像はそもそもかなりメモリを使うので 1GB → 1.5 GB に変更 • メモリを 1536MB (1.5倍) にすると、1ms あたりのコストも 1.5倍になるが、実行速度は半分以下になり、コストはむしろ減 少するので効率の良い設定 • https://repost.aws/ja/knowledge-center/lambda-memory- compute-cost
  12. 動画 - 発生した問題①-1 - 署名付きURLによる配信 • 動画もサブスクユーザのみが再生できるので署名付きURL また は Cookie

    による制御を検討 • ただし、生成されるプレイリスト(.m3u8)は解像度ごと の .m3u8 を含み、さらに各 .m3u8 の中に .ts 形式のファイル リストが含まれる • このそれぞれのパスを署名付きにする必要がある
  13. 動画 - 発生した問題①-2 - 署名付きURLによる配信 • 例).m3u8 ファイル #EXTM3U #EXT-X-VERSION:3

    … #EXT-X-STREAM-INF:BANDWIDTH=358380,AVERAGE-BANDWIDTH=… video_480x270.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=562812,AVERAGE-BANDWIDTH=… video_640x360p.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=899700,AVERAGE-BANDWIDTH=… video_960x540p.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=1593970,AVERAGE-BANDWIDTH=… video_1280x720p.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=3129959,AVERAGE-BANDWIDTH=… video_1920x1080p.m3u8
  14. 動画 - 発生した問題①-3 - 署名付きURLによる配信 • 例)video_1920x1080p.m3u8 #EXTM3U #EXT-X-VERSION:3 ...

    #EXTINF:3, video_1280x720p/00000/video_1280x720p_00001.ts #EXTINF:3, video_1280x720p/00000/video_1280x720p_00002.ts #EXTINF:3, video_1280x720p/00000/video_1280x720p_00003.ts #EXTINF:3, video_1280x720p/00000/video_1280x720p_00004.ts #EXT-X-ENDLIST
  15. 動画 - 発生した問題①-4 - 署名付きURLによる配信 • 複数のパスにまたがるので最初は署名付き Cookie を検討 •

    ただ、別ドメインの扱いや、フロントエンドのどこで Cookie 発行するかなど気を付けるポイントがいくつかありそうだった • その後、ドキュメントを読み込んだところ、カスタムポリシー を使用すると URL にワイルドカードを使えることが分かった ので、この方法を検証 • https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/Dev eloperGuide/private-content-creating-signed-url-custom- policy.html#private-content-custom-policy-statement-values
  16. 動画 - 発生した問題①-6 - 署名付きURLによる配信 • 共通の署名で特定のパス以下にアクセスできるようになったが、 フロントエンドで再生する際に、すべてのパスに署名をつけら れるかを確認 •

    フロントエンド (Next.js) では動画の再生に hls.js と react- player の2つのライブラリが使われていたが、どちらも署名を 付与することが可能だった
  17. 動画 - 発生した問題①-7 - 署名付きURLによる配信 • 例)hls.js const videoSrc =

    "https://example.com/video1/video.m3u8"; const hls = new Hls({ xhrSetup: function (xhr, url) { const query = "<SIGNATURE>"; xhr.open("GET", url + query, true); }, }); hls.loadSource(videoSrc); hls.attachMedia(video);
  18. 動画 - 発生した問題①-8 - 署名付きURLによる配信 • 例)react-player <ReactPlayer url="https://example.com/video1/video.m3u8" config={{

    file: { hlsOptions: { xhrSetup: function (xhr: XMLHttpRequest, url: string) { const query = "<SIGNATURE>"; xhr.open("GET", url + query, true); }, }, }, }} />
  19. 動画 - 発生した問題②-1 - iPhone で再生できない • iPhone で再生できない問題が発覚 •

    前述の署名を付与する方法は Media Source Extensions API を 利用している • https://developer.mozilla.org/ja/docs/Web/API/Media_Source_Exte nsions_API
  20. 動画 - 発生した問題②-2 - iPhone で再生できない • Media Source は

    iOS Safari では利用できなかった (iPad OS は利用可能) • https://caniuse.com/mediasource
  21. 動画 - 発生した問題②-3 - iPhone で再生できない 対策 • 最終的に Lambda

    から .m3u8 ファイルを返す際に、含まれる 各パスを署名付きに変換して返すことで解決 • ワイルドカードの署名をここで活かすことができた • ユーザごとに結果が変わるので CloudFront のキャッシュはし ない形にした
  22. MediaConvert のオプション調整-1 • 最初、オプション調整などせずに実行したところ、解像度を上 げると SaaS よりも出力ファイルサイズが大きくなる傾向に あった - 元ファイル

    SaaS MediaConvert 1920x1080 326 MB - 67.9 MB 1280x720 28 MB 17.04 MB 27.7 MB 960x540 21 MB 12.18 MB 20.6 MB 640x360 15 MB 10.27 MB 9.1 MB 480x270 12 MB 7.89 MB 6.6 MB
  23. MediaConvert のオプション調整-4 • 以下のパターンで検証 • パターン1 : 帯域幅削減フィルター(PRO) ON •

    パターン2 : QVBR 品質レベル=7に統一 • パターン3 : 品質チューニングレベル=マルチパス(PRO) • パターン4 : 1〜3 全て有効 - 元ファイル SaaS 既存の設定 パターン1 パターン2 パターン3 パターン4 1920x1080 326M - 67.9 MB 64.4 MB 34.2 MB 67.5 MB 33.1 MB 1280x720 28M 17.04 MB 27.7 MB 26.6 MB 19.2 MB 27.6 MB 18.5 MB 960x540 21M 12.18 MB 20.6 MB 19.7 MB 14.7 MB 20.6 MB 14.1 MB 640x360 15M 10.27 MB 9.1 MB 8.8 MB 9.1 MB 9.0 MB 8.7 MB 480x270 12M 7.89 MB 6.6 MB 6.5 MB 6.6 MB 6.6 MB 6.4 MB
  24. 画像のキャッシュ① • 1枚の元画像から複数のサイズを生成する • Rekognition の結果は変わらないのに毎回実行するのは非効率 • 変換コストは 1画像 =

    0.001USD (0.15円) 対策 • 顔検出の結果を Redis にキャッシュすることにした • 時間短縮 (300-400ms) & コスト削減となった
  25. 画像のキャッシュ② • Sharp で強くぼかしをかきえた時の処理が遅い • 数sec かかる • 元画像が png

    だととくに遅い • 結果的に Lambda の実行時間も伸びる 対策 • Sharp の実装で改善 + 変換結果を S3 にキャッシュ
  26. 署名付き URL の生成が遅い問題-1 • 署名付き URL を生成するのに 1URL に対して約 1ms

    かかって いた • 1ms は早いのだが、今回大量に画像を一覧表示しているページ があり、かつ1画像に対して8パターンのサイズを生成していた • 具体的には約300画像あり、 x8倍で 2400ms かかってしまった • SaaS のときにも似たような処理があったが、その際は 1URL = 0.06ms 程度だったので問題にはなっていなかった。