Slide 1

Slide 1 text

ステートフルなアプリケーション のダウンタイムを 10 秒以下 にすることを目指 して © 2021 TIS Inc. 1 / 39

Slide 2

Slide 2 text

私 は何者︖ 根来 和輝 Negoro Kazuki TIS 株式会社 テクノロジー&エンジニアリングセンター ミッション ⾼可 用システムを安価 なインフラで 課外活 動 Akka 実践 バイブル執筆 Scala Matsuri 登壇 @negokaz negokaz © 2021 TIS Inc. 2 / 39

Slide 3

Slide 3 text

Lerna Stack ⾼可 用性 と⾼ スループットを実現 するソフトウェアスタック ⾃⼰修復可能 なアーキテクチャにより高可 用性 を実現 Event Sourcing を用いたロックフリーな永 続化 方式により 高スループットを実現 © 2021 TIS Inc. 3 / 39

Slide 4

Slide 4 text

稼働率 99.9999% を実現 できるか︖ キャッシュレス決済などミッションクリティカルな業務 で 要求 される可 用性 許 容できる年間停 止時間は 31.5 秒 従来 、専 用のハードウェアを搭載 した高価なサーバー を用意 して実現 してきた稼働 率 © 2021 TIS Inc. 4 / 39

Slide 5

Slide 5 text

稼働率 99.9999% を実現 できるか︖ キャッシュレス決済などミッションクリティカルな業務 で 要求 される可 用性 許 容できる年間停 止時間は 31.5 秒 従来 、専 用のハードウェアを搭載 した高価なサーバー を用意 して実現 してきた稼働 率 当 時の Lerna では難 しいことが明 らかに © 2021 TIS Inc. 5 / 39

Slide 6

Slide 6 text

Lerna Stack が直⾯ した稼働率 の限界 レイヤー 障害 種別 ダウンタイム ネットワーク ネットワーク分断 ~20s Keepalived ノード停 止 ~ 5s HAProxy ノード停 止 ~ 5s アプリ(Akka) ノード停 止 ~15s Cassandra ノード停 止 0s MariaDB(Galera) ノード停 止 ~ 2s © 2021 TIS Inc. 6 / 39

Slide 7

Slide 7 text

Lerna Stack が直⾯ した稼働率 の限界 レイヤー 障害 種別 ダウンタイム ネットワーク ネットワーク分断 ~20s Keepalived ノード停 止 ~ 5s HAProxy ノード停 止 ~ 5s アプリ(Akka) ノード停 止 ~15s Cassandra ノード停 止 0s MariaDB(Galera) ノード停 止 ~ 2s © 2021 TIS Inc. 稼働 率 99.999% (年間 5.26 分以 下の停 止)が限界 7 / 39

Slide 8

Slide 8 text

アプリの MTTR を10 秒以下 にしたい ただし、ターゲットとする業務領域 では 強整 合性 が求 められる 結 果整 合性 で問題なければ Akka の Replicated Event Sourcing という選択肢 がある © 2021 TIS Inc. MTTR: mean time to recovery(平均復旧 時間) Replicated Event Sourcing • Akka Documentation 8 / 39

Slide 9

Slide 9 text

ダウンタイムの原因 は何なのか︖ 強整 合性 を保障 しながらスケーラブルな Event Sourcing を 実現 するために Lerna Stack では Akka Persistence Akka Cluster Sharding を組 み合わせて使う © 2021 TIS Inc. 9 / 39

Slide 10

Slide 10 text

ダウンタイムの原因 は何なのか︖ 強整 合性 を保障 しながらスケーラブルな Event Sourcing を 実現 するために Lerna Stack では Akka Persistence Akka Cluster Sharding を組 み合わせて使う この組 み合わせでネットワーク分断障害 の フェイルオーバーを考える © 2021 TIS Inc. 10 / 39

Slide 11

Slide 11 text

ネットワーク分断障害 の解 決方法 Split Brain Resolver を使う それぞれの側 が到 達できないノードを確認し、 停⽌ する と判 定したほうが自らをシャットダウンする © 2021 TIS Inc. これは keep-majority の例 (多 数派を維 持する) 11 / 39

Slide 12

Slide 12 text

