SYM's Tech Knowledge Index & Creation Records

「INPUT:OUTPUT=1:1以上」を掲げ構築する Tech Knowledge Stack and Index. by SYM@孤軍奮闘する IT Engineer.

マイクロサービスアーキテクチャ メモ

マイクロサービスアーキテクチャ メモ

アーキテクチャは組織に引きづられる(コンウェイの法則)

  • それを解決する術がマイクロサービスアーキテクチャ
  • 分ける粒度はドメイン
  • 他サービスとの連携に、ドメインイベント、イベントソーシング等が活躍すると思われる
  • システムを設計するあらゆる組織がその組織構造に習った構造を持つ設計を生み出す。システム設計が組織を変える(逆コンウェイの法則)

比較

マイクロサービス&モノリス

モノリス

  • 概要
    • モノリシック(一枚岩)なアプリケーション
    • 必要なコンポーネントが全て1つのアプリケーションに含まれるような構成
  • 利点
    • 開発しやすい
    • 大きな変更を加えやすい
    • テストしやすい
    • デプロイしやすい
    • 障害調査しやすい
  • 欠点/課題
    • 時間と共に肥大化、それに伴い…
    • 修正の影響範囲の特定が難しい
    • デプロイまでに時間がかかる(コンパイル等)
    • リリース時の調整コスト大
    • リソース効率が悪い
    • 信頼性が低い(システム全体に影響)
    • 技術スタックが陳腐化する

マイクロサービス

  • 概要
    • アプリケーションの構成要素を独立したサービス群に分割、それらを連携させ1つのアプリケーションとして組み立てる
    • 直さないものを小さく分割しても意味がない
    • 必要な機能を必要なサイズに分割するのが大事
    • 分割はデータごと分割する
  • 利点
    • 修正範囲がサービスに限定される(そのように分割されていれば)
    • CI/CDが必要なテストが容易になる
    • 素早くリリースできる
    • サービスに合わせたスケールができる
    • 障害分離しやすい
    • 新しい技術を試しやすい
  • 欠点/課題
    • サービスの適切な分割範囲を見極めるのが難しい
    • アプリケーションの設計が複雑になる
    • 複数のサービスを同時に修正しないと実現できないような機能の場合、デプロイ/リリースの調整要
    • アプリケーション全体のテストが難しくなる

マイクロサービス & SOA

SOA(Service Oriented Architecture): サービス指向設計

システムの機能単位で決めていく設計(業務分析必須)

|SOA マイクロサービス
アーキテクチャ リソース共有(同じサーバ)|独立したサービス(サーバも複数)
コンポーネント共有 共有する|共有しない
サービス粒度 比較的大きい|非常に小さい
サービス間通信 ESB(Enterprise Service Bus)|メッセージブローカー
通信プロトコル SOAP|REST/gRPC

マイクロサービス化する時の設計面の課題

  • サービス間通信
  • 外部公開
  • アプリ設計
  • データ整合性
  • セキュリティ
  • テスト
  • デプロイ/リリース
  • CI/CDパイプライン
  • サービス正常性確認
  • 可観測性(障害発生時の原因特定/対処)

サービス間通信

連携方法一覧

- 1対1 1対多
同期通信 REST/gRPC 無し
非同期通信 バッチ メッセージブローカー/DB共有
REST
  • 一般的、どの言語でもサポート
  • 1リクエストで複数リソース操作できない
  • バッファリングなしに直接接続するため可用性が下がる
gRPC
  • HTTP/2 を利用したバイナリベースのプロトコル(.protoファイルにIF定義)
  • バイナリ+HTTP/2で高速通信
  • (通信がバイナリのため)監視が難しい
  • 対応していない言語も一部あり(swift)
  • 詳細&実装例:

参考

バッチ
  • 定量の処理を非同期でまとめて実行
  • 頻度を上げると負荷増(実行タイミングは負荷低の時に限定される)
  • 反映までに時間がかかる
メッセージブローカー
  • 非同期にメッセージを交換する仕組み。確実に後続サービスに通知
  • 双方が疎結合になる
  • メッセージのバッファリングができる(瞬間的に大量のアクセスがある場合に有効)
  • (メッセージブローカーで問題が起きると)パフォーマンスのボトルネック/単一障害点になる懸念
  • 運用複雑度の上昇
  • 方式は以下2つ
