読書メモ: 「Designing Data-Intensive Applications」Chapter 1. Reliable, Scalable, and Maintainable Applications

感想

Chapter1は、本書のサブタイトルにもなっている、データシステム全体の非機能要件に関する用語(Reliability, Scalability, Maintainability)の定義をしている。 もうわかってるよととばしたくなるけれど、人に説明するときのリファレンス元として重宝する。 Figure 1-1の図のようなことをインフラと呼ぶこともあったけど、インフラが指す範囲が広すぎることとなんでもインフラがやることになってしまい、この層のシステムを指す用語として、データシステム、もしくはData-Intensive Applicationsと名をつけていることがよい。 Scalabilityに関するTwitterのタイムラインアーキテクチャの例が簡潔に紹介されていておもしろかった。

書籍「Designing Data-Intensive Applications」下読み - ゆううきメモ

メモ

今日のアプリケーションは、compute-intensiveに対してdata-intensiveである。 データベースやキュー、キャッシュを異なる別のカテゴリに所属すると捉えがちだが、「データシステム」という一つの傘の下にいると考えるべきではないか。 最近のデータストレージやデータ処理のためのツールは異なるユースケースをもつ。 そして、多くのアプリケーションには、単一のツールでは満たせない、幅広い要求がある。

f:id:y_uuki:20180404085516p:plain

データシステムのデザインには様々なファクターがあるが、ここでは大抵のソフトウェアにとって重要な「Reliability」、「Scalability」、「Maintainability」*1の3つの関心に着目する。

Reliability

Reliabilityの意味するところは、faultが発生してもシステムが"working correctly"であること。 "fault"は大抵ある一つのコンポーネントの故障を指す。"failure"はシステムが全体としてユーザ提供を停止することを指す。 ハードウェアフォールト、ソフトウェアエラー、ヒューマンエラーがある。 フォールトトーラレンス技術により、エンドユーザからこの種のfaultを隠すことができる。

Scalability

Scalabilityの意味するとことは、負荷が増大してもパフォーマンスを良好に保つための戦略をもっていること。(「もしシステムが特定の方向に成長するなら、その成長に対処する選択肢は何か?」と「追加の負荷を処理するためのコンピューティングリソースをどのようにして追加するのか?」という問いに答えられる状態) まず、負荷とパフォーマンスを定量的に表現する方法が必要となる。

Scalabilityのための戦略の例として、例えば、[16]のTwitterのタイムラインの例を考える。

  • ツイート投稿: ユーザは新しいメッセージをフォロワーに流せる (4.6k requests/sec on average, over 12k requests/sec at peak).
  • ホームタイムライン: ユーザはフォローしている人々が投稿したツイートを閲覧できる (300k requests/sec).

ツイート量だけみるとそれほどではないが、Twitterのスケールには"fan-out"が重要。以下の2つの実装が考えられる。

アプローチ 1.

グローバルなツイート集合に新しいツイートを挿入していく。ユーザがホームタイムラインリクエストをすると、自分のフォローのツイートを探索する。

SELECT tweets.*, users.* FROM tweets
JOIN users ON tweets.sender_id = users.id JOIN follows ON follows.followee_id = users.id WHERE follows.follower_id = current_user

f:id:y_uuki:20180404213018p:plain

アプローチ 2.

各ユーザのホームタイムラインのキャッシュをもつ。 f:id:y_uuki:20180404213023p:plain

初期のTwitterはアプローチ1.だったが、ホームタイムラインのクエリ負荷に耐えられず、アプローチ2へ移行した。アプローチ2は、書き込み時の負荷は読み込み時の負荷は小さくなる。ツイートの平均投稿レートがホームタイムラインの読み込みレートより2桁小さかったため、うまく動いた。 しかし、平均値では問題なくとも、3000万フォロワーを超える特定のユーザのツイート投稿時に、3000万回の書き込みが発生するという欠点がある。 最終的には、ハイブリッドアプローチをとっており、基本はアプローチ2をとりつつ、特定の巨大フォロワーユーザだけfan-outせず、アプローチ1を採用している。*2

Maintainability