フェイルオーバーまでの流 れ Akka Cluster Sharding がフェイルオーバーするまでの流 れ (Split Brain Resolver を利用した場合) © 2021 TIS Inc. 12 / 39

Slide 13

Slide 13 text

フェイルオーバーまでの流 れ Akka Cluster Sharding がフェイルオーバーするまでの流 れ (Split Brain Resolver を利用した場合) © 2021 TIS Inc. 13 / 39

Slide 14

Slide 14 text

さらに伸 びる合意形成 のための時間 合意形成 のための時間はノード数に合わせて ⻑ くとることが推奨 されている cluster size stable-after + down-removal- margin 5 14s 10 20s 50 26s 100 40s © 2021 TIS Inc. Split Brain Resolver • Akka Documentation 14 / 39

Slide 15

Slide 15 text

Split Brain Resolver がなぜ必要 か 強整 合性 (一貫性 )を維持 するため ネットワーク分断 時に無条件 で Entity を回復 させると、 同 じ Entity が複 数のノードに存在 する状態 になり 一貫性 が損 なわれる可 能性 がある © 2021 TIS Inc. 15 / 39

Slide 16

Slide 16 text

Split Brain Resolver がなぜ必要 か ステートフルな「口座 A」の Entity が 2 箇所 で起 動すると… © 2021 TIS Inc. 16 / 39

Slide 17

Slide 17 text

ネットワーク分断 とノード停⽌ ネットワーク分断 とノード停⽌ は区別 できない ノード同⼠ は定期的 に相互 にヘルスチェックを行って 応 答があればノードが生きているとみなす © 2021 TIS Inc. 17 / 39

Slide 18

Slide 18 text

ネットワーク分断 とノード停⽌ ネットワーク分断 とノード停⽌ は区別 できない ノード同⼠ は定期的 に相互 にヘルスチェックを行って 応 答があればノードが生きているとみなす 応 答がないときは ノードが停 止しているのでしょうか? ネットワークが断線 しているのでしょうか? © 2021 TIS Inc. 18 / 39

Slide 19

Slide 19 text

ネットワーク分断 とノード停⽌ ネットワーク分断 とノード停⽌ は区別 できない ノード同⼠ は定期的 に相互 にヘルスチェックを行って 応 答があればノードが生きているとみなす 応 答がないときは ノードが停 止しているのでしょうか? ネットワークが断線 しているのでしょうか? ノード停 止であってもフェイルオーバーに ネットワーク分断 と同 じ時間がかかる © 2021 TIS Inc. 19 / 39

Slide 20

Slide 20 text

ダウンタイムを減 らすには 一貫性 の保証 を Split Brain Resolver に頼らない Entity を複 製しておきリカバリ時の IO を不 要に © 2021 TIS Inc. 20 / 39

Slide 21

Slide 21 text

どうやってそれを実現 するのか︖ 分散 合意 アルゴリズム Raft 一貫性 のあるステートマシンの複 製を実現 するアルゴリズム © 2021 TIS Inc. 21 / 39

Slide 22

Slide 22 text

どうやってそれを実現 するのか︖ 一貫性 の保証 を Split Brain Resolver に頼らない © 2021 TIS Inc. 22 / 39

Slide 23

Slide 23 text

どうやってそれを実現 するのか︖ 一貫性 の保証 を Split Brain Resolver に頼らない Raft が複数 ノードに複製 された Entity の一貫性 を保証 © 2021 TIS Inc. 23 / 39

Slide 24

Slide 24 text

どうやってそれを実現 するのか︖ 一貫性 の保証 を Split Brain Resolver に頼らない Raft が複数 ノードに複製 された Entity の一貫性 を保証 Entity を複 製しておきリカバリ時の IO を不 要に © 2021 TIS Inc. 24 / 39

Slide 25

Slide 25 text

どうやってそれを実現 するのか︖ 一貫性 の保証 を Split Brain Resolver に頼らない Raft が複数 ノードに複製 された Entity の一貫性 を保証 Entity を複 製しておきリカバリ時の IO を不 要に 常に複製 が存在 しているためリカバリ時の IO は不要 © 2021 TIS Inc. 25 / 39

Slide 26

Slide 26 text