- 利点 欠点
キュー方式(Pull方式) 処理量が多くても対応可能(=バッチ処理に対応可能) 多少タイムラグが出る
Pub/Sub方式(Push方式) 軽い処理に向く(リアルタイムに近づけられる) Consumer側(Sub側)のリソースを考慮する必要がある
DB共有
  • Viewやテーブルを公開してデータ連携
  • 大量のデータを移す必要がない
  • 反映したデータが即時扱える
  • データ変更を伴う修正の影響範囲が広がる
各々の使い所の尺度
  • データ量: DB共有/ファイル連携 > 非同期API > 同期API
  • 鮮度: DB共有 > 同期API > 非同期API > ファイル連携
課題

サービス分割により、

  • 直列で多段になったきのうは稼働率が下がる
    • 対策:同期処理が不要な機能は切り離す(メッセージブローカーで切り離す)
  • 可用性が下がる(経路上のどこかのサービスが停止するとタイムアウトまで待たされる=ユーザビリティの著しい低下)
    • 対策:一定回数通信に失敗した際に、遮断する仕組みを入れる(サーキットブレーカー)
  • サービスの負荷状況に応じて、インスタンス(サーバ)が動的に生成・破棄される = アクセス先のIPアドレス/ポート番号が分からない
    • 対策:オートスケール+ロードバランサーもありだが、マイクロサービスの場合は接続先を管理する仕組み(サービスディスカバリ)を導入する
サーキットブレイカ

サービス同士が接続しあい、複雑なネットワークを構成している場合に、局所的な障害がシステム全体に波及させない仕組み。 ブレーカー(Breaker)を落とすようにネットワーク上の回路(Circuit)を遮断する

カスケード障害(ネットワークの一部の障害が要因となり,連鎖的に障害がシステム全体へ波及する現象)を起さないための手段

参考

サービスディスカバリ

中央にサービスレジストリというアクセス先をまとめるものを持つ。インスタンス(サーバ) が立ち上がった時にサービスレジストリに自身のIPを登録。クライアントはサービスレジストリに対して、アクセスしたいサービスを元に問い合わせし、IPアドレスをもらってそれでサーバにアクセスする

参考

外部公開

課題

  • アクセス先のエンドポイントが複数になる(どのアドレスを呼び出せばいいか不明)
  • 認証認可をそれぞれ実装すると冗長になる
APIゲートウェイ
  • 入口を一元化

リバースプロキシの役割を担い、リクエストを各APIに振り分ける。公開点を限定するためセキュリティ向上にも繋がる

代表:Kong

データ整合性

課題

  • DBが分散した状態でシステム全体としてデータ整合性を担保する必要がある (2層コミットは通信が同期で可用性が下がるためマイクロサービスではNG)
    • 対策:サーガ を使って担保
  • 複数サービスに分割されたデータを結合する必要がある
    • 対策:API Composition
  • (API Compositionの場合) 1つのサービスで大量データを収集・結合するとオーバーヘッドが高くなる(大量データ読み取り時)
    • 対策:CQRS
サーガ

Sagaパターンについて

API Composition

ある1つのサービスが中心となってデータを収集・結合する。

ただし、分かりやすい反面、以下の欠点有

  • (1サービスに責任集中&結合したデータが必要なサービスが複数あればアクセス集中するため) オーバーヘッドが高くなる
  • (API Compositionを担うサービスがダウンしたら、結合したデータを必要とする他サービスにも影響が出るため) 可用性が下がる
CQRS

コマンドクエリ責任分離(Command & Query Responsibility Segregation)

  • 書き込み側はORM等で従来のやり方
  • 読み込み側はクエリ実行で必要なデータを取れる状態をあらかじめ作っておく(必要なデータをAPI通信/メッセージブローカーを使ってあらかじめ取得/保持しておく)

セキュリティ

  • 認証:人の特定
  • 認可:アクセス制御

OAuth/OpenID Connect:認可の仕組み

OAuthはサービスが持つ各APIにアクセス許可、OpenID Connectはユーザ情報特化のイメージ。

OpenID Connect = OAuth 2.0 + Identity Layer(ID Token + UserInfo API)

OpenID Connect の場合はアクセストークンに加えIDトークン(=JWT)も受け取る

参考:


マイクロサービスの場合

認可サーバを用意して、

  • アクセスがあればAPIゲートウェイがクライアントとなり認可サーバからユーザ情報とスコープを取得
  • ユーザ情報とスコープをHTTPヘッダ等に埋め込み各サービスにアクセス。各サービスは埋め込まれた情報を元に実行してよいかを判断

テスト

  • 単体テスト:サービス内のクラスに対する自動テスト
  • 結合テスト:依存関係のあるサービスのみを結合したテスト
  • 受入テスト:関連サービスすべてを結合したテスト/受け入れテスト
  • E2Eテスト:すべてのサービスを連携させEnd to Endで動作確認

