もりはやメモφ(・ω・ )

インフラなエンジニアからSREへ

マイクロサービスアーキテクチャ本の感想(6章)

お正月の長期連休が遥か昔のように感じていますが、ぼちぼちやっていきましょう。さて O'Reillyのマイクロサービスアーキテクチャ本感想の第6回です。

6章「デプロイ」を読んで

6章は以前から興味はあったけれど実践はできていない"CI/CD"について語られています。

CIって何よ

継続的インテグレーション(Continuous Integration)とは、すなわち「すべてを互いに常に同期させることであり、実際には新たにチェックインされたコードが既存コードと適切に統合(インテグレーション)されるようにすること」とあります。要するにgit commitしたら適切にマージされるってことだと理解しました。ポイントは「適切に統合」というところで、コードコンパイルやテストが自動で行われ、高速なフィードバックを得られる状態であることを指します。

あなたがCIを行えているかをチェックしよう

Jez Humble氏の質問を引用し、本当にCIがチェックできているかを振り返ってみよう!のコーナー

  • 1日に一度はメインラインにチェックインしているか?
  • 変更を検証するテストスイートがあるか?
  • ビルドが壊れたとき、それを修正するのがチームの最優先事項か?

上記を全て満たしていなければそれは適切なCIを行えていない状態だぞと、具体的な記述こそありませんが、文脈から伝わってきます。

マイクロサービスにふさわしいCIビルド

モノリシックな1レポジトリ&1パイプラインではなく、マイクロサービス単位でレポジトリとビルドパイプラインが用意されている状態が理想であるとしています。

継続的デップローイ!!(CD)

CIに続いてCDの話です。継続的デリバリ(Continuous Delivery)はビルドパイプラインを段階的なステージにモデル化し、各種テストやビルドを経て本番環境へ自信を持ってデプロイするための概念であるとしています。※詳しくは継続的デリバリー 一般的な例として以下が挙げられています。

  1. コンパイル&高速テスト
  2. 低速テスト
  3. UAT(User Acceptance Testing)=ユーザ受け入れテスト
  4. 性能テスト
  5. 本番環境

例外は避けられない(いきなり理想のCI/CDなんて無理だよ)

初期の開発時は無理にサービスごとのパイプラインを作らず、多少モノリシックでも運用がしやすいCDでOKという話。 理由としては3章のSnapCIが例であったように、初期のアプリケーションはBC(コンテキスト境界)が変動しやすいため。

固有の成果物について

  • RubyのGem、JavaのJARやWARなど、各種プログラミング言語のパッケージマネージャを使うと便利ではあるが、サービスごとに技術スタックがバラバラだとデプロイ環境を管理することが大変になる
  • OSのRPMdebMSIなどを作成すればデプロイ管理は楽になるが、パッケージ作成の難易度が高いのが大変だ
    • 特にWindowsMSIが大変だが、Chocolatey NuGetが状況を変えるかもしれない
    • できればOSレイヤのパッケージでデプロイができれば、管理はシンプルになるだろう(そりゃそうだ)

カスタムイメージでデプロイを楽にしよう

Ansible(Chef,Puppet)などの構成管理ツールを使う際の問題として実行に時間がかかるという点があります。対策としてOSをある程度ビルドしてイメージを準備しておきましょうという話。

  • 第一段階としてはサービスを入れるだけの状態のOSイメージを用意
  • 第二段階としてはサービスも入っているOSイメージを用意
  • 加えてイミュータブルサーバという概念を実施する
    • あらゆる変更は必ずビルドパイプラインを通して行うということ
    • sshを無効化してしまう(なんて恐ろしいと思ってしまうけど)

ステージごとの環境差異はバランスを考えること

次章のテストに繋がる話として、ビルドパイプラインのステージごとに環境が異なり、適切なバランスを取りましょうという話。 例として本番は複数台でレプリケーションを行っているが、ソフトウェアライセンスの理由で開発環境はシングル構成だったため、レプリケーションに影響が出るテストがスルーされてしまって大惨事となったことが紹介されています。

  • 本番に近い環境にすることで
    • 費用が上がる(ソフトウェアライセンスやサーバ費)
    • デプロイの時間がかかる(台数が増える)
  • 本番とは異なる環境にすることで
    • 費用削減
    • デプロイ時間短縮

という天秤のバランスを上手く取る必要があります。 これは私にも経験がありまして、CentOS6のTHPというメモリ管理の仕組みが本番の大規模なメモリを積んだホストではCPUを完全に奪うという事象にやられたことがあります。開発環境の少ないメモリでは全く問題が起きなかっただけに、環境差異がある状況での発見は難しかったんですよね。。

ビルドは1回、環境差異は構成で吸収する

各環境毎にビルドするのはナンセンス。1回のビルドでテスト、ステージ、本番環境の全てを通すべきで、各環境の際は構成を別に管理することで吸収するのがオススメとのこと。 加えて機密情報をビルドに加えるのもアンチパターンであり、こちらも構成管理で吸収するべきとしています。 具体的には以下のような方法があるとのことです。

  • 環境ごとのプロパティファイル
  • インストールプロセスに渡すパラメタ

個人的にはGo言語製のOSSとか起動時のパラメタでほぼ全ての設定が完結するイメージがあります。(Consulとかtraefikとか)

サービスのホストへのマッピング

この節ではサービスをホストへどのようにマッピングするかを取り上げています。 1サービス/1ホストでは物理と変わりがありませんし、多すぎても耐障害性が。。ということで、以下のパターンの良し悪しが挙げられています。

複数サービス/ホスト

1つのホストに複数のサービスを展開する手法。

  • メリット
    • ホストを管理するというシンプルさがある
  • デメリット
    • 仮想化によるオーバーヘッド
    • CPUなどのリソースがシェアされるため、リソース監視が難しい

アプリケーションコンテナ

複数サービスを1つのアプリケーションコンテナとしてパッケージしてホストに展開する手法。

  • メリット
    • 言語ランタイムのオーバヘッドが削減できる(Javaサーブレットコンテナ内で複数のJavaサービスが起動)
  • デメリット
    • 技術選択が限定されてしまう

1サービス/1ホスト