Maintenabilityの本質は、エンジニアリングとオペレーションチームのために生活を良くすること。システムの複雑さを減らし、システムの変更を容易にし、新しいユースケースに対応できる。 以下の3つの設計原則がある。

  • Operability: オペレーションチームが、システムをなめらかに動作させ続けやくすること。
  • Simplicity: 複雑さを減らして、新しいエンジニアがシステムを理解しやすくすること。
  • Evolvability: 将来、エンジニアがシステムを変更しやすくすること。

興味深いリファレンス

  • [3] Ding Yuan, Yu Luo, Xin Zhuang, et al.: “Simple Testing Can Prevent Most Critical Failures: An Analysis of Production Failures in Distributed Data-Intensive Sys‐ tems,” at 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI), October 2014.
  • [11] Richard I. Cook: “How Complex Systems Fail,” Cognitive Technologies Laboratory, April 2000.
  • [16] Raffi Krikorian: “Timelines at Scale,” at QCon San Francisco, November 2012.
  • [18] Kelly Sommers: “After all that run around, what caused 500ms disk latency even when we replaced physical server?” twitter.com, November 13, 2014.
  • [21] Tammy Everts: “The Real Cost of Slow Time vs Downtime,” webperformancetoday.com, November 12, 2014.
  • [23] Tyler Treat: “Everything You Know About Latency Is Wrong,” bravenew‐ geek.com, December 12, 2015.
  • [24] Jeffrey Dean and Luiz André Barroso: “The Tail at Scale,” Communications of the ACM, volume 56, number 2, pages 74–80, February 2013. doi: 10.1145/2408776.2408794
  • [25] Graham Cormode, Vladislav Shkapenyuk, Divesh Srivastava, and Bojian Xu: “Forward Decay: A Practical Time Decay Model for Streaming Systems,” at 25th IEEE International Conference on Data Engineering (ICDE), March 2009.
  • [26] Ted Dunning and Otmar Ertl: “Computing Extremely Accurate Quantiles Using t-Digests,” github.com, March 2014.
  • [27] Gil Tene: “HdrHistogram,” hdrhistogram.org.
  • [28] Baron Schwartz: “Why Percentiles Don’t Work the Way You Think,” vividcortex.com, December 7, 2015.
  • [34] Hongyu Pei Breivold, Ivica Crnkovic, and Peter J. Eriksson: “Analyzing Software Evolvability,” at 32nd Annual IEEE International Computer Software and Applications Conference (COMPSAC), July 2008. doi:10.1109/COMPSAC.2008.50

*1:SREの考え方では、これらはすべて最終的にはReliabilityにつながるという考え方な気がする

*2:12章でこの例が再度でてくるとのこと

「喜嶋先生の静かな世界」

4年前に買ったのと同じ本棚が届いて、散らかった本を本棚に並べていると、以前になくしたと思っていた、「喜嶋先生の静かな世界」を発見して再読していた。

主人公は、研究室配属された大学生で、恩師である喜嶋先生との研究に没頭した生活が描かれている。短く言い切る文を連ねた文章で、ひとつひとつの描写ははっきりしている。ただし、全体的な読後感はどこか抽象的でさらりとしている不思議な読み味の物語だと感じさせる。

初読のときから記憶に残っているのは、物語の最後の一節だ。主人公は順調に博士号をとり、結婚し、そのまま大学に務め、助教授にまで出世する。そしてあるきっかけからふと以下のような独白を始める。

僕はどうだろう?

最近、研究をしているだろうか?

勉強しているだろうか?

そんな時間が、どこにあるだろう?

子供も大きくなり、日曜日は家族サービスで潰れてしまう。大学にいたって、つまらない雑事ばかりが押し寄せる。人事のこと、報告書のこと、カリキュラムのこと、入学試験のこと、大学改新のこと、選挙、委員会、会議、会議、そして、書類、書類、書類......。

いつから僕は研究者を辞めたのだろう? <中略>

僕はもう純粋な研究者ではない。

僕はもう......。

一日中、たった一つの微分方程式を睨んでいたんだ。

あの素敵な時間は、いったいどこへいったのだろう?

喜嶋先生と話した、

あの壮大な、

純粋な、

綺麗な、

