Slide 1

Slide 1 text

音声プラットフォームの 
 アーキテクチャ変遷から学ぶ、 
 クラウドネイティブなバッチ処理 
 2025/4/22 株式会社Voicy 千田 航己 


Slide 2

Slide 2 text

© Voicy, Inc. はじめに
 クラウド環境におけるバッチ処理の設計 
 ● ただクラウドで実行すればいいわけではない
 ○ 例: オンプレでやっていたことをそのままクラウドの仮想サーバー上で実行 
 ■ → ランニングコストはむしろ上がる傾向 
 
 ● クラウドネイティブなアーキテクチャが必要 
 ○ → Voicyの音声処理のアーキテクチャ変遷を通じて解説 
 


Slide 3

Slide 3 text

© Voicy, Inc. この発表で言いたいこと 
 「パフォーマンス」と「コスト」の両方を改善
 ● 事前準備
 ○ 疎結合
 ■ Evnet-driven
 ■ Queueing
 ○ 冪等
 ■ 並列処理
 ■ リトライ
 ● Autoscaling
 ○ いつスケールアウトするべきか/いつスケールインしていいか 
 すべてはAutoscalingのために 


Slide 4

Slide 4 text

© Voicy, Inc. 自己紹介
 ● 名前
 ○ 千田 航己 (せんだ こうき) 
 ● 担当
 ○ チームリード
 ○ バックエンド開発, クラウドインフラ 
 ● 仕事でよく使う技術
 ○ Go
 ○ Kubernetes
 ○ AWS
 ● 趣味
 ○ ギターと将棋とオムライス作り 
 ソフトウェアエンジニア 


Slide 5

Slide 5 text

© Voicy, Inc. Voicyとは 
 スマートフォン1台で音声の発信や聴取を楽しめるプラットフォーム 
 会員登録者数 
 250万人超 
 チャンネル数 
 2000超 


Slide 6

Slide 6 text

© Voicy, Inc.

Slide 7

Slide 7 text

© Voicy, Inc. Voicyにおける音声処理 
 収録された音源が配信されるまで 
 パーソナリティ
 収録
 リスナー
 音声処理1
 音声処理2
 音量調節
 ノイズ低減
 など
 統一フォーマット
 への変換


Slide 8

Slide 8 text

© Voicy, Inc. リスナー目線 
 ● コア機能「安定した聴取体験」
 パーソナリティ目線 
 ● 編集の手間をかける必要がない
 ● 録ったらすぐに公開したい
 ○ → 数分程度では処理されてほしい 
 Voicyにおける音声処理 
 重要性とビジネス要件 
 音声処理1
 音声処理2
 音量調節
 ノイズ低減
 など
 統一フォーマット
 への変換


Slide 9

Slide 9 text

© Voicy, Inc. アーキテクチャ変遷 
 ● 初期のアーキテクチャ
 ● Step 1. Event-drivenアーキテクチャ
 ● Step 2. 並列処理
 ● Step 3. Autoscaling
 ● (Step 4. Cluster Autoscaling)
 目次


Slide 10

Slide 10 text

初期のアーキテクチャ 


Slide 11

Slide 11 text

© Voicy, Inc. 初期のアーキテクチャ 
 ● 特徴
 ○ RDBで処理状況を管理 
 ○ 直列実行
 Cronによる定期実行 
 バッチサーバー
 (仮想サーバー)
 RDB (音源のメタデータ)
 オブジェクトストレージ (音源本体)
 依頼者
 under_processing = 1 
 内製
 音声処理サービス
 on K8s
 リクエスト
 定期監視
 1件ずつ
 処理依頼


Slide 12

Slide 12 text

© Voicy, Inc. 初期のアーキテクチャ 
 ● 特徴
 ○ RDBで処理状況を管理 
 ○ 直列実行
 Cronによる定期実行 
 RDB (音源のメタデータ)
 オブジェクトストレージ (音源本体)
 依頼者
 under_processing = 1 
 リクエスト 
 アップロード 
 バッチサーバー
 (仮想サーバー)
 内製
 音声処理サービス
 on K8s