読んで字のごとく1ホスト上に1つしかサービスを展開しない手法。 著者はこの構成こそ目指すべきものだとしています。

  • メリット
    • 構成がわかりやすくシンプルになる
    • SPOFの削減
    • デプロイがやりやすい(イメージベースデプロイ、イミュータブルサーバパターン)
  • デメリット
    • コストがかかる

ここで言うホストとは物理的な物を指すのではなく、VMでも良いです。とにかく1:1の関係でサービスとホストを構築することで、 マイクロサービスの疎結合の考え方を適用しろと理解しました。

PasSについても

最上級のPaaSとしてHerokuが挙げられており、その可能性と有用性を評価する一方で以下のような厳しいコメントでした。 とはいえ本書が執筆された2015年から3年がたった2018年の今では、状況が変わっているのかもしれません。 (PCS[Pivotal Container Service]とかIBM Cloud privateとか、色々出てきてはいますしね)

  • 使用できる技術が限定される(GAEがruby使えないとかそういう話)
  • 標準的なアプリケーションに最適化されがち
  • 内部の細かい制御(チューニング)ができない
  • ホスト型(herokuのような)が主流で、オンプレに展開するPaaSはまだまだ発展途上だ

自動化!!自動化!!自動化!!!

本章ではこれが一番言いたかったんじゃないかと思います。 自動化によりサービスのスケールにオペレーション工数が比例しない状況を作るべきで、 そのための最適な技術を選択して利用する必要があるとのこと。

また、初期は自動化のための作り込みが発生するのは仕方ない(RealEstate.com.auの事例も挙げつつ)が、 後になって確実に効いてくるから頑張れとエールをもらいました(気がします)。

デプロイ先の環境の変化(物理 -> 仮想)

デプロイ先の仮想化技術の選択肢も挙げられています。

  • 仮想化
  • コンテナ
    • Linuxコンテナ
    • Dockerコンテナ

デプロイはコマンド化しろ

節のタイトルは「デプロイのインターフェース」で、あらゆるプラットフォームに共通して必要なデプロイ方法が提示されます。 それは以下の3点を含むインターフェースであることです。

  1. 環境に依存しない統一されたインターフェースを用意し、かつ環境を指定できること
  2. サービス名を指定できること
  3. バージョンを明確に指定できること

上記を考慮すると必然的に以下の様なコマンドになるはずとしています。

deploy service=<サービス名> env=<環境名> ver=<バージョン番号>
※簡略化してます。そのまま引用ではありません。

さらに面白いのが、開発者であれば自分の環境へデプロイするだろうから以下のようになり、

deploy service=<サービス名> env=local ver=local

開発者がコミットすればCIツールが以下のようにバージョンを振り、

deploy service=<サービス名> env=ci ver=1.2.34

QAチームであれば常に最新のビルドをテストしたいから以下のようになるといった、具体的な説明もしています。

deploy service=<サービス名> env=qa ver=latest

環境定義は外に切り出せ

デプロイ方法を記載したコードと、各環境を定義したパラメタは別に管理するべしとあり、Terraformへの期待が記載されています。 HashicorpはVaultという機密情報管理のサービスも行っており、著者が期待したサービスの展開を行っているのだなぁとしみじみ思いました。

具体的には以下のような差異を管理できるようにします。

  • 本番と開発環境ではマシン性能を変える
  • 本番と開発環境では認証情報を変える
  • 本番と開発環境ではスケールを変える

6章の感想

デプロイを従来の手作業でハートフルな方法でやるのは極めてナンセンスであるというのがよくわかり、読みながら胸が痛みました(現状がそうではないのでorz)。 CI/CD環境を初期投資を惜しまずに構築し、自動化を進め、デプロイの速度・制度・開発側への移譲(セルフデプロイ)を高める必要がありますし、実現に向けたモチベーションと作業イメージを得ることができました。 また、パイプラインの粒度も可能な限り小さく、サービス単位とすべきであるという文を読み、本書が掲げるマイクロサービスという言葉の具体的なイメージを得ることができたとも感じています。(この章まではどちらかというと抽象的な話が多い印象でした)

マイクロサービスアーキテクチャ本の感想(5章)

そろそろ正月気分も抜けてきました。 O'Reillyのマイクロサービスアーキテクチャ本感想の第5回です。

5章「モノリスの分解」を読んで

4章の統合に対し、5章では既存のモノリシックなアプリケーションをどうやって分割していくかの話になります。

コンテキスト境界再び

モノリシックなアプリケーションを分割するためには、アプリケーションの接合部(seam)を探し出すこと、接合部としてはBC(コンテキスト境界)がわかりやすいのでおすすめとあり、BCを洗い出した後の流れが語られます。

  1. BC(コンテキスト境界)を特定する
  2. 各コンテキストを表すパッケージの作成
  3. 既存コードを各パッケージへ移動
  4. 取り残されたコードから新たにBCを特定 ※以後繰り返し

このフローによってコードベースを接合部に合わせて整理すると捗るそうです。

なぜ分解するのかを考えながら行うことが大事

モノリスを分解するということは、大理石を削る行為に似ているとし、勢いよく削ろうとすると全体が想定外に割れてしまうと警告があり、そのうえでコードベースをさらに分解する指針として以下が挙げられています。

  • 変化の速度
    • 更新が大量に走る処理はそれ自体が分割の対象になる
  • チーム構成
    • チームの地理的な要素(コンウェイの法則もあると思う)
  • セキュリティ
    • 転送データ、格納データ、監視などの観点で適切に保護ができるようになる
  • 技術
    • 技術特異性の話。新しいライブラリ、ミドルで最適化が可能になる

データベースは依存関係の塊です

structure101のようなツールで依存関係をグラフ化すると接合部を見つけるのが捗るぜ、そして大抵の場合はデータベースが最も依存関係の多いポイントだ(意訳)というノリで従来のRDBのあり方に切り込んでいきます。

  • 外部キー関係は削除しよう
    • 品目、帳簿といったテーブルは、品目管理サービス、帳簿管理サービス専用テーブルとして外部キーを削除する
    • 従来のRDBより性能(処理速度)は落ちるが許容できるか判断しよう
    • 品目テーブルと帳簿テーブルのデータ整合性が一致する必要があるか、サービス要件は技術者が考えることではないのでエスカレしよう
      • (確かに商品名が変わったとして、商品名変更前の帳簿には古い名前が載っていても良い気はする)
  • 共有静的データの取り扱い
    • 案1,各サービスで重複して持ってしまう(ハードコーディングするケースもよくあるとか)
    • 案2,サービス化してしまう
  • 共有データはサービス化すべし
    • データベースで暗黙的にモデル化されているドメイン概念
  • 共有テーブルは分割して各サービスに返す
    • (正規化が可能なテーブルだと思う)