解析モデルは、今、誰が考えているのだろうか?

世界のどこかで、僕よりも若い誰かが、同じことで悩んでいるのだろうか。

もしそうなら、

僕は、その人が羨ましい。

その人は幸せだ。

気づいているだろうか。教えてあげたい。

そんな幸せなことはないのだよ、と。

もう......、

もう二度と......、

もう二度と、あんな楽しい時間は訪れないだろう。

もう二度と、あんな素晴らしい発想は生まれないだろう。

僕からは、生まれないだろう。

僕からは......。

僕は......。

今の僕は、王道から外れている。

エキセントリックだ。

外れてしまったのは、いつからだろう?

外れてしまったのは、どうしてだろう?

森博嗣著 「喜島先生の静かな世界」 講談社 2010年発行

研究者を技術者に置き換えてみるとハッとなる。

最近、コードを書いているだろうか。勉強しているだろうか。オフィスで会議ばかりしていないだろうか。自分にとっての王道とは何だろうか、王道から外れていないだろうか。

まだ大丈夫だとは思う。でも5年後はどうか。

本棚にひっそりと佇み、折りにふれて読み直させ、自問を促してくれる本。

GoバイナリのGitHubリリース: gobump + ghch + goxz + ghr

最近は、GoバイナリのGitHubリリースにgobump、ghch、goxz、ghrを利用している。これらのツールを組み合わせることで、repairableな形でリリース作業を自動化できる。

GoバイナリのGitHubリリースに一般的に必要な一連のプロセスを分解すると、下記のようになる。

各ステップを担当するミニマムなツールを組み合わせ、一連のプロセスを実行できる。*1

ghchとghrは特にGoに限らず汎用的に使える。 yuuki/lstf では実際に、gobump+ghch+goxz+ghrによりリリースを自動化している。

References

*1:同僚と友人が開発したツールでまかなえててすごい

Linuxサーバ上でホスト間コネクションを集約表示するツール lstf をつくった

概要

netstatssコマンドにより、あるホストと他のホストとのコネクションを一覧表示できる。しかし、Webシステムの場合、クライアントが並行接続するため、 同一ホストから複数のポートを介してコネクションを確立しているケースが多い。コネクション数が大きい場合は、1万以上のコネクションが表示され、ホスト間のコネクション状況を人間の目で概観することが難しかった。

そこで、同一ホストとのコネクションを集約表示し、コネクション状況を概観する 「lstf」 (「えるえすてぃーえふ」)コマンドをつくった。

github.com

lstfの特徴は以下の通り。

  • コマンド実行ホストを起点に、active openコネクションかpassive openコネクションを判定する。つまり、接続をする側かされる側かを判定する。
  • 各ホストフローごとにコネクション数を表示する
  • Goで実装されているポータビリティ。i386バイナリであれば、CentOS5でも動作する。*1
  • JSONサポート

実行結果

コマンド実行結果をみてみよう。

$ lstf -n
Local Address:Port   <-->   Peer Address:Port     Connections
10.0.1.9:many        -->    10.0.1.10:3306        22
10.0.1.9:many        -->    10.0.1.11:3306        14
10.0.2.10:22         <--    192.168.10.10:many    1
10.0.1.9:80          <--    10.0.2.13:many        120
10.0.1.9:80          <--    10.0.2.14:many        202

ローカルホスト(10.0.1.9)はWebサーバでポート80番で待ち受けており、10.0.2.13と10.0.2.14からHTTPリクエストを受け付けていることがわかる。逆にローカルホストから10.0.1.10と10.0.1.11のMySQLサーバのポート3306番へ接続していることがわかる。ホスト同士の接続状況を知るためだけであれば、クライアントソケットが利用するポート番号を表示する意味はないため、manyとして集約している。

--jsonオプションでJSON表示もできるため、他のツールと連携し、利用できる。

$ lstf -n --json | jq -r -M '.'
[
  {
    "direction": "active",
    "local": {
      "Addr": "localhost",
      "Port": "many"
    },
    "peer": {
      "addr": "10.0.100.1",
      "port": "3306"
    },
    "connections": 20
  },
  {
    "direction": "passive",
    "local": {
      "addr": "localhost",
      "port": "80"
    },
    "peer": {
      "addr": "10.0.200.1",
      "port": "many"
    },
    "connections": 27
  },
  ...
]

