Slide 1

Slide 1 text

Ruby×AWSで作る 動画変換システム yumu 2025.01.18 東京Ruby会議12 1

Slide 2

Slide 2 text

2 自己紹介 GMOペパボ minne事業部 プロダクト開発チーム 2023年 新卒入社 湯村 美吹香 yumu エンジニア5年目。 バックエンドが主戦場ですが、フロントやインフラもや ります! KoRのスポンサーLTをしてました。 ● 最近の推し漫画 : メダリスト⛸ ● X : @myumura3

Slide 3

Slide 3 text

3 minneとは

Slide 4

Slide 4 text

4 動画投稿機能をリリースしました🎉

Slide 5

Slide 5 text

5 動画を配信するには変換処理が必要 変換

Slide 6

Slide 6 text

6 アジェンダ 1. システムのアーキテクチャ • なぜ自前実装を選択したのか • アーキテクチャの詳細 2. 実装の核となる技術 • streamio-ffmpeg • Shoryuken 3. 開発・運用の実際 • 開発環境 • モニタリング • リソース監視と最適化

Slide 7

Slide 7 text

7 1. システムのアーキテクチャ

Slide 8

Slide 8 text

8 SaaS VS 自前実装 SaaS 自前実装 e.g. AWS MediaConvert e.g. FFmpegを直接使用 ⭕ APIを通して簡単に利用できる ⭕ 費用が安い ⭕ 運用の手間が少ない ⭕ 変換のカスタマイズ性が高い ✖ 処理件数に応じて費用が増加 ✖ 実装・運用に手間がかかる ✖ 変換のカスタマイズ性に制限があ る

Slide 9

Slide 9 text

9 SaaS VS 自前実装 SaaS 自前実装 e.g. AWS MediaConvert e.g. FFmpegを直接使用 ⭕ APIを通して簡単に利用できる ⭕ 費用が安い ⭕ 運用の手間が少ない ⭕ 変換のカスタマイズ性が高い ✖ 処理件数に応じて費用が増加 ✖ 実装・運用に手間がかかる ✖ 変換のカスタマイズ性に制限があ る streamio-ffmpeg https://github.com/streamio/streamio-ffmpeg

Slide 10

Slide 10 text

10 全体アーキテクチャ 署名付きURL 動画アップロード S3 署名付きURL 動画ダイレクト アップロード

Slide 11

Slide 11 text

11 全体アーキテクチャ Shoryuken Shoryuken 署名付きURL 動画アップロード イベント送信 ウイルス スキャン 動画を取得 ポーリング 動画にタグを付与 S3 SQS ポーリング ウイルススキャン イベント送信 動画にタグを付与

Slide 12

Slide 12 text

12 全体アーキテクチャ Shoryuken Shoryuken 署名付きURL 動画アップロード イベント送信 ウイルス スキャン イベント送信 メッセージ送信 動画を取得 ポーリング 動画にタグを付与 S3 SQS Lambda SQS イベント送信 チェックOKの場合、メッ セージ送信

Slide 13

Slide 13 text

13 全体アーキテクチャ Shoryuken Shoryuken Shoryuken Shoryuken 署名付きURL 動画アップロード イベント送信 ウイルス スキャン イベント送信 メッセージ送信 動画を取得 ポーリング 動画にタグを付与 ポーリング 動画を取得 変換後の動画をアップロード 動画の変換 S3 SQS Lambda SQS 変換後の動画を アップロード 動画を変換

Slide 14

Slide 14 text

14 全体アーキテクチャ Shoryuken Shoryuken Shoryuken Shoryuken 署名付きURL 動画アップロード イベント送信 ウイルス スキャン イベント送信 メッセージ送信 動画を取得 ポーリング 動画にタグを付与 ポーリング 動画を取得 変換後の動画をアップロード 動画の変換 S3 SQS Lambda SQS

Slide 15

Slide 15 text

15 2. 実装の核となる技術

Slide 16

Slide 16 text

16 streamio-ffmpeg gemの活用 movie = FFMPEG::Movie.new('input.mp4') puts movie.duration # 動画の長さ puts movie.resolution # 解像度 puts movie.bitrate # ビットレート movie.transcode('output.mp4', { video_codec: 'libx264', # H.264でエンコード resolution: '640x360', # 解像度を指定 video_bitrate: 800 # ビットレートを指定 }) { |progress| puts "変換進捗: #{progress * 100}%" }

Slide 17

Slide 17 text