この節は自分の携わるシステムのDBの思い浮かべながら読んだのですが、アプリケーション側というか、開発者への負担は相当な物になるなあという印象。

分割は段階的にやっていこう

サービスの分割と、データベース内のスキーマ分割のどちらから行うべきかという話です。適切な順番としては以下としています。

  1. DBのスキーマを分割
  2. アプリケーションを分割

理由はアプリケーションコードを一緒にしておくことで、コンシューマに影響を与えることなく変更を戻せるためです。(アプリケーション側にも変更は必要のはずで、DBへのクエリだけ変えると理解した)

トランザクションはマイクロサービスでは大変だ

分散システムでのトランザクションの担保は、2フェーズコミットやリトライ、ロールバックなど考慮すべき点が多いとし、結論としては以下3点に集約されます。

  1. 本当に単一のトランザクションで行う必要のある処理かを見直す
  2. 単一のトランザクションが必要な処理を分割させずに済む方法を探す
  3. 分割が必要ならトランザクション事態を表す概念を生成する(処理中のhoge、といったステータス)

読んでいて"CAP定理"の話だなと思いましたが、"CAP定理"という言葉は出てきませんでした。

KPIなどのレポート用データベースの取り扱い

よくあるユースケースとして、レポート用のデータベースは複数のデータを結合することが求められますが、どう扱うと良いのかという話です。

  • サービスを介してAPIで呼び出す
    • 一見よさそうだが、大量データ(全ユーザなどの)の取り扱いにRESTは向いていない課題がある
  • データポンプ
    • レポート用のDBへ、必要なデータを定期的にコピーする
    • S3を使ってJSONファイルで渡すといった事例もある
  • イベントデータポンプ
    • レポート用のDBへ、データ変更が発生するなどのイベントをトリガとしてデータをコピーする
  • バックアップデータポンプ
    • NetfrixのCassandraのデータをバックアップする事例から
    • S3とHadoopを利用する後のOSSAegisthus
      • Aegishusは2018/1/11時点ではメンテナンスモードで今後機能拡張されない様子
  • リアルタイム
    • fluentdやlogstashなどを利用したリアルタイムデータ転送(本では具体的なツール名はでていないけど)
    • 今後加速すると著者は予想している

変更コストと根本原因を意識する

  • 変更コスト
    • 最小の影響範囲で失敗できるポイントから変更していく
  • 根本原因
    • モノリシックになることは悪いことではない
    • サービス初期開発時は作りやすさからモノリシックになる、それはOK
    • 適切なタイミングで分割を行うことが大事

5章の感想

本章はDB分割のところを大変面白く読みました。コンテキスト境界を意識して大きな枠から分割していくイメージは言葉だけで分かった気になっている感覚があります。一方でDB分割は自分の業務に近いところでもあり、業務を想定しながら具体的な分割方法を検討する良い機会にもなり、そしてそれが非常に苦労する道のりだという知見が得られた章でした。

マイクロサービスアーキテクチャ本の感想(4章の後半)

まだまだ正月気分が続きますが、O'Reillyのマイクロサービスアーキテクチャ本感想の第4回です。

4章「統合」を読んでの続き

バージョニングについて

この節ではマイクロサービスにおけるバージョニングについて説明があります。マイクロサービスの内部としては分かりやすいバージョニングをしつつ、外部のマイクロサービスへはバージョンを意識させないことが大事とあります。(いつから新バージョンになったと錯覚していた!!)

  • セマンティックバージョニングを利用した"分かりやすい"バージョニング方法
    • x.y.zのxはメージャーバージョンを意味し、破壊的な変更を伴う
    • x.y.zのyはマイナーバージョンを意味し、機能追加だが互換性あり
    • x.y.zのzは不具合修正
    • (この辺はdockerみたいな日付(年)を採用するところもあるので考え方次第かも)
  • 破壊を伴うバージョンアップを行うためのエンドポイントの共存について
    • ブルーグリーンデプロイメントやカナリアデプロイで一時的に共存させるのはOK
    • "/api/v1/hoge"と"/api/v2/hoge"のようなわかりやすいURIを利用するケースもあるがお勧めできない
      • クライアントからマイクロサービスのAPIバージョンは不透明(バージョンはわからない)とするべき
    • 古いクライアント向けに古いバージョンを稼働させ続けるのも手だが、管理コストが跳ね上がる

ユーザインタフェースについて

PCだけでなく、スマホタブレット、スマートウォッチなどデバイスが多様化している現在でのUIの構成ポイントが語られます。

  • 様々なデバイスが存在し、それぞれの制約がある
    • ブラウザ、解像度
    • モバイルの場合通信は最低限であるべき
    • SMSを利用するケースも多い
    • 今後新たなデバイスが出てくる可能性も高い(VRやARもありますしね)
  • API合成は各マイクロサービスが提供するAPIを組み合わせてUIを作る手法
    • メリット
      • バイス単位に再利用できる
      • 部品ごとに利用して特定の画面パーツを自由にカスタマイズできる
    • デメリット
      • バイスを意識したレスポンス調整ができない
      • 合成したUIをだれが作るのか問題(APIまでは作るけど、その後はだれ?)
  • UI部品合成はマイクロサービスで小さな画面部品(UI)まで作成し、それを各デバイス向けに組み合わせる手法
    • メリット
      • マイクロサービスチームがUI部品の変更まで担当可能
    • デメリット
      • 各UIの一貫性をどうやって保つか問題(担当チームの趣味がでるかも)
      • HTTPだけじゃなくてネイティブアプリの場合どうにもならない問題
  • BFF(Backend For Frontend)は、フロントエンド(PCブラウザ、ネイティブアプリ、管理者向け画面)などの各フロントエンドごとにAPIコール用のバックエンドを設ける手法
    • 単一のAPIゲートウェイを置くのは悪手(モノリシックなAPIゲートウェイの誕生)
    • BFFとUIの間にAPI認証/認可のレイヤもはさむことができる
    • 気を付けるのは結合。ほかのレイヤの機能や、ほかのUIの機能がBFFに混入しないようにする
  • ベストな方法は各自探すこと
  • 大事なのは凝集性を維持すること