実装

/proc/net/tcpをパースする部分は、GitHub - shirou/gopsutil: psutil for golang を使わせてもらってる。 active openかpassive openかの判定は簡単で、コマンド実行ホストのLISTENポートに対するコネクションをpassive open、それ以外をactive openとしている。 *2

おまけ

Linuxのnetfilterには、conntrackというL4のコネクションフローに関するパケットを追跡する機構がある。(/proc/net/ipconntrack, /proc/net/nfconntrack からdump結果が読める) これを利用して、同じようにコネクションフローを集約表示するツールも作ってみた。github.com inboundとoutboundとそれぞれについて、各パケットのサイズを合計し、conntrack tableに存在するフローごとのトラフィック量とパケット数を表示できるというメリットはある。 しかし、コネクション数が大きいホストではconntrack tableあふれを避けるために、conntrackを無効にしているため、どのホストでも使えるわけではなかった。

distributed black-box tracingをするための部品づくりを最近やってる。

追記

Linuxサーバ上でホスト間コネクションを集約表示するツール lstf をつくった - ゆううきメモ

とても良さそう。コネクション数が多い場合は ss のように netlink 経由のほうが良いと思うがどうなんだろ

2018/03/25 16:57
b.hatena.ne.jp

コマンド実行速度の観点で言えば、/proc/net/tcpをパースするより、netlinkのほうが有利なようです。*3 GitHub - vishvananda/netlink: Simple netlink library for go. を使えば、わりと簡単にnetlink対応できそうですが、レガシーOSに対応しなければいけない事情*4でポータビリティの確保を優先し、ファイルをパースする実装を選んでいます。

*1:CentOS5でも古いバージョンでは動作しない

*2:https://github.com/yuuki/lstf/blob/d65651e9c3d1956aa9db88812acb6199a2473235/tcpflow/tcpflow.go#L130-L142

*3:参考:netstatコマンドを高速化する - Qiita posix_fadvise(2)でPOSIX_FADV_SEQUENTIALを使えばもうちょいread(2)の回数減らせないかなと思ったけど、それも込みでページサイズ固定ということかもしれない。

*4:同僚調べによるとnetlinkパッケージはCentOS5でサポートされていないシステムコールオプションを使っている。Go処理系そのものがCentOS5をサポートしていないため、仕方がない。cgo依存は排除されている様子。Eliminate cgo from netlink. by hugelgupf · Pull Request #308 · vishvananda/netlink · GitHub

LinuxサーバでネットワークI/Oで刺さっている接続先を発見する

Linuxサーバの障害対応で社内で伝統的に使われているテクニック。I/Oで完全にブロックしているポイントを特定するノウハウ。

  • 問題対応のため、怪しいプロセスをstraceしてみる
  • read(2)やwrite(2)でブロックしていることを発見する
  • read(2)やwrite(2)、connect(2)の引数にはファイルディスクリプタ番号がみえる
  • プロセスIDとファイルディスクリプタ番号を使って、/proc//fd/ の中身をみると、ソケットI/Oで刺さっている場合はソケット番号を発見できる
  • netstat からソケット番号でgrepして接続先を発見する
[y_uuki@hogehoge ~]$ sudo strace -p 10471
Process 10471 attached - interrupt to quit
read(58,  <unfinished ...>
Process 10471 detached
[y_uuki@hogehoge ~]$ sudo readlink /proc/10471/fd/58
socket:[1148032788]
[y_uuki@hogehoge ~]$ netstat -ane | grep 1148032788
tcp        0      0 10.0.0.10:44566            10.0.0.11:3306           ESTABLISHED 48         1148032788

IPアドレス 10.0.0.11 に対する3306番ポート(MySQL)の接続で詰まっていることがわかる。

社内のwikiによると、10年ぐらい前から使われている。現在のメンバーは /procを直接見ずに、straceしてからlsof -i -a -p <pid> などを使っているかもしれない。 networking - How do I find out more about socket files in /proc/fd? - Unix & Linux Stack Exchange