課題

  • モック/スタブが大量に必要
    • 対策:ツールの利用(例:OpenAPI、Mockito、nockなど)
  • テストデータ洗い替えが頻繁に必要
    • コンテナの利用(例:Docker、cri-o、OpenShiftなど)
  • リリース物が増えると手動ではやりきれない
    • CI/CDの導入(Jenkins、CircleCIなど)

参考:

ビルド/デプロイ/リリース

  • ビルド:ソースコードを配布可能/実行可能なファイルに変換
  • デプロイ:システムを利用可能な状態にする
  • リリース:サービスとして公開する

デプロイ方法

デプロイ方法 概要 アーティファクトに含むもの 利点 欠点
アーティファクト配置 資材を物理マシンや仮想マシンへ配置する アプリ/データ アプリ開発チームにとっては単純 インフラ管理が大変
VMイメージ 資材配置が終わった仮想マシンのイメージ作成/展開/起動 アプリ/データ/ミドル/ランタイム/OS インフラがイミュータブルにできる 仮想イメージを使ってサーバを作るのは重い
コンテナ 資材配置が終わったコンテナを作成/起動 アプリ/データ/ミドル/ランタイム OS部分が共通化でき、軽くなる ミドル/ランタイムが新しい物が出ると作り直す必要有
サーバーレス クラウド事業者が提供する環境に直接資材を投入 アプリ/データ 管理範囲を減らせる(クラウド事業者に委譲)
サーバーレス クラウド事業者が提供する環境に直接資材を投入 アプリ/データ 管理範囲を減らせる(クラウド事業者に委譲)

下の物ほど、調整事を減らせ、リリースまでのリードタイムを減らせる

リリース手法

方法 デプロイ先 ダウンタイム 特徴
サービス停止&デプロイ 稼働中サーバ 閉塞期間 ユーザが利用できない時間が発生する
ブルー/グリーンデプロイ 新規サーバ ほぼゼロ サービス停止せず、リリース後は新しい環境を使う。切り戻しが容易
イミュータブルデプロイ 新規サーバ ほぼゼロ 上記と同じだが、元のサーバは削除する。切り戻しする際は、古いバージョンをリリースしなおす
ローリングデプロイ 新規サーバ ゼロ 数台ずつイミュータブルデプロイ

サービスメッシュ

横断的機能(例:サーキットブレ―カー、認証認可、通信暗号化、モニタリング)はサービスとは別に外付け機能として実装 ⇒ サービスメッシュ

マイクロサービスごとにプロキシを配置し、プロキシを経由して他のサービスと通信させることでサービスの依存関係やトラフィックを制御する仕組み

参考:

代表:

コンテナオーケストレーション

課題:

  • サービスが増えたことでインフラ管理する対象も増えるため、増えたものを効率よく管理する必要がある

対策

  • 1パッケージにおさめる(カプセル化)=コンテナ化
  • 複数のコンテナを統合的に管理できるツールを活用(コンテナオーケストレーション=デプロイ制御ができる)

代表

CI/CDパイプライン

目的:リリースの頻度を上げる

CI(Continuous Integration):実装 > コミット > マージ > 自動ビルド > 自動テスト CD(Continuous Delivery): 上記 > デプロイ > リリース

運用/監視

ヘルスチェック

課題:各サービスの正常動作を判断する必要がある (例:GET /health /hc /healthz)

対策:稼働状態を返す専用のAPIを公開する

  • ステータスコードで判定できるようにする
  • レスポンスボディを付ける場合は詳細情報
  • 必要ならDBへアクセスできるかもチェック

可観測性 (Observability)

課題:障害発生箇所の分析や障害原因の特定が難しい

対策:複雑なシステムを横断的に監視できるようにする

手段 概要 補足 ツール
ログ収集 各サービスで発生するイベントの記録 ログは1箇所に収集/集約。ログ送信はサービスが多い場合バッファリングも検討 Elasticsearch Kibana/Fluentd
メトリクス 各サービスの定量数値情報の記録(増減の確認) モニタリングとアラートのために収集。収集自体はログ収集と同じやり方
分散トレーシング サービスが呼ばれた順序やその時の状態を分析/可視化 Trace ID/Span ID/Parent Span IDを保存 Zipkin/Jaeger/AWSX-Ray

利用用途(要件)に合わせたツールを選定

  • KPI分析
  • 監視/アラート etc.

マイクロサービスへの移行

段階的に進めるべき。