サードパーティソフトウェアとの統合

SaaSCMSCRMなどのサードパーティとの付き合い方についての解説。便利かつ多機能であるが故に、気が付くと沼のように沈み込んでしまう危険性がありますよという話。

  • ツールの手前にファサードサービスを設けて隠ぺいする
    • ファサードは自前で作成したサービスでAPI呼び出しなどを中継する役割
    • 各マイクロサービスから直接ツールのAPIをたたくと結合になって大変
    • サードパーティソフトウェアが一つの巨大な共有データベースになる危険性
  • 沼から抜け出す場合はストラングラーパターンで徐々に対応する

4章の感想

4章は分割したマイクロサービスをいかに統合して顧客サービスとして展開していくかを学ぶことができました。分割したそれぞれのマイクロサービスをどのように連携させ、UIをどう構成し、サードパーティとどのように連携するのか、実業務への落とし込みを考えるとタスクの洗い出しだけでも大変に違いありません。

もっとも衝撃的だったのは共有データベースがマイクロサービスとしては悪手であるということで、目からうろこでした。うすうす感じてはいた「とりあえずデータストアといえばRDBでしょ、という時代では無い」ことを言葉として認識できました。

サービス間連携についてはRPCとRESTについて多くの用語や概念を学べました。リチャードソン成熟度モデルやらHATEOASとかRxとか聞いたことがある程度でしたので、著者が進める文献へのリンクなどが載せられているところは親切で助かります。

マイクロサービスアーキテクチャ本の感想(4章の前半)

始まる前は長いと思っていたお正月休みも終わってしまいますね。 O'Reillyのマイクロサービスアーキテクチャ本感想の第3回です。

4章「統合」を読んで

4章はまず、そのボリュームに圧倒されます。実ページ数で47Pもあり、これは1章の13P、2章の17P、3章の10Pと較べて倍以上のページ数となっています。章タイトルは「統合」で、マイクロサービスとして分解した各サービスをどうやって組み合わせて実サービスにつなげていくかの話になります。時間と集中力の兼ね合いで4章は前半と後半に分けます。

マイクロサービス技術の一番大事なところが統合なんだぜ

「統合を間違えると大惨事」や「SOAの試みを苦しめてきた最大の落とし穴」といった脅し文句から4章は始まります。技術的な話の前に、選択しようとする技術から何を得たいのかを確認しており、それは以下であるとしています。

  • 破壊的な変更を回避すること
    • 疎結合で繰り返し言われた、他のサービスに影響を与えずに変更が可能であるという話
  • APIを非技術依存にする
  • コンシューマにとって単純なサービスにする
    • コンシューマに簡単に利用してもらいたいが、簡単にするためのクライアントライブラリは結合を生むというジレンマがある
  • 内部の実装詳細を隠す
    • 隠れモデルの変更は共有モデルやインタフェースに影響させないという話

僕の価値観が崩壊した「共有DBは大規模な共有APIであり脆弱である」という考え方

僕がこれまで携わったシステムでRDBを使用しないシステムは無く、その大抵のシステムに"共有DB"的なものが存在していましたが、共有DBは「4.3 共有データベース」でけちょんけちょんにされます。この項は衝撃的で実に面白い内容でしたし、項の終わりが「(データベース統合)はいかなる代償を払ってでも避けてください」というインパクトのあるものです。

疎結合の喪失

従来のRDBを用いたデータ参照は"一般的"であるとしながらも、DBへアクセスする全ての関係者とデータ構造全体を共有しているため、極めて結合状態にあり、スキーマ変更などの影響範囲が大きくてリスクが高くなるとしています。 加えて特定のDBミドルウェアへのアクセスには専用のドライバを使用するケースが大きく(JDBCのような?)、データストアしてRDBではなく非リレーショナルなデータストアミドルが適切であったと後から気づいたとしても、コンシューマ側への変更が多大であって、ミドルの変更は現実的ではなくなるという問題があります。

凝集性もグッバイ

DBを直接操作するようなケースでデータの変更ロジックが複数のコンシューマ側に存在しがちとなり、DB側の変更が複数のコンシューマの変更に繋がってしまう問題があります。複数のサービス内に同じテーブルのUPDATE文がありえてしまうという問題です。

データベース統合による弊害

データベース統合は各サービスがデータを共有しやすくなりますが、振る舞いの共有は何もせず、内部表現が直接公開されてしまうことで、破壊的変更が避けがたくなってしまうという問題があるため「(データベース統合)はいかなる代償を払ってでも避けてください」としています。

サービス間の連携は同期、非同期通信があるよ

同期通信と非同期通信に関する説明です。

  • 同期通信は処理の呼び出し、応答がくるまでブロックする
  • 非同期通信は呼び出して後のブロックを行わずに、以下の2パターンが存在する
    • リクエスト/レスポンスは同期通信でも行われるが、非同期でも利用可能で、処理の終わりをレスポンスで検知する
    • イベントベースは非同期通信のみで、他サービスに指示(リクエスト)を送らず、ただイベントが発生したことを通知し、他サービスがヨロシクやってくれるのを期待する

モデル化手法も知っておこう

ビジネスプロセスをモデル化する2つの手法について説明が入り、マイクロサービスであればコレオグラフィシステムを目指すべきとしています。

  • オーケストレーション手法は、モデル化されたフローチャートがそのままサービスのリレーションになる
    • わかりやすい
    • リクエスト/レスポンス
    • 特定のサービスが中央集権になりがち(貧弱な神がうまれる)
  • コレオグラフィ手法は、イベントベースで各サービスが正しく動くことを期待する
    • ビジネスプロセスが暗黙手な実装になり(わかりづらくなる)
    • イベント発行後に各サービスが適切に動作したかを監視する必要がある
    • 各サービスの変更に強くなる

リクエスト/レスポンスを実装するための技術

リクエストレスポンスの実装に適したRPCとRESTについて解説が入ります。

リモートプロシージャコール(RPC)

