Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

ReproのActiveJob活用例

Slide 10

Slide 10 text

例: 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ワーカーの限界までしかスケールしない

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

Apache Kafkaとは何か

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

そもそものKafkaの採用理由

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

例: 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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

Event Dataflow Semi-realtime User Segmentation Next flow Kafka Streams

Slide 29

Slide 29 text

参考: その他の手段 ある程度複雑な非同期処理を構築するには他にもいくつか手段がある。 単機能なコンテナ化されたコマンドとワークフローエンジンの組み合わせが最近のオス スメ。 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/

Slide 30

Slide 30 text

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