ポイント

  • アセスメント
  • 組織/体制の変革
  • 段階的な分割/再構築
  • 共通の仕組み作り

アセスメント

目的をはっきりさせる

  • What:事業の目的や目標は何か
  • 目的や目標の達成手段としてマイクロサービスが適切か

新規事業よりもある程度成熟しドメイン境界がはっきりした事業に向く(モノリシックでは効率が悪くなってきたときにマイクロサービスへの移行を考えるもの)

組織/体制の変革

レビンの変革プロセス

  • 解答:関係者に働きかけ変革の必要性を関係者に理解してもらう
  • 変革:目指す姿の共有/アジャイル体制へ変更(適宜改善)
  • 再凍結:共有化された新たな開発手法の定着

ジョン・コッターのリーダーシップ論に学ぶ『変革時代で生き残るためには?』(変革へ至るための8つプロセス)

段階的な分割/再構築

  • ドメインモデル作成: サービス境界及び優先順を定義するため高レベルのドメインモデル作成
  • 優先順位付け:分解による利益と分解しやすさを軸に決める
  • チーム再編成/システム再編成:分解するサービスに合わせてチーム再編成/チームで必要とするスキル見直し/再教育
  • 効果測定:チェックポイントを設けて定量/定性評価
    • 定量:リリースまでの日数/デプロイ数/障害率
    • 定性:フィードバック

分割方法

アプリ分割

方法 メリット デメリット
プロキシを利用した分割 モノリスに対する修正が減らせる URL単位である程度分割できないと利用できない
I/F定義を利用した分割 複雑化したコードでも分割できる モノリスに対する修正要、リリース調整要
  • プロキシを利用した分割
    • プロキシの挿入(最初は素通り)
    • 新サービス実装(最初はAPIだけの中身がないもの、デプロイプロセスに慣れつつ、徐々に機能実装)
    • カナリアリリース検討
    • 切り替え
  • I/F定義を利用した分割
    • ドメインモデル毎に処理を分割
    • サービス化したい処理をI/F定義(切り替えフラグを差し込んでおく)
    • 新サービス実装
    • 後始末(使わないコードは削除)

データ分割

データベースの分割

物理分割:DBサーバを分割

  • サーバを物理的に分け障害時の影響を下げる
  • DBの種類によってはライセンス費がかかる可能性有り(利用するDBの見直しも検討)

論理分割:DBサーバは共有し、スキーマを分割

  • スキーマを分けることでデータ主管をサービスに紐づけ
  • DBサーバ1つに対して接続元が増えるためコネクション数に注意が必要
分割方法
方法 メリット デメリット
データから分割 データに対して分割に問題がないか確認しやすい マイクロサービスの効果を体感しづらい
アプリから分割 マイクロサービス化の開発プロセスやメリットを体感しやすい 意識的にデータ分割まで行わないと潜在リスクを抱えたままになる
アプリとデータ同時分割 マイクロサービスとしての理想形にいきなり到達可能 修正コード量が増えるためマイクロサービスの効果を体感するまで時間を要する

直感

  • マイクロサービスの恩恵への理解がなければ、アプリから分割
  • 恩恵への理解はあるが慣れがなければ、データから分割
  • 恩恵も慣れもあれば一気に、アプリとデータ同時分割

※プロダクトの特性も考慮して考える必要はあるはず

データから分割
  • スキーマ分割
    • 新しいスキーマへは書き込むだけ
      • 既存には新しいスキーマへの書き込み処理実装
      • このタイミングでデータに整合性の問題がないか等チェック
  • 真とするデータの切り替え(旧スキーマの参照は許容)
    • データ同期や整合性に対する処理が必要になるため実装
      • データ同期:DB共有(Viewの共有)、同期/非同期API、ファイル教諭等検討
      • データ整合性:サーガ、CQRSの利用等検討
  • アプリ分割
    • 新サービスを作成(関連する操作を全て新サービスで行うよう切り替え)
アプリから分割
アプリとデータ同時分割
  • アプリとデータを同時に分割
    • 最初は新スキーマには書き込みのみ
    • スキーマをメインに読み書き実施(これまで通り)
  • 真とするデータの切り替え

共通の仕組み作り

  • DevOpsツールの導入:ソースコード管理、CI/CD、静的解析、単体テスト、メトリクス、分散トレースツールなど
  • 共通機能の導入:APIゲートウェイ、メッセージブローカー、サービスメッシュなど
  • 開発標準の準備:ドキュメント更新ツール、共通利用するツールの使い方、ログ出力のルールなど

ルール/仕組みがある上で初めて自由が許される世界

その他