2016年07月

マイクロサービスアーキテクチャを読んだ

マイクロサービスアーキテクチャを読んだ。

マイクロサービスアーキテクチャ
Sam Newman
オライリージャパン
売り上げランキング: 5,636

所感としては、カバー範囲が意外と広範囲だったのと、日本で流行っているものとUSで流行っているものの違いのおかげか、知らないプロダクトや技法などについて書いてあって勉強になった。 パラパラと読んでしまったので、もう1度読んで深掘りしたいかもしれない。

以下、ハイライトしてた文章を引用。すでに自分が知ってたり、考えたことがあったものについてはハイライトしてない。

  • いろいろな意味で、マイクロサービスに分解したい既存のコードベースがある方が、最初からマイクロサービスに取り組むよりはるかに簡単です。
  • 組織に存在する境界づけられたコンテキストについて考える際、共有するデータの観点ではなく、そのコンテキストが残りのドメインに提供する機能について考えるべきです。
  • データについて考えると、貧血症のCRUDベースのサービスとなるのを何度も見ています
  • 1つのマイクロサービス内ではDRYを破らないけれども、すべてのサービスにわたるDRYの違反には寛大に対処します。
  • オーケストレーションでは、オーケストラの指揮者のように中枢部に頼ってプロセスを推進します。コレオグラフィでは、バレエで周りの動きに合わせて自分音動きを決めるダンサーのように、システムの各部分にジョブを知らせ、詳細に対処させます。
    • コレオグラフィ手法では、代わりに顧客サービスに非同期でイベントを発行させ、「顧客を作成した」と通知することができます。それぞれのサービスはイベントをサブスクライブし、適切に対応します。この手法の方が大幅に分離されます。
    • 一般に、コレグラフィ手法に向かう傾向が強いシステムの方が、疎結合で柔軟性があり、変更を受け入れることがわかっています。しかし、システム境界にまたがるプロセスの監視と追跡には追加の作業が必要です。最も重いオーケストレーション実装は極めて脆弱であり、変更のコストが高くなることがわかっています。その点を考慮して、私は断然コレオグラフィシステムを目指します。
    • 一方、非同期のイベント連携はコレオグラフィ手法を採用にするのに役立ち、大幅に分離されたサービスを生み出せます。これは、サービスを独立して利リリースできるようにするために追求したい形態です。
  • 多くのRPC実装は隠し過ぎています。最悪の例では、抽象化があまりにも不透明な場合、開発者がそうと知らずにリモート呼び出しを使っていることもあります。
  • RESTで導入された、クライアントとサーバとの間の結合を避けるのに役立つ原則がもう1つあります。アプリケーション状態エンジンとしてのハイパーメディア(HATEOAS: Hypermedia As The Engine Of Application State)の概念です。
    • 欠点としては、クライアントがリンクをたどって実行したい操作を探す必要があるため、コントロールの異動では呼び出しが多くなります。結局、これはトレードオフです。
  • ワーカは競合コンシューマ(Competing Consumers)パターンを使い、処理するメッセージがなくなるまで各ワーカができるかぎり速くメッセージを取得していました。
    • ワーカが停止すると、リクエストのロックがタイムアウトし、リクエストがキューに戻されます。結局、別のワーカがそのリクエストを引き受けて停止するだけです。これは Martin Flower が「壊滅的フェイルオーバー」(Catastrophic Failover) と読んだ典型的な例でした。
  • 私が気に入っており、適切に機能しているのを見たことがあるモデルは、図4-10に示すようにこのようなバックエンドの仕様を特定のユーザインタフェースやアプリケーションに制限する方法です。このパターンは BFF: Backends for Frontends と呼ばれることもあります。
    • 補足: APIゲートウェイについて語っていて、デバイスの種別ごとに(モバイル、ウェブなど)ゲートウェイを作るのが良いと言っている
    • そもそもとしてゲートウェイが必要なのは、複数のAPIリクエストを1つに束ねるなどしたいため
  • BFFには、特定のユーザエクスペリエンスの提供に特化した振る舞いだけを追加すべきです。
    • バックエンドが使うさまざまな機能のビジネスロジックは、サービス自体の中にとどまるべきです。
  • ファサードサービスを使って基盤となる大きく恐ろしいCRM(Salesforceのような)を隠す
    • 基盤となるCRMを移行できるようにしておく
  • ストラングラーアプリケーションパターン(絞め殺しアプリケーションパターン)が便利です。CMSシステムの手前に自らのコードを配置する例と同様に、ストラングラーでは古いシステムへの呼び出しを補足してインターセプトします。これにより、呼び出しのを既存のレガシーコードにルーティングするか、自分で記述した新しいコードに向けるかを判断できます。
  • 合成監視
    • 補足: ピタゴラスイッチ的なシステムの end-to-end な監視。テストジョブをエンキューして期待通りに最後まで処理が行くか
  • フィーチャーチームとは、小規模なチームが一連の機能開発を推進し、たとえコンポーネント(さらにはサービス)境界を超えても、必要なすべての機能を実装するという考え方です。
    • 複数の異なるチームにまたがる変更を調整するという課題を避けられます
    • しかし、サービス管理者の役割はずっと複雑です。
  • たとえ制御できたとしても、ほとんど制御できないキャッシュが1つあります。それはユーザのブラウザのキャッシュです。
    • 補足: Expires: Never を食わせてしまい、URLを変えるしかなかった事案が紹介されていた
  • サーキットブレーカー: 下流リソースへの特定の数のリクエストが失敗すると、サーキットブレーカーが落ちます。サーキットブレーカーが落ちている間は、すべてのリクエストがすぐに失敗します。特定の時間の経過後、クライアントはリクエスト送信して下流サービスが復旧しているかを確認し、十分に健全なレスポンスを得たら、サーキットブレーカーをリセットします。
  • システムに分断耐性がなければ、ネットワーク上で動作できません。つまり、ローカルで動作する単一プロセスにならざるを得ません。CAシステムは分散システムでは存在しません。
  • swaggerでサービスの文書化
  • Consumer Driven Contract
  • Chaos Monkey
  • Suro
  • Riemann - Distributed Monitoring System

 

