Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Sidekiq to Kafka ストリームベースのmicro services

Sidekiq to Kafka ストリームベースのmicro services

Kaigi On Rails 2020 発表資料。

ActiveJob(sidekiq)の限界とKafkaを採用してサービス分割する上での戦い方の例について紹介する。

Tomohiro Hashidate

October 03, 2020
Tweet

More Decks by Tomohiro Hashidate

Other Decks in Programming

Transcript

  1. Sidekiq to kafka
    Streamベースの
    micro services
    Repro inc. @joker1007
    Kaigi on rails 2020

    View Slide

  2. 自己紹介
    Repro inc. CTO
    Tomohiro Hashidate a.k.a @joker1007
    asakusa.rb
    最近はKafkaとKafka Streamsを用いて
    セミリアルタイムストリーミングシステムを
    構築している。
    その他、satisfactoryで工場作ったり。
    気付いたんですが、マジ仕事と変わらんよねこれ。

    View Slide

  3. 所属
    Marketing Solution Companyとして
    Customer Engagement Platformを提供しています。
    大規模なデータをすぐに活用可能にするためのフローが非常に重要なサービス。

    View Slide

  4. アジェンダ
    ● Reproの様なサービスとRailsが想定するWebサービスの違い
    ● Railsでは非同期のサービスをどう扱うか
    ● Railsのエコシステムの限界
    ● イベントストリームによるmicro services化
    ● Kafkaを基盤としたアーキテクチャの例

    View Slide

  5. Reproの特徴
    ● データ量が多い (秒間5万件ぐらいの書き込みが発生する)
    ● エンドユーザーは通信自体を意識しない
    ● サービス利用者のアクションもすぐに結果は出ない
    ○ 数百万ユーザーにpushメッセージを配信する
    ○ アプリケーション内にキャンペーン情報を配信し結果を待つ
    ○ 特定条件に合致するユーザー集合を exportする
    ● 集計処理が多い
    ○ 日に数千万ユーザー * 100ぐらいの集計結果が必要
    ○ 出来る限り短時間で結果を出す
    つまり、同期的に結果を返すことが重要ではない大規模なデータを扱う。
    非同期処理の重要度が高い。

    View Slide

  6. Railsにおける非同期処理
    ほとんどのケースで最初に検討されるのはActiveJob (恐らくsidekiq backend)。
    しかしActiveJobは割と偏った非同期処理の実行手段であり、基本的なユースケースか
    ら外れるとしばしば問題になる。
    ActiveJobもRailsが用意したOpinionativeなレールであって、どういった特徴を持ってい
    るのかを理解する必要がある。

    View Slide

  7. ActiveJobの特徴
    ● モデルのメソッドを呼び出すことを非同期化することに特化している。
    ● キューイングの管理を隠蔽している。
    ○ 基本的にキャンセル機構が無い。
    ● スケジューラは簡易的である。
    ○ 再帰的なスケジュールを設定できない
    ● 複数のgemの最大公約数となるインターフェースである
    ○ Backend固有の機能については考慮しない
    端的に言って、ユーザーからのWebリクエストを受けて、多少時間がかかる処理(数
    秒〜数分)の結果を待たずにレスポンスを返すのが、主な用途である。

    View Slide

  8. ActiveJobの限界
    ● 定期実行が面倒
    ● コードベースが密結合する
    ○ ファットモデルと結合すると分離が困難
    ● 1台で処理できるジョブを越えると非常に面倒
    ○ 長時間実行する処理だとリトライ粒度がコントロールできない
    ○ redisがスケール限界になる (通常ここまでは早々到達しないけど )
    ● 複数段階にジョブが分かれている場合、依存関係が分かりにくい
    一定以上複雑化すると対応に困る要素が多くなる。特にActiveJobはRailsの中でも限
    界が早くやってくる。
    選択する際には、かなり簡易的な非同期ジョブ実行基盤であることを意識しなければな
    らない。
    ぶっちゃけ個人的には生でbackend使う方が…

    View Slide

  9. ReproのActiveJob活用例

    View Slide

  10. 例: Push Message配信
    Push Campaign (Rails)
    Push sender (Go)
    Sidekiq
    CronoTrigger (Scheduler)
    1. Fetch user informations
    2. Store user informations
    Amazon
    Aurora
    SQS
    3. Queue execution
    別のActiveJob
    4. Poll execution
    5. Fetch user informations
    6. External Service
    7. Feedback results
    パッと見でややこしい。
    工程毎の責任分離が出来ていない。
    Redisとsidekiqワーカーの限界までしかスケールしない

    View Slide

  11. やりたいこと
    ● 工程毎に責任範囲を明確にしたい
    ○ サービス分割も視野
    ○ 言語非依存なメッセージング
    ● スケーラビリティの確保 (特に集計のスケーラビリティ)
    ○ 一台のノードだけに処理が閉じない様にする
    ○ データの入口から保持する場所まで全てを分散可能にしないと結局データストアがシングルポイン
    トになる
    ○ 数分で数千万件〜数億件がキューイングされることを想定する
    ● 耐久性の確保
    ○ sidekiqとredisは最悪の場合キューがロストする。 (proは解決可能らしい)
    ○ SQSでも解決可能だけど shoryukenはコードベースが読み辛い ……。

    View Slide

  12. Apache Kafkaを利用した
    producer/consumerモデルへ

    View Slide

  13. Apache Kafkaとは何か

    View Slide

  14. Apache Kafkaとは
    Kafkaは公式ドキュメントによるとDistributed
    Streaming Platformと呼ばれている。
    ● レコードのストリームをpub/subする
    ○ キューとは似て非なるもの
    ● 対障害性を持った方法でレコードを永続
    化する.
    ● パーティショニングされたトピックという
    単位でレコードを保持する
    ○ パーティション毎にデータの時系列は保証さ
    れる
    from
    https://kafka.apache.org/intro

    View Slide

  15. キューとの相違点
    ● Subscribers (Consumers) はレコードを削除(消費)しない
    ● SubscribersはKafka Brokerに保持されているoffsetの値を基に取得するレコード
    の位置を決める。
    ● Publishers (Producers) がパーティショニングの責任を持つ
    ○ Kafka brokerは到着したレコードをパーティショニングするロジックを持っていない
    ○ パーティショニングのアルゴリズムはクライアントライブラリに実装されていて、各々のクライアント
    実装によって異なる
    ● 複数のワーカーが同じレコードを処理しない様にするためにConsumerGroupと
    いう概念を用いる。
    from

    View Slide

  16. Kafkaの設計方針
    Kafkaはデータストレージでもある.
    Kafkaは受け取ったデータを即時ディスクに書き込み他のノードにレプリケーションする。
    この時、OSのファイルシステムをそのまま利用して単純にファイルとして書き込みを行
    う。
    つまり、Kafka BrokerのパフォーマンスはファイルシステムのスループットやOSのペー
    ジキャッシュサイズに大きく依存する.
    そのため、高いディスク性能や大容量のメモリが必要になる。

    View Slide

  17. そもそものKafkaの採用理由

    View Slide

  18. 集計のスケーラビリティ確保
    ● 数千万以上のユーザー毎に起きたイベントを集計する必要があった
    ● バッチだとHadoop等を使っても1時間に1度ぐらいしか更新できない
    ● それを1分から2分で活用可能にするためにkafkaを採用した
    ○ ユーザーIDごとにパーティショニングし、 100台以上に分散させて一貫性のある集計を 1分程度で行
    う集計基盤を実装した
    こういった理由からKafkaが既に利用されている状態で、先に述べた様なアプリケーショ
    ンの構造上の課題を解決する上でも活用できそうだと判断した。

    View Slide

  19. Kafkaを利用した
    producer/consumerモデルで
    得られるもの

    View Slide

  20. Kafkaを利用したproducer/consumerモデルの利点
    ● Avroを利用したスキーマフルで言語非依存なメッセージ伝達
    ○ 任意の言語で独立したサービスを実装可能
    ○ Kafka関連ツールの中にSchema Registryがありスキーマ管理がしやすい
    ● producer, broker, consumer全てが分散処理可能
    ○ 適切な仕事の切り分けは必要
    ● メッセージはストレージに永続化され、レプリカによって保護される
    ○ 必要であればidempotentなキューイングやトランザクションも実装できる
    ● キューでは無いため、一つのメッセージを元に複数のサービスを起動できる
    ○ 各サービスはメッセージの取得先とスキーマが分かれば良い
    ○ Avroのスキーママイグレーションと組み合わせると、サービス追加時に必要な変更を最小限に出来

    View Slide

  21. 例: Push Message配信の改善
    Push Campaign (Rails)
    Push sender (Go)
    CronoTrigger (Scheduler)
    Amazon
    Aurora
    Return queueing
    Control the pace of delivery
    6. External Service
    Queueing Service
    Fetch user informations
    Delivery Scheduler
    Send event
    それぞれのサービスは任意の言語で実装可能
    一つ一つの工程で完結しておりお互いを知る必要は無い
    それぞれの仕事の結果をお互いが知る必要も無い
    任意の箇所からリトライ可能 (但しストリームとして )
    Fetch targets
    Send event

    View Slide

  22. ストリームベースの非同期処理の考え方
    ● 出来る限り他サービスの結果を待たない
    ● 処理を羃等にし、任意の箇所から処理の継続を可能にする
    ○ 特定の個別の処理ではなくデータの流れとして扱う
    ● 終端のサービスは結果をデータストアに格納し、フロント側のサービスはそれを参
    照するだけ
    ○ CQRSの考え方に近い
    ● 命令を送るというよりイベントを送る
    ただReproのケースでは、イベントをベースにサービスを起動しているが所謂イベント
    ソーシングとは考え方が異なる。

    View Slide

  23. 参考: CQRSとは
    Command and Query Responsibility Segregationの略。
    副作用を伴うDomain Specificな処理をCommand、参照をQueryとしてそれぞれ独立し
    たデータモデルを利用するアーキテクチャを指す。
    同一のサービス内で分離することもあるし、サービスレベルで分割されているケースもあ
    る。
    ワークロードの違いや複雑さを分離したり、個別にスケーラビリティをコントロールするこ
    とが主な目的。
    一方で、状態を同期的に収束させるのが困難になるので、処理フローの工程数は増え
    るし、システム構成そのものは複雑化する。

    View Slide

  24. 参考: イベントソーシングとは
    データモデルを更新するのではなく、不変のイベントを追記していくことで現在の状況を
    復元する。
    トランザクションが必要な領域を限定してスケーラビリティをコントロールしたり、頑健な
    データモデルの構築や履歴を残すことを容易にする等を目的とする。
    イベント履歴から状態を復元するのは負荷がかかるので、通常は参照用途のビューの
    構築とセットで行う必要がある。

    View Slide

  25. イベントソーシングとは異なる点
    Reproの業務領域として、エンドユーザーの行動ログそのものをイベントとして取り扱っ
    ている。
    そして複数の機能を連携させる中核としてイベントの集計ストリームが存在する。
    Reproサービス内部で発生したエンドユーザー毎のアクションもインターナルなイベント
    としてKafkaに送り込んでおくと、同一の基盤を活用してサービス間連携の基盤として活
    用できる。
    変更を伴うデータモデルの変遷をイベントとして記録しているのではないし、その状態復
    元を目的としている訳でもない。

    View Slide

  26. RubyからKafkaに送信する
    クライアントライブラリ
    ● ruby-kafka
    ● rdkafka-ruby (C extension)
    ● Waterdrop (wrapper of rdkafka)
    シリアライズ
    ● avro_turf (Schema Registryに対応したavroシリアライズを行う)
    fluentd
    ● fluent-plugin-kafka, fluent-plugin-avro_turf
    フレームワーク
    ● karafka

    View Slide

  27. Storage Layer
    User Identification
    Reproのイベントストリーム概要
    Event Acception (API)
    User Identify
    User Profile
    Persistence
    Event Tracking API
    Event Acception (Amazon S3 -> SQS)
    Event Aggregation
    Event with property Counting
    Data Arrangement
    リアルタイムのイベントの集計、ユーザープロフィールの
    保存等を行いストレージレイヤーに保存している
    Compatibility Layer
    Native and Web SDK API
    Kafka Streams
    User Activities
    User Activities

    View Slide

  28. Event Dataflow
    Semi-realtime User Segmentation
    Next flow
    Kafka Streams

    View Slide

  29. 参考: その他の手段
    ある程度複雑な非同期処理を構築するには他にもいくつか手段がある。
    単機能なコンテナ化されたコマンドとワークフローエンジンの組み合わせが最近のオス
    スメ。
    AWSだとStep FunctionsとFargate (or AWS Batch)を組み合わせるとスケーラビリティ
    を確保しやすい。(最近採用しようとしている)
    GCPだとGCP Workflow辺りを活用できそう。
    kafkaとRailsを組み合わせる利点についてはこの記事が参考になる。
    https://mensfeld.pl/2017/11/kafka-on-rails-using-kafka-with-ruby-on-rails-part-1-kaf
    ka-basics-and-its-advantages/

    View Slide

  30. This talk is presented by
    We are hiring developers. Please remember us!

    View Slide