17 streamio-ffmpeg gemの活用 movie = FFMPEG::Movie.new('input.mp4') puts movie.duration # 動画の長さ puts movie.resolution # 解像度 puts movie.bitrate # ビットレート movie.transcode('output.mp4', { video_codec: 'libx264', # H.264でエンコード resolution: '640x360', # 解像度を指定 video_bitrate: 800 # ビットレートを指定 }) { |progress| puts "変換進捗: #{progress * 100}%" } ffmpeg -i input.mp4 -vcodec libx264 -s 640x360 -b:v 800k output.mp4

Slide 18

Slide 18 text

18 streamio-ffmpeg gemの活用 movie = FFMPEG::Movie.new('input.mp4') puts movie.duration # 動画の長さ puts movie.resolution # 解像度 puts movie.bitrate # ビットレート movie.transcode('output.mp4', { video_codec: 'libx264', # H.264でエンコード resolution: '640x360', # 解像度を指定 video_bitrate: 800 # ビットレートを指定 }) { |progress| puts "変換進捗: #{progress * 100}%" } ffmpeg -i input.mp4 -vcodec libx264 -s 640x360 -b:v 800k output.mp4 ffprobe -v quiet -print_format json -show_format -show_streams input.mp4

Slide 19

Slide 19 text

19 デモ: 実際の変換処理 movie = FFMPEG::Movie.new('sample.mov') puts "=== 変換前の動画情報 ===" puts "長さ: #{movie.duration}秒" movie.transcode('output.mp4', { video_codec: 'libx264', resolution: '360x640', video_bitrate: 100 }) { |progress| print "\r進捗: #{progress * 100}%" } result = FFMPEG::Movie.new('output.mp4') puts "\n\n=== 変換後の動画情報 ===" puts "長さ: #{result.duration}秒"

Slide 20

Slide 20 text

20 デモ: 実際の変換処理

Slide 21

Slide 21 text

21 デモ: 実際の変換処理

Slide 22

Slide 22 text

22 デモ: 実際の変換処理

Slide 23

Slide 23 text

23 Shoryukenによる非同期処理 class VideoConverterWorker include Shoryuken::Worker shoryuken_options queue: 'video_converter_queue', auto_delete: true, body_parser: :json, auto_visibility_timeout: true def perform(sqs_msg, body) # ここに処理を実装 end end https://github.com/ruby-shoryuken/shoryuken

Slide 24

Slide 24 text

24 Shoryukenによる非同期処理 def perform(sqs_msg, body) # S3から動画をダウンロード download_from_s3(body['video_key'], input_path) # 動画の変換処理(作品詳細ページ用と作品一覧ページ用) movie = FFMPEG::Movie.new(input_path) movie.transcode(detail_path, detail_options) movie.transcode(thumbnail_path, thumbnail_options) # 変換後の動画をS3にアップロード upload_to_s3("detail/#{body['video_key']}", detail_path) upload_to_s3("thumbnail/#{body['video_key']}", thumbnail_path) end

Slide 25

Slide 25 text

25 3. 開発・運用の実際

Slide 26

Slide 26 text

26 開発環境 services: localstack: image: localstack/localstack environment: - SERVICES=s3,sqs ports: - "4566:4566" worker: build: . environment: - AWS_ENDPOINT=http://localstack:4566 volumes: - .:/app

Slide 27

Slide 27 text

27 モニタリング ● デッドレターキューの監視 ● メッセージの経過時間の監視

Slide 28

Slide 28 text

28 リソース監視と最適化 CPUは3コアくらいが 適度かな メモリは1.5GiBは 必要そう

Slide 29

Slide 29 text

29 まとめ

Slide 30

Slide 30 text

30 実績 いい感じに稼働している!

Slide 31

Slide 31 text

31 今後の展望 変換処理の最適化

Slide 32

Slide 32 text

32 今後の展望 変換処理の最適化 パフォーマンスチューニング

Slide 33

Slide 33 text

33 今後の展望 変換処理の最適化 パフォーマンスチューニング ストリーミング配信への対応

Slide 34

Slide 34 text

34 まとめ 1. 動画変換システムはRubyで実現可能!

Slide 35

Slide 35 text

35 まとめ 1. 動画変換システムはRubyで実現可能! 2. バックグラウンドジョブ処理にShoryukenを採用!

Slide 36

Slide 36 text

36 まとめ 1. 動画変換システムはRubyで実現可能! 2. バックグラウンドジョブ処理にShoryukenを採用! 3. 自前実装とマネージドサービスを組み合わせるとお得!

Slide 37

Slide 37 text

37 Thank You! Thank You!