RPCはローカルで呼び出しを行い、リモートサーバで実行するテクニックで、メリットもあるとしながら、おすすめできないという結論になっています。

  • インターフェース定義に依存する、SOAP,Thrift,Protocol Buffers
    • インターフェースさえ同じであれば、異なる技術スタックを選択できるというメリット
  • Java RMIはインターフェース定義は不要だが同じ技術基盤を使用する必要があり、密結合が生じる
  • RPCは早く実装できる以外のメリットは少ない
  • ローカル処理とリモート処理が区別できなくなり、負荷を無視した処理を行いがち
  • RMIは密結合を起こしがちだが、Protocol BuffersやThriftならサーバとクライアントが別デプロイ可能となる

REST(REpresentational State Transfer)

Webからアイデアを得たアーキテクチャ。REST自体にも様々な形式がありリチャードソン成熟度モデルは一読すべしとあります。 ※英語力が低い僕としてはこちらの方が短くてありがたかった

リチャードソン成熟度はざっくりREST実装をどの程度成熟して使用できているかの評価モデルであり以下のような4レベルが定義されているとのこと。

  • レベル0は何も考えてない
  • レベル1はURIは使えている
  • レベル2はURIとHTTPは使えている
  • レベル3はURIとHTTPとHATEOASが使えている
    • HATEOAS(Hypermedia As The Engine Of Application State)は"アプリケーション状態エンジンとしてのハイパーメディア"の意味で、リンクを辿ることでコントロール達することができるような概念
      • 要するに何かを実行するために、どこを探せば良いかだけを知っておいて、実行する際は探して見つかったものを実行する

RESTはHTTPを利用することが以下の理由でメリットがあり一般的だそうです。

  • もともと動詞(GET,PUT,POST)を持っていて新たな概念を定義する必要が無い(SOAPが失敗したのはこの概念の新定義がイマイチだったとか)
  • HTTPプロトコルを制御する膨大なエコシステムを利用できる

RESTにおけるデータ形式JSONが人気ですがXMLにも利点があり、著者のSam Newman氏は自分は少数派としながらもXMLを推すそうです。

  • JSON
    • 形式が単純で利用が簡単
    • 人気
    • 欠点としてLinkコントロールの定義が無い
  • XML
    • Linkの問題が無い
    • 多くのサポートツールが優れている(XPath)

当然ながらHTTP上のRESTにも欠点はあり、それは以下としています。

  • クライアントスタブの作成は、RPCよりは大変
    • 優れた多くのHTTPクライアントライブラリは使用できるが、HATEOASまで実現しようとするとフルスクラッチになりがち
      • こっそりRPCを持ち込んだりしてしまう
      • 共有クライアントライブラリを作ったりしてしまう
  • HTTPの持つ動詞で、PUTやDELETEを実現するのは複雑になりがち
  • 大量トラフィックには向いているが、低遅延通信には向いていない
    • WebSocketの方が優れている

イベントベース連携をを実装するための技術

続いてコレオグラフィシステムで用いるイベントベース連携を実装するための技術についての解説で、はじめに考慮する点は以下の2点とあります。

  • マイクロサービスがイベントを発行する方法
  • コンシューマがイベント発行を検出する方法

これらの実装方法については以下があります。

  • 従来はRabbitMQなどのメッセージブローカが対応しており、構築と運用が大変なものの稼働させられればとても効果的な方法である
    • ただしシンプルに使うことが重要で、ベンダーはキューに機能を盛り込みがちなことは注意
  • HTTPを利用したATOMフィードを利用する方法
    • ただしRESTでも示したようにHTTPプロトコルであるための欠点(低遅延が苦手)はある
  • 現在JSONを使っていて問題なければそれでもOK(ここは文脈が読めなくて、JSONやめろと言っているようにも読めます。。。)

非同期アーキテクチャの複雑さ

これまでイベントベース連携すごい、非同期通信ナイスと持ち上げてきましたが、この項では実際の失敗談を交えながらそれらを実現するために生じる複雑さについて考える事を呼びかけられます。 筆者の教訓としては以下があったとしています。

  • ジョブの最大リトライ上限を設定
  • 不適切なメッセージの監視
  • 再現手法の準備
  • 最終的なメッセージ病院の準備(配達不能、失敗したメッセージの送り先のキュー)
  • 不適切なメッセージの参照および必要によってリトライするためのUIの準備
  • そもそもこの複雑なアーキテクチャで実装すべきかを真剣に検討するようになった
  • 相関IDを利用したプロセス境界をまたいだ適切な監視がマジで大事

覚えておきたい考え方など

ここで流れが変わり、2つの考え方とライブラリについて簡単な説明が入ります。

  • 状態マシンとしてのサービス
    • どんな(RPCでもRESTでも)実装を行うにしても、コンテキスト境界内の振る舞いに関連するロジックを、別のサービスに流出させては行けない
  • Rx(Reactive Extentions)
    • 複数の呼び出し結果をまとめ、それに対応して操作を実行するライブラリ
      • サイトによると「オブザーバブルシーケンスを使用した非同期でイベントベースなプログラムのためのライブラリ」とある
    • 主要な言語に対応している
    • サービスの呼び出しが増えて来ていて、特に1つの操作のために複数の呼び出しを行っている場合はRxを思い出すと吉かも

マイクロサービスではコード重複を認めよう、なぜなら

