マイクロサービス
信頼性、性能、メンテナンス性
おそうじガイド
リソース/費用の計測
定量化して未知を計測する
未知であるということがレガシーを加速する
スコープ/タスク/リスクの分解
小さいところから
ステークホルダー/開発メンバーとの合意
「なんのためにやるか」を繰り返し伝える
開発上のメリット、事業上のメリット
Feature toggles pattern
Circuit breaker pattern
Release
新機能をユーザに届ける
既存機能の修正
既存機能の削除
内部実装のリファクタリング
リリースは難しい
もちろんテスト
でも、テストも間違うことはあるし、テスト自体がコストになる
テストの利益の話
安心してリリースするための取り組み
手動テストを自明に
CIが専用のクラスタ環境にPRをデプロイしてくれる
人間によるテストをアウトソース
毎週末テストを委託している
残った課題
でかい変更を毎週リリースしたいが、どのコミットで壊れたかをどうやって特定するか
長期間いじってるブランチのマージや作業がつらい
ロールバックがむずい
1週間分全部をロールバック、ではなく、特定のコミットをrevertしたい
いつでもリリース
リリースの粒度
Big bang release 0% -> 100%
0% -> 20% -> 30% -> ... -> 90% -> 100%
小さい機能に分割して徐々にリリース
既存機能が破壊されたかどうかが検知しやすそう
一部のユーザにだけリリースしていく
いわゆる段階解放
ロールバックが容易
コードの修正はいらない
最小で動作するプロダクトが優勝
機能は簡素だが動くものを出していく
user * featureのgradual release
新しい問題も現れた
新旧コードを管理する複雑性
feature toggle
quipperではdarklaunchと呼ぶ
切り替え部分がボイラープレート化されたものに見える
ロジックの部分はエンジニアが新旧を把握してかかないといけなさそう?
darklaunchはビジネスロジックを知らないようにする
よくわかっていない
ロールバックの典型的なタスク
新しいコードで例外が発生したら自動でUndarklaunchする
Circuit Breaker
これで残りの問題も解決
動画を見た
https://www.youtube.com/watch?v=VFmLqWuGtPk
Webアプリ開発ではreleaseが最も重たい
本番にコードを入れることがリリースではなく、ユーザに価値を届けるのがリリース
本番環境でしか本番ぽい挙動は再現しない
機能ごとに分割、ユーザごとに分割
フィーチャートグルでデプロイとリリースを分離できる
新たに問題も出る
旧/新コードが交じる
解決方法: まぁええんちゃう?
モジュールにしてメソッドにする
ロールバックの頻度が増えて面倒
ダークローンチの管理画面を作って簡単にした
例外が起きたら自己修復→サーキットブレーカー
マイクロサービス化のためにサービス境界を決めるのはめっちゃむずい
マイクロサービスのインターフェースの変更もめっちゃむずい
書籍を買ったのでとりあえず目を通してから読む
マイクロサービスとは
マイクロサービスはさまざまな分野の技術や方法論を組み合わせることで成り立っており、これらが総合的に実践できなければその価値を享受できない。
公開APIを持ち、その呼び出し元に影響を与えずに単独で変更・本番リリースができる
簡単に捨てたり、作り直したりできる
進化的アーキテクト
マイクロサービス同士がどのように連携するか、などのシステム全体をアーキテクトが見る
各マイクロサービスで定めるべき共通の標準はある
冠詞、API、障害対応
ビジネス要求から本番運用まで自分のチームでやる
サービスのモデル化方法
まずはモノリシックに作って、ドメインの理解が進むに連れて徐々にマイクロサービスに分離していく
コンテキスト境界を分離するときは、データの観点ではなく、そのコンテキストが外部に公開する振る舞いに注目
イベントベース
rabbitmqとかのキューのサービスはこれを実現するために使われる?
ユニークな相関IDを持って複数サービスをトレースする
モノリスの分割
境界づけられたコンテキストで分割する
影響の小さい機能から徐々にマイクロサービスに分割していく
データベースの分割は段階的に、その後にサービスを分解
インフラ
インフラ/ミドルウェアも自動構成できるようにする
CI/CDで構成、稼働するサーバは手作業で変更してはいけない
サービスとサーバのマッピング
複数のサービスを1ノードに同居させてはならない
セキュリティ
シングルサインオンの仕組みが必要
ユーザインターフェースとサービスの間に入って認証・認可を透過的に行うゲートウェイを置く
ユーザがそのサービスを呼び出すことができるかどうかの判断まで
コンウェイの法則
システムを設計するあらゆる組織は、必ずその組織のコミュニケーション構造に倣った構造を持つ設計を生み出す。
チームビルディングも大事
チーム
各サービスはひとつのチームによって所有され、チームはそのサービスについての要件・設計・実装・テスト・運用のすべてに権限と責任を負う。
大規模なマイクロサービス
障害から簡単に復旧することに注力したほうがいい
非機能的な要件で整理
応答時間、可用性、データの耐久性、など
対策例
タイムアウト、サーキットブレーカー、隔壁
サービスの振る舞いをできる限り冪等にしておく
何も考えず再処理だけでよくなる
疑問点
なぜサービスを混在させてはいけないのか?
監視や障害対応がしにくい、仲良死になる
密結合になる?
スケーリング
DBの読み出しのスケーリング
レプリカ、もしくはキャッシュ
DBの書込みのスケーリング
シャーディング
データの整合性
APIシステムが適切な選択しになることがおおい
可用性/分断耐性
1. グローバルサービスでのデザインリニューアルプロセス
手書きモックアップがフロントエンド感ある
2. Quipperにおける将来の学習データを支える基盤づくり
サービスによるコンポーネント化
ドメイン知識の集約、データの一貫性を担保
ニアリアルタイム
EventProcessor
CQRS、Write Model、Read Model
処理完遂の保証
イベントの処理前にチェックポイントを設けて永続化
処理の完遂責務をバックエンドサービスが持つ
Eventual Consistency
非同期で処理結果の一貫性を保証
異なるデータストレージをまたいだ一貫性
サービスによる部分的なリプレイス
小さい塊でリリースしていく、ビッグバンリリースは避ける
3. 動画視聴ログの実装について
単調増加なIDをログに付与して、順序がわかるようにしている
結果整合性
結果的に一貫性が保たれればよい、と言う考え方
途中では整合性が崩れるが、十分な時間が経過したら整合性が取れている
イベントドリブンアーキテクチャ
送り手、受け手はお互いをしらない
AWS SNS, AWS SQS
結果整合性を担保する
送りて側が適切にリトライするのは無理
結果だけ受け取れなかった場合、送信自体に失敗した場合、リトライ可否自体を調べるのもむずい
受け手側でリトライされても大丈夫につくる (冪等性)
冪等性をどうやるか?
冪等ではない箇所があると崩れる
ACIDは常に一貫した状態を保証することを指す
Atomicity, Consistency, Isolation, Durability
トランザクション分離レベル
Dirty reads: Commitしてない変更が他のトランザクションで読めてしまう
Non-repeatable reads: 読み出し中にあった変更の結果込みで結果を返してしまう、タイミングによって冪等では無いという意味?
Phantom reads: 読み出し中にあったレコードの追加/削除の結果込みで結果を返してしまう
ファジーリードとファントムリードは同じ?
BASE
システム全体の特性
Basically Available Soft-State Eventually Consistent
更新はそのうち全体に反映させる
スケールさせるための妥協点か
DDDのサブドメインをマイクロサービスの単位とする
境界づけられたコンテキスト : サブドメイン : マイクロサービス = 1:1:1
マイクロサービスは他のサービスが停止していても独立して可動する
その間は不整合が生じるが、結果的整合性があればよい
DDDの知見が足りなくて余り飲み込めない
Job Queueの話、RailsのもつActiveJobじゃJob Queueを抽象化したもの
Railsでの実装概要と実運用であった障害を踏まえた進化の話
Rails力不足で飲み込めない
sidekiq, resque
イベント駆動アーキテクチャ
クライアントはある事態が発生したことを通知するだけ
他者が何かをすると期待している
AWS SNS、SQSを利用
癖はある、移行の楽さは大事
サービスメッシュ
サービス同士をつなげるのが難しい
Observability
分散システムをうまく運用するためにシステムの繋がり部分の挙動に注目して可視化・監視する
取得すべき値や集約・ストレージの設計などがスコープ
Service Meshとは
アプリケーションに代わってネットワーク層のしごとをする
メトリクス取得/送信、リトライ実行、service discovery, load balancingなどもやる
data-planeとcontrol-planeからなる
proxyとして別プロセスにする
control-planeがproxyを管理する
IstioはService Meshの実装の一つ
control-place: Pilot, Mixer, Istio-Auth
data-plane: Envoy
Envoy
service mesh用のdata-plane proxy
Service Meshの現状
リトライ、タイムアウト、サーキットブレイカーをアプリケーションの外で実現できている
コンテナで動作するアプリケーションの下で動く
ルーティングや、リトライ、メトリクスなどなどをやってくれる
障害対応、キャパシティプランニング、Fault Isolationに関わる設定値の管理など、運用面での課題を解決するために導入された
サービス群の管理コストの削減
どのサービスとどのサービスがつながっているのかわからない
障害時の影響範囲が把握しづらい
Observabilityの向上
サービス同士の通信状況がわからない
より良いFault Isolation機構の構築
タイムアウト・リトライ・サーキットブレーカーの設定がアプリケーション別だった
上の記事を一通り読んだ後だとわかる気がする
サービスメッシュは 、マイクロサービスアプリケーションの構成ができるインフラストラクチャ層です。これにより、サービスインスタンス間の通信が柔軟で信頼性が高く、高速になります。このメッシュは、サービスのディスカバリ、ロードバランシング、暗号化、認証と認可、サーキットブレーカーパターンのサポート、およびその他の機能を提供します。
コンテナ オーケストレーション フレームワーク
コンテナの管理と監視をする
サービスとサービスインスタンス
クラス定義とオブジェクトインスタンスみたいなものか?
サイドカープロキシ
特定のサービスインスタンス専用のプロキシインスタンス
サービス・ディスカバリ
他のサービスインスタンスを教えてくれる
データプレーン
サービスメッシュで作業を実行している部分
コントロールプレーン
新しいインスタンスの作成、不健全または不必要なインスタンスの終了、監視、監視と管理の統合、アプリケーション全体のポリシーの実装、およびアプリケーション全体の正常なシャットダウンなどのタスクを処理します。
親のアプリケーションに接続してサポート機能を追加する
言語が同じでないと結合できないというのは密であり、相互に依存しているとみなせる
これをサービスとして分離する
このときに、多分、機能として単体で使用できる単位で分割する必要がある、なので、サービスと言われる
ただし、メインのアプリケーション専用のサービス、みたいな感じ
通信時に待ち時間は無し、リソースも共有できる
向いているケース
サイドカーコンテナで使いたいコンポーネントが別のチームによって開発されている
メインのアプリケーションと同じホスト上で動作させたい
メインのアプリケーションと別の言語がフレームワークで実装されている
向いていないケース
プロセス間通信を最適化する必要がある
メインのアプリケーションとの間の通信コストが大きい、呼び出し時の待ち時間が大きい
対象のアプリケーションが小さくて運用コストに見合わない
メインのアプリケーションと無関係にスケールしないと行けないとき
マイクロサービスが抱える問題
サービスディスカバリ
カスケード障害
連鎖的に障害が起こること
サーキットブレーカーがそのための安全策だが、各アプリケーションで実装は大変
監視・デバッグ
セキュリティ
Istioはアプリケーションのコードをそのままに導入できることを目標にしている
サーキットブレーカーなどの仕組みを各アプリケーション、各言語で実装するのは大変
それをサイドカープロキシとして提供してくれるのがEnvoy
さらにそれをk8sと連携して動かすのを補助してくれるのがIstio
そもそもSidecarって何?という人もいるかと思いますが、アプリケーションサーバの入り口にキャッシュやアクセス制限用にnginx立てるってのが、多くの人が経験ある一番分かりやすい例かなと思います。
nginxとenvoyの大きな違いはenvoyはoutgoingなリクエストも処理してくれること
実際に使い込まずに、理解しようという考えが甘い気がします。
Amazonのサービス
SQS (Simple Queue Service)
メッセージキューイングサービス
Elastic Transcoder
メディア変換サービス
ECS (Elastic Container Service)
コンテナオーケストレーションサービス
Dockerがうごく
EC2 (Elastic Compute Cloud)
仮想サーバ?
経験が無いのでピンと来ないが機会があれば脳死で従ってみたい