Slide 13

Slide 13 text

© Voicy, Inc. 初期のアーキテクチャ 
 ● 特徴
 ○ RDBで処理状況を管理 
 ○ 直列実行
 Cronによる定期実行 
 RDB (音源のメタデータ)
 オブジェクトストレージ (音源本体)
 依頼者
 under_processing = 1 
 定期監視
 バッチサーバー
 (仮想サーバー)
 内製
 音声処理サービス
 on K8s


Slide 14

Slide 14 text

© Voicy, Inc. 初期のアーキテクチャ 
 ● 特徴
 ○ RDBで処理状況を管理 
 ○ 直列実行 
 Cronによる定期実行 
 RDB (音源のメタデータ)
 オブジェクトストレージ (音源本体)
 依頼者
 under_processing = 1 
 参照
 1件ずつ
 処理依頼
 バッチサーバー
 (仮想サーバー)
 内製
 音声処理サービス
 on K8s


Slide 15

Slide 15 text

© Voicy, Inc. 初期のアーキテクチャ 
 ● 特徴
 ○ RDBで処理状況を管理 
 ○ 直列実行
 Cronによる定期実行 
 RDB (音源のメタデータ)
 オブジェクトストレージ (音源本体)
 依頼者
 under_processing = 1
 取得
 バッチサーバー
 (仮想サーバー)
 内製
 音声処理サービス
 on K8s


Slide 16

Slide 16 text

© Voicy, Inc. 初期のアーキテクチャ 
 ● 特徴
 ○ RDBで処理状況を管理 
 ○ 直列実行
 Cronによる定期実行 
 RDB (音源のメタデータ)
 オブジェクトストレージ (音源本体)
 依頼者
 under_processing = 1
 処理
 バッチサーバー
 (仮想サーバー)
 内製
 音声処理サービス
 on K8s


Slide 17

Slide 17 text

© Voicy, Inc. 初期のアーキテクチャ 
 ● 特徴
 ○ RDBで処理状況を管理 
 ○ 直列実行
 Cronによる定期実行 
 RDB (音源のメタデータ)
 オブジェクトストレージ (音源本体)
 依頼者
 under_processing = 1
 格納
 バッチサーバー
 (仮想サーバー)
 内製
 音声処理サービス
 on K8s


Slide 18

Slide 18 text

© Voicy, Inc. 初期のアーキテクチャ 
 ● 特徴
 ○ RDBで処理状況を管理 
 ○ 直列実行
 Cronによる定期実行 
 RDB (音源のメタデータ)
 オブジェクトストレージ (音源本体)
 依頼者
 under_processing = 1
 完了連絡
 バッチサーバー
 (仮想サーバー)
 内製
 音声処理サービス
 on K8s


Slide 19

Slide 19 text

© Voicy, Inc. 初期のアーキテクチャ 
 ● 特徴
 ○ RDBで処理状況を管理 
 ○ 直列実行
 Cronによる定期実行 
 RDB (音源のメタデータ)
 オブジェクトストレージ (音源本体)
 依頼者
 under_processing = 0 
 完了連絡
 記録
 バッチサーバー
 (仮想サーバー)
 内製
 音声処理サービス
 on K8s


Slide 20

Slide 20 text

© Voicy, Inc. 初期アーキテクチャの問題点 
 ● サーバーのリソース量が一定
 ○ 短時間にたくさんアップロードされると待ち時間が長くなる 
 ○ 仕事がないときでもお金がかかる 
 ● 直列処理
 ○ 巨大データがひとつあるだけで後続の処理が滞る 
 ● 状態管理の方法に難あり
 ○ ステータスが二値しかなくジョブの状態管理が難しい 
 ○ DBが他の用途にも使われており、責務が混在 
 スケーラビリティがない 
 バッチサーバー
 (仮想サーバー)
 内製
 音声処理サーバー
 on K8s
 RDB (音源のメタデータ)
 オブジェクトストレージ (音源本体)


Slide 21

Slide 21 text

Step 1. Event-drivenアーキテクチャ 


Slide 22

Slide 22 text