共有コードは結合を生みかねない。ゆえにカスタムテンプレートを利用して各サービスは都度それをコピーして開発し、コード重複は許容しようとしています。 DRY(Don't Repeat Yourself)は一般的にコード重複を避けるべしと理解されがちですが、正しくは「システムの振る舞いと知識の重複を回避する」ことを指すとのことで、

  • 一つのサービス内でのコード重複はDRYに
  • 全てのサービス間でのコード重複は許す

とし、経験則からサービス間の結合が及ぼす害は、コード重複の外よりも大きいためであるためとのことです。

クライアントライブラリについて

この項は項番が間違っているんじゃないかと思っています。「4.11 マイクロサービスの世界におけるDRYとコード再利用のリスク」に対して「4.11.1 クライアントライブラリ」となっているのですが、内容に関連性が無いためです。 この項ではクライアントライブラリについて、AWSSDKが良い手本であるとし、サーバAPI開発者がクライアントライブラリを作成すると、クライアントライブラリ側にサーバ側のコードが結合しがちであり、Netflixにおいても結合が発生し始めているともあります。大事なポイントとしては

  • クライアントコードを分離すること(サーバ側のコードから)
  • トランスポートプロトコルへの対処(サービスの検出や障害、目的のサービスに関連すること) ※ここは僕は理解できてないorz
  • クライアントライブラリのアップグレードのタイミングを、クライアントに管理させる

力尽きたのでここまでorz

やはり4章は長いですね。。。この感想文は1度さらっと読んでから、読み直しつつ書くというやり方をしていますが、今日は力尽きたのでここまでにします。次は「4.12 参照によるアクセス」です。

マイクロサービスアーキテクチャ本の感想(3章)

あけましておめでとうございます。雑煮とお酒と3歳児と遊ぶ年始を過ごしながらO'Reillyのマイクロサービスアーキテクチャ本感想の第2回です。

3章「サービスのモデル化方法」を読んで

3章では実際にマイクロサービスをどのように実践していくかの手始めとして、サービスの境界について説明がされています。

優れたサービスにするための疎結合と高凝集性

本章以前でも触れられていた疎結合と高凝集性について改めて語られています。絶対に忘れてはいけない非常に大切な2つの概念であると位置づけており、一言でいうと以下になります。

  • 疎結合とは、特定のサービスへの変更が他のサービスへ影響しない状態であること
  • 高凝集性とは、振る舞いが同じものを適切にまとめること

コンテキスト境界という考え方

続いてコンテキスト境界(Bounded Context)という言葉が出てきます。こちらもマイクロサービスの概念を語るうえでとても重要な用語でありエリック・エヴァンスのドメイン駆動設計にも触れながら説明がされています。僕はざっくり1マイクロサービス=1コンテキスト境界が理想という理解をしました。

  • ドメイン、事業全体を差す大きな枠(詳しくはDDDを読んだ方がよさそう)
  • コンテキスト境界、ドメイン内で異なるコンテキストごとにまとめたもの
  • コンテキスト境界内には以下の二つのモデルがある
    • 隠れモデル、他のコンテキストへ見せる必要がないもの
    • 共有モデル、他のコンテキストと共有するもの
  • インターフェース、共有モデルを実際に受け渡しするエンドポイント

外へ見せる必要の無いものを隠すことで疎結合の'疎'を実現しつつ、コンテキスト毎にまとめることで高凝集性を高めること、それがコンテキスト境界であるといったところでしょうか。

モジュールとサービス

続いてモジュールについての説明がされますが「あれっ?」となりました。第1章の「1.4 他の分解テクニック - 1.4.2 モジュール」の項で、モジュールも蜜結合になりがちだから気をつけろというような文脈があるのですが「3.3.2 モジュールとサービス」ではモジュール化しましょうとあります。読みなおすと"コンテキスト境界内では"モジュール化をするべきで、それは隠れモデルと共有モデルを体現したコードベースであることが望ましく、そのモジュールがそのまま一つのマイクロサービスの候補となります。

コンテキスト境界を定義する方法アレコレ

その後はどのように境界を定義するかの話が続きますが、GoCDにおける失敗談(早く分割し過ぎて定義を失敗し、一度モノリシックにして再び分割しなおした)で釘を刺しつつ以下が挙げられます。

  • ビジネス機能では該当サービスが何のデータを他サービスへ提供するかを考えるのは二の次で、とにかく機能を先に考えることが重要
    • データに着目して分割すると機能不足になりがちだから
  • コンテキスト境界内での分割も進めていくべし(共有モデルには影響しないように)
  • とはいえ技術的な観点からも注意して分解すること

3章の感想

3章はコンテキスト境界という概念を学ぶための章だったなという感想です。こういった高水準(技術よりではないという意味)な内容は、概念を定義するための用語がバンバン出てきて正直初見は辛いのですが、"コンテキスト境界"のように言葉として定義されることで理解が進むんですよね。分解の方法については読んでいる分には当たり前なんですが、実業務だと同じ失敗をしそうですが、本書を読んだことですぐに失敗に気づけることを願います。

マイクロサービスアーキテクチャ本の感想(1章、2章))

年末年始ですね。この長期休暇を利用して停滞していた意識改革を行うために、O'Reillyのマイクロサービスアーキテクチャを読み始めたので読んだ部分から感想をメモしていきます。(一気読みするには内容が濃いのです...)

1章「マイクロサービス」を読んで

1章ではマイクロサービスって何?から入り、それは「強調して動作する小規模で自律的なサービス」であると定義しています。小規模とは組織や人それぞれの感覚によるとしながら一例として「2週間程度で書き直せる」程度の規模であること、自律的とは分離されて他に影響を与えずに変更することができること、としています。

マイクロサービスによるメリット

マイクロサービスがどのようなメリットを生み出すかについては、

  • 分離によってサービス単位で異なる技術を採用することができるという「技術特異性」
  • 各サービスが分離されていることで単一サービスの障害影響がまるで隔壁が存在するかのように広まらないという「回復性」
  • 分離されているんだから特定のサービスの規模だけ大きくできる「スケーリング」
  • 特定のサービスだけ変更できる(独自のライフサイクルを持てる)「デプロイの容易性」
  • サービス単位に柔軟に人をアサインできる「組織面の一致」
  • 多様化するサービスの提供形態に応じるために、サービスを部品として組み合わせられるという「合成可能性」
  • 小さいコードであることで書き換えが容易であることから「交換可能性」
  • マイクロサービス化の結果としての「SOA」の実現

が挙げられています。

似て非なる分解技法

あなたとは違うんです」byマイクロサービス
として、共有ライブラリとモジュールについて何が違うのかが解説されています。共有ライブラリについてはその特性として"結合"が生じやすく、変更の容易性が失われてしまうケースが多いことが問題としてあること。モジュールは適切に分離されたモジュールを開発することがそもそも大変であり、共有ライブラリと同様に結合性をもってしまうケースが多くなりがちとあります。

定番のあのワードで釘を刺される

マイクロサービスは銀の弾丸ではありません。と1章の終わり前に明記されています。上述したメリットを得るためにはマイクロサービスの複雑さ、難しさに挑む必要があるんだぜ?だから2章以降もしっかり読みなされ、と釘をさされます。

