Railsで実装されている機能のマイクロサービスへの切り離し / split to microservices from rails

C0e58802a4228552548bb8ca596ca966?s=47 Mihyaeru
December 14, 2018

Railsで実装されている機能のマイクロサービスへの切り離し / split to microservices from rails

freee Tech Night #1 「freee on Rails の今とこれからの話」
https://freee-tech-night.connpass.com/event/106851/
にて発表に使った資料です。

C0e58802a4228552548bb8ca596ca966?s=128

Mihyaeru

December 14, 2018
Tweet

Transcript

  1. freee 株式会社 Railsで実装されている機能の マイクロサービスへの切り離し 2018.12.14

  2. 2014年、新卒として五反田の会社へ入社。 いくつかの位置情報ゲームの開発に携わった。 2016年、同じく五反田のfreeeへ入社。 会計freeeの開発を行なっている。 今一番好きな言語はRust。 あるお店のハンバーガーが好きすぎて勝手に注文 システムを作ってる。 @mihyaeru21 ミヒャエル freee株式会社

    2
  3. 3 話す内容 • なぜ切り離すのか • 切り離す境界の選定 • 実装の置き換え • 気をつけたポイント

    • 厳しかったこと
  4. 01 なぜ切り離すのか 4 Section

  5. 5 1つのアプリケーションが大きくなりすぎた • 開発者の増大 • コードの複雑さの増大 • 機能が不用意に密結合になってしまう • デプロイの粒度が大きい

  6. 6 噴出する問題 不用意な密結合 • 規約では治安を維持しきれない • 一度不用意な密結合が生まれると、そ れが前例となりなかなか排除できない • 変更の影響範囲が想像以上に広がる

    ことで開発難易度が上昇 デプロイ巨大化 • 毎日大量のプルリクがマージされる • 1つのアプリケーションを大勢で開発す る限り避けられない
  7. 7 マイクロサービス化を進めることで 不用意な密結合 • 機能が強制的に分離され、仕組み上密 結合が防がれる • 機能の境界が明確に定義され、変更に よる影響が見えやすい デプロイ巨大化

    • 個別にデプロイができる • デプロイの粒度が小さくなる
  8. 8 マイクロサービス化を進めることで • 今回の切り離しを実施した機能は、他のアプリケーションからも利用する

  9. 9 マイクロサービス化にはデメリットもある • 移行自体にかかるコストがかかる • 間にネットワークが入る ◦ 実装方法によってはパフォーマンス問題が発生する ◦ ネットワーク起因のエラー

    • トランザクションを使い整合性を担保できない部分がでてくる ◦ 機能の実装が難しくなる
  10. メリットとデメリットを天秤にかけた結果 やっていく気持ち

  11. 02 切り離す境界の選定 11 Section

  12. 12 切り離し対象機能 ※これらは実際に切り離した機能の関係に似せたダミーです 販売の状態を管理 • 自動車 • バイク • おもちゃ

    • etc マーケティングチャネルの設定 • 販 売 状 態は、あらかじめ設 定したチャ ネルを利用する
  13. 13 リソースに分解して境界を見つける アプリケーション 自動車 バイク おもちゃ 販売状態 チャネル • 機能を構成する要素を関連するいく

    つかのリソースとして分解する • アプリケーションに固 有の要 素なの かを検討する • 固有でないものを切り離し対象とする
  14. 14 リソースに分解して境界を見つける アプリケーション マイクロサービス 自動車 バイク おもちゃ 販売状態 チャネル •

    今回は2つのリソースが切り離し対象 に決定 • DB的には10個程度のテーブルが該 当
  15. 15 この境界でデータの整合性は大丈夫? チャネル • マイクロサービス側のみで作成・更新・削除処理を実行する • マイクロサービスの中でトランザクションを使えばOK

  16. 16 この境界でデータの整合性は大丈夫? 販売状態 • アプリケーション側で自動車販売リソースを作成・削除した時、マイクロサービス側で も販売状態リソースを作成・削除する • 自動車販売リソースの更新と販売状態リソースの更新は、ほぼ独立している • マイクロサービス側の実装を冪等性を持つように実装できれば、厳密なトランザク

    ションでなくともほぼ問題なく状態を同期できる
  17. この境界で分離しても大丈夫そう

  18. 18 マイクロサービス側のインタフェース Protocol Buffersで作成 • 先行していたマイクロサービスでの機能開発でgRPCを利用しており、内部 的な通信にはgPRCを使っていくことが既定路線 • 切り離す対象のリソースを操作するユースケースを元にRPCを洗い出す •

    実装していくうえで足りないものは見えてくるので、この時点では大雑把に
  19. 19 マイクロサービス側のインタフェース インタフェースの整理を決意 • 移行前のコードはActiveRecordで前提になっている • ロジックをそのままコピペして切り離すだけだとサービスとしての使い勝手が悪い • 独立したサービスとしてのあるべきインタフェースが必要

  20. 03 実装の置き換え 20 Section

  21. アプリケーション 21 切り離し前のイメージ モデル DB コントローラ

  22. マイクロサービス 22 切り離し後のイメージ gRPCサービス アプリケーション モデル DB コントローラ ユースケース DB

  23. 23 どうやる? • マイクロサービスを作ってからアプリケーション側を入れ替える? • アプリケーション 側 で 新 しいインタフェースを

    満 たすようにリファクタリングしてか ら、裏側をマイクロサービスに入れ替える? • データはどこに持つ? • 入れ替えるときは全部一気に?一部ずつ? • ……
  24. 実際に行なった手順

  25. アプリケーション 25 初期状態 モデル DB コントローラ

  26. マイクロサービス 26 マイクロサービス側を実装 gRPCサービス アプリケーション モデル DB コントローラ

  27. アプリケーション マイクロサービス 27 ユースケースを実装 gRPCサービス モデル DB コントローラ ユースケース

  28. アプリケーション マイクロサービス 28 ユースケースで2つの実装を切り替え gRPCサービス モデル DB コントローラ ユースケース 実装を

    切り替え
  29. アプリケーション マイクロサービス 29 旧ロジックを使用する実装を削除 gRPCサービス モデル DB コントローラ ユースケース

  30. マイクロサービス 30 DBを分離 gRPCサービス アプリケーション モデル DB コントローラ ユースケース DB

  31. マイクロサービス 31 完了! gRPCサービス アプリケーション モデル DB コントローラ ユースケース DB

  32. 04 気をつけたポイント 32 Section

  33. 33 少しずつ置き換えていく 1. 最も独立しているチャネルの読み込みコントローラのみ 2. チャネルの書き込みコントローラまで拡大 3. 販売状態の読み込みを伴う自動車販売のコントローラのみ 4. 販売状態の書き込みを伴う自動車販売のコントローラまで拡大

    5. バイク販売、おもちゃ販売のコントローラまで拡大 6. その他のリソースに関するコントローラまで拡大 7. コントローラ単位ではないモデルレベルでの参照を置き換え 8. 古い実装を削除
  34. 34 リソースを操作するクラス群を導入 • マイクロサービス側のリソースに関わる操 作に対 する責務を負う ◦ マイクロサービス 側 のユースケースと、アプリ

    ケーション側のユースケースの間を埋める • マイクロサービス側、アプリケーション側の実装を 切り替える役目も兼ねる • リソースの数 x 操作の種類 の掛け算でたくさん マイクロサービス gRPCサービス アプリケーション モデル DB コントローラ ユースケース DB
  35. 35 リソースのアダプタ+α • マイクロサービスから取 得したリソースそのままで はちょっと困る ◦ JSONにシリアライズする部分 ◦ マイクロサービス側リソースではidしか持ってい

    ないデータ ▪ ユーザの名前など • アダプタ兼、追加のデータを効率良く取得するため にラッパー マイクロサービス gRPCサービス アプリケーション モデル DB コントローラ ユースケース DB
  36. 05 厳しかったこと 36 Section

  37. 37 ActiveRecord系Gemの置き換え • マイクロサービスはGolangで実装した • ActiveRecordで実装されたGemを切り離し対象モデルで使用していた • 幸い、必要な機能はGemで提供されているうちの一握りだった • 必要な機能のみをGolangで実装した

  38. 38 モデルのvalidationが難しい • 自動車販売やおもちゃ販売を表現するモデルの validationが販売状態の状態に一部依存していた • 該当するvalidationはユースケースのクラス経由で 行うようにして対応した • その

    他 は、マイクロサービス 側 、アプリケーション 側でそれぞれvalidationを実行する アプリケーション マイクロサービス 自動車販売 バイク販売 おもちゃ販売 販売状態 チャネル アプリケーション マイクロサービス gRPCサービス モデル DB コントローラ ユースケース 実装を 切り替え
  39. 39 事故 • マイクロサービス側が古い状態なのにアプリケーション側を新しくしてしまい、呼び出 そうとしたRPCが存在せず…… • リリースの回数が多すぎて混乱してた • 小出しにした事自体は良かったはず •

    マイクロサービス側に処理を切り替えるタイミングで、マイクロサービスのバージョン をチェックするようにした
  40. 実はまだ移行完了していません

  41. 41 まだ実装切り替え状態 • コントローラ単位での移行は全て完了済み • モデルの関連経由で呼び出されている部分が少し 残ってる • 意外な部分で関連が見つかると駆逐完了が遠のく アプリケーション

    マイクロサービス gRPCサービス モデル DB コントローラ ユースケース 実装を 切り替え
  42. まとめ 42

  43. 43 まとめ • マイクロサービス化のメリットとデメリットを検討 • リソース単位で分離の境界を選定した • 切り戻しを意識しながら小さな単位で段階的に移行した • アプリケーション側のユースケースに該当するクラスを作成した

    • 移行の方法はいろいろあるはずなので懇親会でお話しましょう!