© Voicy, Inc. Step 1. Event-drivenアーキテクチャ 
 ● ジョブの処理状況の把握が困難
 ○ ステータスが処理対象かそうでないかの二値しかない 
 ● DBが他の用途にも使われており、責務が混在
 ○ 関係ない負荷の影響を受ける / 関係ない処理に影響を与える 
 
 Pick up: 初期アーキテクチャの問題点 
 バッチサーバー
 (仮想サーバー)
 RDB (音源のメタデータ)
 依頼者
 under_processing = 1 
 リクエスト
 定期監視


Slide 23

Slide 23 text

© Voicy, Inc. Step 1. Event-drivenアーキテクチャ 
 ● ProducerとConsumerが疎結合になる
 ● ジョブの処理状況を管理しやすい
 ○ 「待ち/処理中/完了」を表現 
 ○ リトライが容易 など
 ● Queue自体も他のシステムと独立にスケール可能
 Queueを採用する (VoicyではAmazon SQS) 
 Job Handler (Consumer)
 on K8s
 Queue
 依頼者 (Producer)
 リクエスト
 定期監視
 (Polling)
 (これは初期アーキテクチャでも実現されていた)
 (汎用DBでも同じことはできるが大変)


Slide 24

Slide 24 text

© Voicy, Inc. Step 1. Event-drivenアーキテクチャ 
 ● 「同じ処理を何度実行しても同じ結果になる」ようにしておく
 ○ → 失敗したら再実行すればよくなる 
 一連の処理を冪等にする 
 内製
 音声処理サーバー
 on K8s
 処理依頼
 Job Handler (Consumer)
 on K8s
 Queue
 Polling
 失敗しました
 もう一回頼む


Slide 25

Slide 25 text

© Voicy, Inc. Step 1 終了後のアーキテクチャ 
 入り口がQueueになった & リトライしやすくなった 
 オブジェクトストレージ (音源本体)
 依頼者
 Job Handler (Consumer)
 on K8s
 Queue
 リクエスト
 定期監視
 (Polling)
 処理依頼
 内製
 音声処理サービス
 on K8s


Slide 26

Slide 26 text

© Voicy, Inc. 比較: 初期のアーキテクチャ 
 ● 特徴
 ○ RDBで処理状況を管理 
 ○ 直列実行
 Cronによる定期実行 
 バッチサーバー
 (仮想サーバー)
 RDB (音源のメタデータ)
 オブジェクトストレージ (音源本体)
 依頼者
 under_processing = 1 
 内製
 音声処理サービス
 on K8s
 リクエスト
 定期監視
 1件ずつ
 処理依頼


Slide 27

Slide 27 text

Step 2. 並列処理 


Slide 28

Slide 28 text

© Voicy, Inc. Step 2. 並列処理 
 ● 直列処理
 ○ 巨大データがひとつあるだけで後続の処理が滞る 
 
 Pick up: 初期アーキテクチャの問題点 
 バッチサーバー
 (仮想サーバー)
 RDB (音源のメタデータ)
 under_processing = 1 
 参照
 1件ずつ
 処理依頼
 内製
 音声処理サーバー
 on K8s


Slide 29

Slide 29 text

© Voicy, Inc. Step 2. 並列処理 
 ● 入り口がQueueになったことでジョブの状態管理が楽になっている
 ● 一連の処理を冪等化したので、同じ処理が複数回走っても壊れない
 
 Step 1 の結果 
 1件ずつ
 処理依頼
 Queue
 Polling
 内製
 音声処理サーバー
 on K8s
 Job Handler (Consumer)
 on K8s


Slide 30

Slide 30 text

© Voicy, Inc. Step 2. 並列処理 
 ● 入り口がQueueになったことでジョブの状態管理が楽になっている
 ● 一連の処理を冪等化したので、同じ処理が複数回走っても壊れない
 → 8倍高速に! 
 音声処理サーバーへのリクエストを並行化 
 並行して複数処理依頼 
 Queue
 Polling
 以前の発表: https://speakerdeck.com/thousanda/20230705-concurrent-normalization 
 内製
 音声処理サーバー
 on K8s
 Job Handler (Consumer)
 on K8s