1章の感想

ふわっとしていたマイクロサービスという言葉がかなり明確な形になりました。特に「技術特異性」の項でサービスごとに異なるストレージを選択している図が提示されていて、それを見てマイクロサービスってこういうことかと腑に落ちました。

2章「進化的アーキテクト」を読んで

2章ではマイクロアーキテクチャを設計するアーキテクトの心得について語られます。章の冒頭からアーキテクト(建築士)という言葉がソフトウェア開発におけるアーキテクトにはふさわしくないという否定から入り、本来の意味でのアーキテクトは物理建築のような見通しがたって物理的制約を受けるものを設計する役職であるに対し、ソフトウェアのアーキテクトは不確実性が多く柔軟性が求められるから勘違いするなよ、と言われます。

進化的アーキテクト

アーキテクト(建築士)という言葉を否定した上で、ソフトウェアのアーキテクトがどのような役割を持つべきかというと、進化し続けるソフトウェアと同様に常に進化が求められるとし、建物を建てるというよりはシムシティを例に「都市計画課」が役割として近いとしています。決められるのは区画までで、区画内で開発者がどのような建物を建てるのか、ユーザがどのように建物を利用するのかまでは制御できないとしています。その上で開発者もユーザも満足が行く区画配置を行うのが進化的アーキテクトの役割であると定義します。

良いアーキテクトへの道しるべ

ではどうすればよいのか?開発者もユーザも満足させるアーキテクトになるためのノウハウとして、

  • 区画間で起こることに目を向けるべし
  • 各区画の中に積極的に入り、状況を把握すべし(ここちょっと矛盾している気がしたけど、制御はしなくて良いから状況は理解しろってことだと読んだ)
  • 組織が決定した戦略的目標を理解し、原則とプラクティスを持って各区画を正しい方向に導く必要がある
    • 原則とは戦略的目標に行動を一致させるための規則集(有名なHerokuの12Factorsは設計原則週)
    • プラクティスとは開発者よりの原則を実行するための指針(CI/CDを実践する、みたいな)
  • 標準的な機能を定義して適切な監視、インターフェース、エラーコードを実装する
  • 手本とサービステンプレートを利用したコードを介したガバナンスの実践
    • 手本とは実際に稼働しているマイクロサービスのコードが望ましい
    • サービステンプレートとは上述した「標準的な機能」を提供できるようなテンプレートをさす
  • 技術的負債を把握し、全体的な視点から対処すべき負債を洗い出す必要がある
  • 原則に沿わない技術もある程度許容し、ただし「例外処理」の理由をログとしてしっかりと残しておく
  • 上述した項目を個人で抱えるのではなく、グループとしてガバナンスを形成することが望ましい
    • ただしグループが誤った方向へ向いていると判断した場合は、毅然とした大度で拒否するべし
      • ただしグループからの信頼と、グループのやる気を失うリスクを十分に承知して、慎重な判断をすべし(ここは本当に著者の苦しみが伝わってくるところでした)
  • マイクロサービスは小さいがゆえに開発者が所有しやすく、成長を促そう

とした上で、章のまとめとしては「進化的アーキテクト」はこれらを全部綱渡りしつつ実践する必要があって、それを達成するには経験を積むしかないけれど、とにかく固定(停滞といってもいいかも)せずに努力していこうぜとしていました。

2章の感想

2章は言葉の勉強と進化的アーキテクトという超人の物語だったなという感想です。原則、プラクティス、戦略的目標といった言葉の意味を学べたということは、人にも説明しやすくなったということで、自分たちのサービスのマイクロサービス化を実践していく上で認識の共有に役立てることができそうだと思ったし、進化的アーキテクトに求められる様々な要求を通してマイクロサービスに必要な考え方を教えられたと感じています。

Team-Geekを読んだ感想

twitterでオススメされたのがきっかけでこの本を読みました。@xinolinxさん、ありがとうございますm( _ _ )m

読んでひとこと

端的に言って素晴らしい本です。本当に読んで良かった。 世の中には人に勧めたい本とそうじゃない本がありますけど、圧倒的に前者の勧めたい本ですよこれ。

HRT(ハート)については、けものフレンズの関連で知っていたものの、そもそも「えっちあーるてぃー」と読んでいたくらいでした。 本書では三本柱であるHRTを中心にして人、チーム、組織、ユーザとのより良い関係性の構築についてウィットに飛んだ文章とあるあるな事例で学ぶことができます。

HRTの全てに通じる感

HRTとは以下の頭文字を組み合わせたものです。「痛みを和らげるからハートって読む」とありますが、とてもイカした呼び方ですね。

  • Humility(謙虚)
  • Respect(尊敬)
  • Trust(信頼)

本書では人間関係の基盤、あらゆるコミュニケーションの基盤としてHRTが非常に大切であること、そしてその実践が難しいことが冒頭の一章で記述されます。 そして「あらゆる人間関係の衝突は、謙虚・尊敬・信頼の欠如によるものだ」と定義し、ではどうやってHRTを実践して衝突を無くしていくのかを様々な場面を通して提示してくれます。

読み進めるうちに自分の中で拡大していくHRT

章の構成も秀逸で個人から徐々に組織へとHRTの適用範囲を広げていくため、「全部HRTでいけるね!?」というHRTの重要性がどんどん自分の中で高まっていき、だからそこ常にHRTを意識した行動を取るということが難しいという理解と、意図的に実践していかねばならないという意気込みが芽生えていきます。 参考までに全体の流れを書くと以下のような形です。

  • 一章:HRTについて個人での実践方法
  • 二章:良いチーム文化の作り方
  • 三章:良いリーダとしてのあり方
  • 四章:困った人への対応
  • 五章:組織の動かし方(下から上を効果的に利用する)
  • 六章:ユーザとの良いつきあいかた

僕の立場はいわゆる平社員で、リーダなんかとんでもない(と今のリーダのハードワークな状況を見て)思っていたけれど、チームが良くなるための行動は惜しみなく実践しようという意気込みは持っているつもりです。だから二章と三章を読んでリーダにとって良いメンバでありたいし、良いチームの文化を作るメンバになりたいと思えました。 そこへ四、五、六章がチームだけの事を考えてはいけない、チームの外にも目を向けてHRTを実践すべしと語りかけてくるので、目からウロコとなりました。