どうやってそれを実現 するのか︖ 一貫性 の保証 を Split Brain Resolver に頼らない Raft が複数 ノードに複製 された Entity の一貫性 を保証 Entity を複 製しておきリカバリ時の IO を不 要に 常に複製 が存在 しているためリカバリ時の IO は不要 Raft によりフェイルオーバー時の 障害 検知より後の時間を全て削 減できる © 2021 TIS Inc. 26 / 39

Slide 27

Slide 27 text

Raft の適 用効 果 - フェイルオーバー時 © 2021 TIS Inc. 27 / 39

Slide 28

Slide 28 text

私 たちが目指 したもの ダウンタイムが 10 秒以下 強整 合性 が保障 される スケーラビリティがある 学習 コストが低 い(Akka ユーザーにとって) © 2021 TIS Inc. 28 / 39

Slide 29

Slide 29 text

akka-entity-replication © 2021 TIS Inc. カードは GitHub Link Card Creator で生成 しました 29 / 39

Slide 30

Slide 30 text

アーキテクチャ © 2021 TIS Inc. Cluster Sharding と似 た Actor の階層 スケールアウト時は Shard が移 動 Entity のイベント列 を Shard が Raft で同期 30 / 39

Slide 31

Slide 31 text

ダウンタイムを計測 してみた 目標のダウンタイム10 秒以下 を達成 できたのか? © 2021 TIS Inc. 31 / 39

Slide 32

Slide 32 text

ダウンタイムを計測 してみた 目標のダウンタイム10 秒以下 を達成 できたのか? 達成 !!! 🎉 レイヤー 障害 種別 ダウンタイム ネットワーク ネットワーク分断 8.0s アプリ(Akka) ノード停 止 6.0s © 2021 TIS Inc. 32 / 39

Slide 33

Slide 33 text

デモ - 環境 の構成 https://github.com/negokaz/sample-akka-entity-replication © 2021 TIS Inc. 33 / 39

Slide 34

Slide 34 text

API Akka の既存 ユーザーが実装 しやすいよう Akka Classic Persistence と似 た API を提 供 class CountActor extends ReplicationActor[Int] { private[this] var count: Int = 0 ... // 複製の中で代表者だけが実行する override def receiveCommand: Receive = { case CountUp(_, amount) => replicate(Incremented(amount)) { event => count = count + event.amount } // 代表者以外の複製が実行する // ただし、代表者においても起動直後は状態を復元するために呼ばれる override def receiveReplica: Receive = { case Incremented(amount) => count = count + amount ... } } © 2021 TIS Inc. 34 / 39

Slide 35

Slide 35 text

API Raft のログコンパクションのため Actor の状態 は スナップショットとして自動的 に永 続化 される class CountActor extends ReplicationActor[Int] { private[this] var count: Int = 0 // 現在の"状態"を返す override def currentState: Int = count override def receiveCommand: Receive = { case CountUp(_, amount) => replicate(Incremented(amount)) { event => count = count + event.amount } override def receiveReplica: Receive = { case Incremented(amount) => count = count + amount // Actor の復元時、最後に保存した "currentState" を最初に適用 case SnapshotOffer(snapshot: Int) => count = snapshot } } © 2021 TIS Inc. 35 / 39

Slide 36

Slide 36 text

Akka 公 式プロダクトとの⽐較 一貫性 リカバリタイム リソース消費 Replicated Event Sourcing 結 果整 合性 0s - Akka Cluster Sharding 強整 合性 19s 以 上 ※1 - Akka Entity Replication 強整 合性 10s 以下 大 © 2021 TIS Inc. ※1: Akka 公 式の推奨値 で構成 した場合 36 / 39

Slide 37

Slide 37 text

今後 の予 定 3⽉末 に GA 版 (v1.0.0 )リリース 🚀 © 2021 TIS Inc. 37 / 39

Slide 38

Slide 38 text

ワンクリックでできる コントリビュートがあります © 2021 TIS Inc. 38 / 39

Slide 39

Slide 39 text

仲間を探しています! Akka Cluster を用いたシステムの開発 OSS ライブラリの開発 分散システムに関わる課題解決 興味ある方はお気軽にお問い合わせください! @negokaz © 2021 TIS Inc. 39 / 39