Slide 31

Slide 31 text

© Voicy, Inc. Step 2 終了後のアーキテクチャ 
 ジョブの並列処理が可能になりました 
 オブジェクトストレージ (音源本体)
 依頼者
 Queue
 リクエスト
 Polling
 処理依頼
 Job Handler (Consumer)
 on K8s
 内製
 音声処理サービス
 on K8s


Slide 32

Slide 32 text

© Voicy, Inc. 比較: 初期のアーキテクチャ 
 ● 特徴
 ○ RDBで処理状況を管理 
 ○ 直列実行
 Cronによる定期実行 
 バッチサーバー
 (仮想サーバー)
 RDB (音源のメタデータ)
 オブジェクトストレージ (音源本体)
 依頼者
 under_processing = 1 
 内製
 音声処理サービス
 on K8s
 リクエスト
 定期監視
 1件ずつ
 処理依頼


Slide 33

Slide 33 text

© Voicy, Inc. Step 2 の問題点 
 ● Job Handlerが単一障害点 (Single Point of Failure; SPOF)
 ○ 音声処理サーバーのスペックが固定 → 意図せず並列度が上がるとパンクする 
 ○ 並列度をコントロールしやすくするためにひとつのインスタンスにしてしまった 
 
 
 当時の自分の設計ミス 
 処理依頼
 Queue
 Polling
 SPOF
 内製
 音声処理サーバー
 on K8s
 Job Handler (Consumer)
 on K8s


Slide 34

Slide 34 text

Step 3. Autoscaling 


Slide 35

Slide 35 text

© Voicy, Inc. Step 3. Autoscaling 
 ● こちらはまだ本番リリースされていません
 ● (発表までもう1か月あると思ってた...)
 ○ CNDS2025: 5月23日 
 ○ プレイベント: 4月22日 ← 今日 
 注釈


Slide 36

Slide 36 text

© Voicy, Inc. Step 3. Autoscaling 
 ● サーバーのリソース量が一定
 ○ 短時間にたくさんアップロードされると待ち時間が長くなる 
 ○ 仕事がないときでもお金がかかる 
 Pick up: 初期アーキテクチャの問題点 
 バッチサーバー
 (仮想サーバー)
 処理依頼
 内製
 音声処理サーバー
 on K8s


Slide 37

Slide 37 text

© Voicy, Inc. Step 3. Autoscaling 
 ● サーバーのリソース量が一定
 ○ 短時間にたくさんアップロードされると待ち時間が長くなる 
 ● 並列化された
 ○ が、並列度を超える量の重いリクエストが届くとやはり滞る 
 Step 2 で並列処理できるようになった 
 バッチサーバー
 (仮想サーバー)
 処理依頼
 処理依頼
 内製
 音声処理サーバー
 on K8s
 Job Handler (Consumer)
 on K8s
 内製
 音声処理サーバー
 on K8s


Slide 38

Slide 38 text

© Voicy, Inc. Step 3. Autoscaling 
 ● まずはJob Handlerと音声処理サーバーの機能を統合
 ジョブの量に応じて勝手にリソース量を調節してほしい 
 音声処理Consumer on K8s
 Queue
 Polling


Slide 39

Slide 39 text

© Voicy, Inc. Step 3. Autoscaling 
 ● まずはJob Handlerと音声処理サーバーの機能を統合
 ● 次にConsumerを複数立ち上げる
 ジョブの量に応じて勝手にリソース量を調節してほしい 
 音声処理Consumers on K8s
 Queue
 Polling
 ・・・
 処理を冪等にしておけばこれが可能 


Slide 40

Slide 40 text

© Voicy, Inc. Step 3. Autoscaling 
 ● KEDA: Kubernetes Event-driven Autoscaling 
 ○ イベントに応じて水平スケールできるAutoscaler 
 ○ 例: Amazon SQSの場合 
 ■ 最小: 0 pods
 ■ 最大: 5 pods
 ■ 10メッセージ溜まるごとに1 pod増やす 
 ジョブの量に応じて勝手にリソース量を調節してほしい 
 Queue
 Polling
 ・・・
 音声処理Consumers on K8s