特に刺さったエピソード

読んでいて特に記憶に残ったエピーソードを備忘として取り上げます。

1.3 隠したらダメになる

「バス係数」この用語にやられました。プロジェクトのメンバーがバスに轢かれたとして、どれだけ耐えられるか?というブラックな係数ですが。ちゃんと共有することの大切さがストレートに伝わってきましたし、語感のやわらかさと意味のハードさのギャップがとても印象的です。

1.7 批判の配分と対応を学ぶ

ちょうど最近は業務の引き継ぎを行うことが多くなっていて、しかも相手が不慣れなメンバだったこともあり、ついつい批判しがちなコメントをしてしまう場面がありました。 この項では「相手に対する疑問ではなく、自分の疑問として謙虚に聞く」という文章が出てきて、なるほどこれはスマートだと得心しました。 これまで「本当にこれで合ってる?大丈夫?問題ないの?」と言っていたところを「僕はこれではXXXの場合に上手くいかないと思いますがどうでしょうか?」と置き換えるだけで、HRTをやれている感が出てくる気がします。これはすごい。ちゃんとやるように自分。

2.3 文化と人々

「チームのエゴはいいことだ、しかし個人のエゴは大惨事につながる」からの「(本書についての批判、レビューを無視したら)もっとひどかったと思う」で思わず笑いがでました。 僕としても批判やレビューをチームから貰うことがちょくちょくあり、その度にとても感謝しています(心が折れそうになることもありますけど)

3.5.6 正直になる

アンチパターンとして「褒め言葉のサンドイッチ」が上げられていて、自分が正にやっていたことだったので大反省した。これまで相手に伝わりきっていない感があったのはこのアンチパターンのせいだった可能性が高い。 要するに相手に注意とか改善点を伝える時に耳に心地よい言葉で挟むのはダメだということ。僕がやりがちだったのは

  • 褒める「なるほど、そういうやり方も良いですね」
  • 注意「でもxxxxの方法が早いし分かりやすいですよ」
  • そして褒める「とは言え、あなたのやり方も面白いといえば面白いですけど」

という言い方で、最終的に相手に残るのは「これは面白いやり方」ということになってしまっていたということ。だから今後は

  • 褒める「なるほど、そういうやり方も良いですね」
  • 注意「でもxxxxの方法が早いし分かりやすいですよ」

だけで止める。絶対止める。ついフォローしたくなるけど我慢しようと思います。

5.2.1 理想的なマネージャー

武士道なんてカッコいいものではないものの、僕はダメな意味での忠実さを表面的に持っていて、上司がXと決めたら多少不満があってもXを支持するスタンスでやってきていた。(そして我慢できなくなればその組織を去る覚悟でもいた) 上司というか誰もがそうだけど、納得いかないことに関してはちゃんと質問すべきだし、相手に察することを求める前にこちらから伝えにいくべきだって思えました。

5.4.3 上司の管理方法を学ぶ

この項は読んでいて少しショックを受けました。自分が上手くやっていることをキチンと上司に伝えるべしとあり、そこは納得できたのだけど、防御的な仕事に時間をかけても評価されないとあるからです。 僕はインフラエンジニアなので、プロダクト開発のような攻撃的な仕事はそうそう無いって思ってしまった。 なぜなら"運用"ってサービスができている正常な状態を守り続けることなので、もう完全に防御的なわけで、評価されないとかツラいとなったんです。 が、2度読み直して、そこは考え方の問題で、新プロダクトのためのインフラ設計とか、既存プロダクトの運用の中でもツール導入によるデプロイ時間短縮とか、要は見せ方が大事なんだなと思い直します。 この項で伝えたいことは「自分がうまくやっていることをちゃんと上司に伝える」ことですから。

6.1.2 小さく約束して、大きく届ける

リリース日を事前に発表するのではなくて、できてから発表することでユーザに驚きを提供するという話。 これ日本の会社も実践するだけで、デスマーチとか減るんじゃないですかね...(前職のSier仲間を思い出しながら)

6.2.1 観客を選ぶ

この項は他とは違って、少々苦々しいというか複雑な気分で読みました。というのも著者の二人がsubversion開発者だったこともあり、ちょくちょくsubversionのエピソードがでてくるのだけれど、この項はGitの複雑さに焦点をあてて反面的に使いやすさの大事さを説いています。でもほら、世の中のVCSデファクトはGitでほぼ決まりですよね。そういう意味でも味わい深い項でした。

6.2.4 速度重視

サービスのレスポンスが早いことは良いことだし、実際に利用ユーザ数への効果もあったというエピソード。 前の部分で「リファクタリングのような防御的な仕事は評価されない」とあったけど、リファクタリングによってアプリケーションの速度が上がるなら、それは攻撃的な仕事なんだってことだと思いました。

6.3.1 見下さない

とても分かりやすいHRTですね。僕は車の内部構造とかさっぱりな人間なので、自動車整備工の例えがとても分かりやすかったです。

6.3.2 我慢する

拷問にあっていたおばあちゃんのMacの話が最高すぎて他が吹っ飛ぶ勢いでした。

まとめ

改めてTeam-Geekという本に出会えたことを感謝します。HRTという言葉の深みを学ぶことができましたし、今この瞬間から意識的にHRT実践を開始したいと思っています。 特に得たものとして大きいと感じているのは、HRTという指標が自分の中にできたという点です。 今後も人なりチームなり会社なりと衝突することはあるでしょうが、険悪になったり傷ついた時に「こんな状態になっているのはHRTが欠如していたということだ、何が悪かったのだろうか?」という振り返りができるはずで、必要ならこのTeam-Geekを読み直すことができるのって最高ですよね(すべての答えが載っているわけないけど、振り返りの大きな助けになるはず)。 このような将来また読み直したいと思える本に出会えたことは本当に幸福なことです。勧めてくれた@xinolinxに改めて感謝です!

追伸

訳者の角 征典(かど まさのり)さんにも感謝したいです。全体を通してユーモアとウィットに飛んだ本だと思いましたけど、これ翻訳本なんですよ、すごいですよね!! で著書一覧を見たらあのリーダブルコードの訳もされている!やっぱりすごい人でした。他の本も機会をみて読ませて頂こうと思いました。