mini editor in ruby

I wrote a mini editor in ruby, kiro. kiro is actually a ruby port of antirez/kilo. kilo is a very interesting project for me because it is a small editor implementation written in only about 1K lines of codes. I've learned how to write an editor with this work.

In brief, what kilo was doing is:

  • Open STDIN in raw mode (ref. termios(3) - cfmakeraw, IO#raw is available in ruby)
  • Get window size using ioctl(1, TIOCGWINSZ, &ws) (io/console is available in ruby)
    • If it fails, write "\x1b[999C\x1b[999B" to STDOUT to go to right/bottom margin,
    • then get cursor position by writing "\x1b[6n" as it reports current position to STDIN
  • loop
    • refresh screen
    • process keypress
  • process keypress
    • Block (wait) until the user types anything
    • Store a character into data structure if the user types something
      • Sometimes append, sometimes insert, sometimes split one row to two rows (ENTER), delete
      • Syntax highlights characters in each row at here
  • refresh screen
    • POINT: Construct a buffer and write to STDOUT in a batch to avoid flickering effects
    • Append each line of syntax highlited characters into a buffer
      • sometimes cutoff characters if raw length overflows the window size
    • Pad ~ until bottom
    • Append status bar to the buffer
    • Then, write the buffer to STDOUT
  • Save
    • Dump the data structure into a file
  • Load
    • Construct the data structure from a file

My kiro codes are not rubyish, and not supporting syntax highlights yet. I may brush up if I have time and willingness.

CloudFrontをかますとキャッシュなしのAPIコールでも速くなるようだ

Slack 社の Secured API Acceleration with Engineers from Amazon CloudFront and Slack という資料を読んでいたら、Slack社のようなグルーバル企業において「CloudFront をはさんだらキャッシュしないAPIアクセスでも速くなった :D」 と書いてあった。しかし、CloudFront (例えばjp)から ELB (例えばus) までは依然として大きな latency があるわけで、本当に速くなるのか懐疑的だったので自分でも試してみた。

このスライドの11ページに速くなった理由は5つ書いてあり、以下のとおり。

  • (1) CloudFront Latency Based Routing
  • (2) TCP/IP Optimizations for the Network Path
  • (3) Keep-Alive Connections to reduce RTT
  • (4) AWS Backbone Network
  • (5) SSL/TLS Optimizations

(1) CloudFront Latency Based Routing については、日本のエッジロケーションを自動選択してくれるんだから、まぁそうだよね、という感じで、 (5) SSL/TLS Optimizations についても、距離が近くなればSSL確立まで速くなるのは、まぁわかる、という感じ。

(4) AWS Backbone Network については、なにかあるんだろうが、実際速くなるんだろうか、という気持ちで、 (2) TCP/IP Optimizations for the Network Path と (3) Keep-Alive Connections to reduce RTT については、何を指しているのかがスライドには書いてなくて、わからん、という感じ。試してみたほうが早いな、と。

やったこと

  • us-east-1 に t2.nano で Amazon Linux なインスタンスを立て、nginx-1.8.1-3.27.amzn1.x86_64 を yum install して空の index.html を返すようにしておく(素の設定のまま)
  • us-east-1 に ELB を立てて作ったインスタンスを追加しておく (ELB を作るのは、CloudFront が ELB か S3 しかオリジンに設定できないため)
  • CloudFront を立てて、ELB をオリジンに設定。キャッシュを一切しないように設定しておく

この状況で日本のオフィスから以下の ruby スクリプトを実行して、ELBを指定した場合とCloudFrontを指定した場合の時間を計測した。

エッジロケーションまでの距離が近くなればSSL確立の時間が短くなるのはわかっているので、あえてhttpsではなくhttpで計測して、(4) AWS Backbone Network にどれぐらいの効果があるのかを検証した。

require 'net/http'
fqdn = 'xxxxxx'

started = Time.now.to_f
# no keeepalive
10.times do
  Net::HTTP.get fqdn, '/index.html'
end
puts "#{(Time.now.to_f - started) / 10.0} sec / req"

結果

  • ELB: 0.405 sec / req
  • CloudFront: 0.267 sec / req

確かに速くなった。

DSA (Dynamic Site Acceleration) と呼ばれる技術体系

rebuildfm fastly の miyagawa さんに twitter で教えて頂いたが、fastly など他のCDNでも、エッジロケーション <=> オリジン間のネットワーク最適化は行っているそうで業界的には DSA と呼ばれる技術体系とのこと。TCP multiplexing や HTTP keep-alive したりしているようだ。参考:速度比較

まとめ

確かにキャッシュなしでも 

  • (2) TCP/IP Optimizations for the Network Path
  • (3) Keep-Alive Connections to reduce RTT
  • (4) AWS Backbone Network
といった DSA の技術により、1 HTTP リクエストにおける RTT が事実上 2/3 程度に短縮された。さらにSSL確立もCDNでやれば距離が近い分もっと速くなるので実用としてはもっと差がでるだろう。


ちょうど us リージョンに立てているけれど、日本からもアクセスされる API サーバがあるので、使ってみようかな。

蛇足

ap-northeast-1 の EC2、ELB に対して日本のオフィスから CloudFront 経由でアクセスした場合はどう?と聞かれたので、念のため実際に計測した結果を書いておく。物理的な距離はどちらもたいして変わらないので、結果もたいして変わらないだろうと予想。むしろ、CloudFront の1段が増えるので遅くなる可能性もありそう。


結果 

  • EC2直接: 0.030 sec / req
  • ELB: 0.063 sec / req
  • CloudFront: 0.064 sec / req

予想通り、たいして変わらなかった。

A Ruby and Fluentd committer working at DeNA. 記事本文および記事中のコード片は引用および特記あるものを除いてすべて修正BSDライセンスとします。 #ruby #fluentd #growthforecast #haikanko #yohoushi #specinfra #serverspec #focuslight
はてぶ人気エントリー