Slide 41

Slide 41 text

© Voicy, Inc. Step 3. 終了後のアーキテクチャ 
 ジョブの量に応じてConsumersがAutoscalingされます 
 音声処理Consumers on K8s
 ・・・
 オブジェクトストレージ (音源本体)
 依頼者
 Queue
 リクエスト
 Polling


Slide 42

Slide 42 text

© Voicy, Inc. 比較: 初期のアーキテクチャ 
 ● 特徴
 ○ RDBで処理状況を管理 
 ○ 直列実行
 Cronによる定期実行 
 バッチサーバー
 (仮想サーバー)
 RDB (音源のメタデータ)
 オブジェクトストレージ (音源本体)
 依頼者
 under_processing = 1 
 内製
 音声処理サービス
 on K8s
 リクエスト
 定期監視
 1件ずつ
 処理依頼


Slide 43

Slide 43 text

Step 4. Cluster Autoscaling 


Slide 44

Slide 44 text

© Voicy, Inc. Step 4. Cluster Autoscaling 
 ● VoicyではKubernetesを使っている
 ○ Worker Nodeは仮想サーバー (EC2) 
 ○ → Podだけでなく、Nodeのスケーリングも考慮する必要がある 
 ■ Podのスケールアウトに合わせて Nodeも増えないとリソースが不足する かも
 ■ Podのスケールインに合わせて Nodeも減らないと料金は減らない 
 
 必要な人のみ対象 


Slide 45

Slide 45 text

むすび


Slide 46

Slide 46 text

© Voicy, Inc. むすび
 クラウド環境におけるバッチ処理の設計 
 ● ただクラウドで実行すればいいわけではない
 ● いかにAutoscalingするか 
 ○ → 「パフォーマンス」と「コスト」の両方を改善 
 ● Autoscalingできる状態にするための用意
 ○ 疎結合
 ■ Evnet-driven
 ■ Queueing
 ○ 冪等
 ■ 並列処理
 ■ リトライ


Slide 47

Slide 47 text

音声×テクノロジーでワクワクする社会をつくる 


Slide 48

Slide 48 text

おまけ
 マネージドサービス vs 内製サービス 


Slide 49

Slide 49 text

© Voicy, Inc. マネージドサービス 
 ● ファイル形式の変換
 ● 音量調節 (一部)
 内製サービス 
 ● 音量調節
 ● ノイズ低減
 マネージドサービス vs 内製サービス 
 Voicyの音声処理ではそれぞれどちらを使っているか 


Slide 50

Slide 50 text

© Voicy, Inc. マネージドサービス 
 ● ファイル形式の変換
 ● 音量調節 (一部)
 特徴
 ● 高い信頼性
 ● 低い開発工数
 ● 高い料金
 → お金を使って、簡単高品質
 内製サービス 
 ● 音量調節
 ● ノイズ低減
 特徴
 ● 高いカスタマイズ性
 ● 高い開発工数
 ● 低い料金
 → 労力を使って、お安く高機能
 マネージドサービス vs 内製サービス 
 それぞれの特徴 


Slide 51

Slide 51 text

© Voicy, Inc. ファイル形式の変換
 ● 基本的な機能のため初期から必要だった = 開発工数削減のためにマネージドサービス を使ったのかも
 ● リリース当初は負荷の量が予測できなかっ た
 ● 内製サービスに移行する可能性は? 
 ○ 単調な処理のため、コスト観点から移 行する可能性がある 
 音量調節 & ノイズ低減
 ● 細かいパラメータ調節が必要な処理であり、 内製が理にかなっている 
 ● マネージドサービスに移行する可能性は? 
 ○ 重い計算が必要なため、内製サービ スで信頼性の確保が難しくなったら検 討する価値がある
 ○ AIによる音声処理サービス等がマネー ジドで提供され始めた場合に使いたく なるかもしれない
 マネージドサービス vs 内製サービス 
 意思決定の理由と今後の方針