2015年Webサーバアーキテクチャ序論

2023年03月31日追記:この記事を基に、@sadnessOjisanさんより、コードレベルにより踏み込んだ、かつ、グリーンスレッドベースの新しいWebサーバアーキテクチャも含めて整理された記事 Webサーバーアーキテクチャ進化論2023 | blog.ojisan.io が公開されました。

主に新卒のWebエンジニア向けに、古典的なWebサーバアーキテクチャを学ぶ道のりと代表的な実装モデルの概要を紹介します。

この辺りの話題がWeb界隈で流行っていたのは数年以上前というイメージですが、Webサービスは相変わらずWebサーバの上で動いているので、流行り廃り関係なく学ぶべき内容だと思っています。

また、HTTP/2がいよいよRFC化し、既にh2otrusterdなどのHTTP/2のサーバ実装があり、今後Webサーバアーキテクチャを再訪することが増えるような気がしています。

ところが、Webサーバアーキテクチャを学ぼうとすると、情報は古いものから新しいものまで山のようにあり、まさに海のものとも山のものともつかない、ググる度に翻弄されているという若者には厳しい状況があり、一旦自分の中でまとめてみようと思いました。

序論ということで、Webサーバアーキテクチャの基本を俯瞰できるということと、より発展的な話題へ進んでいくためのルーターのような役割を目指しています。

続きを読む

Mackerelを支える時系列データベース技術

【追記 2018/01/06】現在Mackerelは、時系列データベースという概念をクラウドの技で再構築する - ゆううきブログの時系列データベース実装へ移行しています。

サーバモニタリングサービス Mackerel で採用している時系列データベース Graphite を用いたシステムの構築と運用事情を紹介します。Graphiteについては、プロビジョニングやアプリケーションからの使い方、Graphite自体のモニタリングなど様々なトピックがありますが、特に大規模ならではのトピックとして、Graphiteの内部アーキテクチャ、パフォーマンスチューニングおよびクラスタ構成についての知見を書きます。

続きを読む

Linuxでロードバランサやキャッシュサーバをマルチコアスケールさせるためのカーネルチューニング

本記事の公開後の2016年7月にはてなにおけるチューニング事例を紹介した。 はてなにおけるLinuxネットワークスタックパフォーマンス改善 / Linux network performance improvement at hatena - Speaker Deck

HAProxy や nginx などのソフトウェアロードバランサやリバースプロキシ、memcached などの KVS のような高パケットレートになりやすいネットワークアプリケーションにおいて、単一の CPU コアに負荷が偏り、マルチコアスケールしないことがあります。 今回は、このようなネットワークアプリケーションにおいて CPU 負荷がマルチコアスケールしない理由と、マルチコアスケールさせるための Linux カーネルのネットワークスタックのチューニング手法として RFS (Receive Flow Steering) を紹介します。

Redis や Nodejs のような1プロセス1スレッドで動作するアプリケーションをマルチコアスケールさせるような話ではありませんのでご注意ください。

続きを読む

Linuxのブロックデバイスレベルで実現するrsyncより高速な差分バックアップについて

社内で論文輪読会みたいなことやってて、そこで紹介した論文の内容についてです。

最近、Graphite に保存しているデータのバックアップ(データ同期)に rsync 使ってて、かなり遅いので困ってた。 LISA っていう 大規模システム、sysadmin 系のカンファレンスがあって、ここから論文探してたら、ちょうど巨大データの高速バックアップの実装の話があったので読んでみた。

論文概要

dsync: Efficient Block-wise Synchronization of Multi-Gigabyte Binary Data - https://www.usenix.org/conference/lisa13/technical-sessions/presentation/knauth - Thomas Knauth and Christof Fetzer, Technische Universität Dresden - In Proceedings of the 27th Large Installation System Administration Conference (LISA ’13)

GB単位のデータだと、rsyncがとにかく遅い。 rsync はブロック分割されたデータのハッシュ値の比較により差分検知して、ネットワーク転送するデータ量をとにかく減らそうとしている。 その反面、同期させたいデータサイズに対してハッシュ値計算のCPU時間は線形に増加する。 さらに、ハッシュ値計算のために全データブロックを読み出す必要があり、I/Oコストも高い。 バックアップ時に、差分を事後計算しようとすると、どうしてもこうなる。 そこで発想を変えて、カーネルのブロックデバイスレベルで、更新のあったブロックを常に記録(トラッキング)しておき、バックアップ時にフラグのあったブロックだけを転送することにする。

発想自体はシンプルだけど、既存の device mapper の Snapshot 機能だけでは実現できなくて、パッチをあてる必要があったのがやや難点。 カーネルのメインラインに取り込まれてほしい。

実装はこちら。 https://bitbucket.org/tknauth/devicemapper/ Linuxカーネル 3.2 の device mapper モジュールにパッチを当てるような感じになってそう。

追記 2014/05/26 16:10

それ ZFS でできるよと DRBD でいいのでは系のコメントをいくつかいただきました。 ちなみに論文の本文には両者に関して言及があります。

"どうせブロックデバイス使うなら実績のあるDRBDでいいんじゃないかと思う。非同期モードで。" http://b.hatena.ne.jp/n314/20140526#bookmark-196769866

ZFS について

ブロックデバイスレベルで実現できてうれしい点は、ZFS など特殊なファイルシステムに依存しないことだと思います。 ext3 のような壊れにくいLinux環境で実績と運用ノウハウがある ファイルシステムを使いつつ、差分バックアップできるのは実運用上でうれしいことが結構あると思います。 あとは、ZFS で定期的に差分バックアップとるときに、スナップショット分のディスクサイズオーバヘッドが気になります。(これは論文にも比較データはないのでどれほどかわかりませんが) ちなみに dsync のオーバヘッドは、10 TiB データに対して、320 MiB 程度のようです。(ただしディスクではなくメモリオーバヘッド)

DRBD について

そもそも非同期モードだろうが同期モードだろうが、DRBD はオンラインなレプリケーションには使えても、定期バックアップに使うものではない気がします。 例えば、プライマリノードで、間違えて重要なファイルを rm してしまったときに、即座にセカンダリノードにその rm の結果が伝播してしまうので、データが壊れた時に復旧するためのバックアップ用途には向いてない気がします。

何か勘違いがありましたらご指摘いただけると幸いです。

スライド

Keynote のテンプレートは、Azusa にお世話になっています。

LISA の他の論文

LISA はオペレーションエンジニアにとって興味深い論文が結構ある。 一例をあげてみる。 今回のように実装が公開されてたいたりするので、あんまりアカデミック感がなくてよい。

所感

たまにサーバ管理ツールとか作ってて、サーバ負荷の未来予測とかサービス間のトラッフィク依存関係(AサービスがBサービスのDB引いてるとか)を可視化できたらいいねとか言ってたりしてた。 今回、LISAの論文眺めてたらちょうどそういうのあって驚きがあった。 研究の世界はできないことができるようになる系統の技術において先を行っていて、ブログとかウォッチしているだけでは追いつけないので、たまに論文もよみたい。

カーネルのI/Oシステム周りの知識に乏しいので、詳解Linuxカーネルを読みつつ知識を補填していた。

詳解 Linuxカーネル 第3版

詳解 Linuxカーネル 第3版

  • 作者: Daniel P. Bovet,Marco Cesati,高橋浩和,杉田由美子,清水正明,高杉昌督,平松雅巳,安井隆宏
  • 出版社/メーカー: オライリー・ジャパン
  • 発売日: 2007/02/26
  • メディア: 大型本
  • 購入: 9人 クリック: 269回
  • この商品を含むブログ (71件) を見る

超高速なパケットI/Oフレームワーク netmap について

の続きで,最近論文読んだやつのプロジェクトの紹介です.

概要

今の汎用OSは高速なパケットI/Oを考慮してない.
20年前のAPIをそのまま使っている.
ネットワークがどんどん高速になっているので,NICとかOSカーネルのパケット処理がボトルネックになってる. (http://news.mynavi.jp/news/2013/04/04/094/index.html)

こういうの解決するために既存手法がいろいろある.

netmapはその試みのひとつ.

  • ユーザプロセス-NIC間の効率のよいパケットI/Oフレームワーク
  • 汎用的な10Gbps環境で14.88Mppsものスループット
  • 60-65 クロックサイクルでwire - userspace間のデータ移動
  • 標準的なデバイスドライバと比較して10-20倍の性能向上
  • FreeBSDにはもう取り込まれている ([base] Revision 227614)

Usenix ATC'12でベストアワード,SIGCOMM 2011でベストアワードを受賞してるらしい.やばそう.

アーキテクチャ

f:id:y_uuki:20130803161153p:plain

netmapは既存の性能向上手法をいくつか使っている.
NICのパケットバッファの(mmapとかで)メモリマッピング,I/Oバッチング,NICのリングキューとマッチするような送信・受信キューのモデリングなど.

既存のものとは違うのはnatmapを使うユーザプロセスのアプリケーションがOSをクラッシュさせることができないようになっていること.
natmapのクライアントはユーザ空間で動作するため,デバイスレジスタやカーネルメモリポインタにダイレクトアクセスできない.

プログラミングモデルは固定長バッファのリングキューを扱うだけなので非常に単純で,アプリケーションは標準的なシステムコールしか使わない.
(ノンブロッキングioctl()でNICと同期,poll()可能なファイルディスクリプタ)

パフォーマンス

f:id:y_uuki:20130803161216p:plain

netmapはCPUのクロックレートが1GHz以下で10Gbpsの上限に達してる.
pktgen(Linuxのカーネルで動作するパケットジェネレータ)やnetsend(UDPを使ったユーザランドで動作するパケットジェネレータ)よりもかなり高いスループットがでてる.

インタフェース

サンプルコードが書いてあった.
/dev/netmapで開いたディスクリプタに対してioctl()とmmap()でリングキューやバッファのメモリ領域を取得して,NETMAP_xxxなインタフェースでリングに対する操作とバッファの中身を読めるイメージ.

重要なのは,1パケットごとにpollとかするんじゃなくて,リングの複数スロットにまとめて1回のシステムコールを発行するところ.
これにより,パケット単位のシステムコールのオーバヘッドを削減できる.

struct netmap_if *nifp;
struct nmreq req;
int i, len;
char *buf;

fd = open("/dev/netmap", 0);
strcpy(req.nr_name, "ix0"); // register the interface
ioctl(fd, NIOCREG, &req); // offset of the structure
mem = mmap(NULL, req.nr_memsize, PROT_READ|PROT_WRITE, 0, fd, 0);
nifp = NETMAP_IF(mem, req.nr_offset);
for (;;) {
    struct pollfd x[1];
    struct netmap_ring *ring = NETMAP_RX_RING(nifp, 0);

    x[0].fd = fd;
    x[0].events = POLLIN;
    poll(x, 1, 1000);
    for ( ; ring->avail > 0 ; ring->avail--) {
        i = ring->cur;
        buf = NETMAP_BUF(ring, i);
        use_data(buf, ring->slot[i].len);
        ring->cur = NETMAP_NEXT(ring, i);
    }
}

ソースコード

システムコール(ioctl,select/poll)に対するパッチとNICドライバのパッチを除いて,現行バージョンは約2000行程度らしい.
さらに,各デバイスドライバのパッチはそれぞれ500行程度で, デバイスドライバの変更を最小にするために機能のほとんどが各ドライバで共通したコードで実装されている.

デバイスドライバパッチのソースをちょっと読んでみた感じだと,リングキューの初期化処理と同期処理(netmap自前のリングとNIC自体のリング)を書けばよいだけみたいだった. 同期処理はちょっと書くのめんどくさそうだった.

資料

あんまり咀嚼して書けてない.

感想

正直,アーキテクチャについてはよくわからない部分が結構あったけど,サンプルコードみたらだいたいイメージ湧いてきた. もうちょっと既存研究あたったほうがよさそう.

netmapで遊ぼうとおもったけど,持ってるNICがMellanox製で,netmap対応ドライバがまだないからつらい. 自分でドライバパッチ書けってことか…

Linuxカーネルにおけるネットワークスタック周りのChangeLog勉強メモ (2.6.0 ~ 2.6.20)

最近,OSのネットワークスタックに興味があって,Linuxカーネルのネットワークスタック実装の変遷について調べてみた.
変更内容の粒度は割りと適当.

TCPとかFireWallとかNIC周りは興味があるのでだいたい書いてる.
Wireless系は興味ないので全部削ってる.

ネットワークスタック周りだけでもかなり量が多かったので,とりあえず2.6.0から2.6.20までをまとめた.

Linux 2.6.x

2.6.5 (2004/04/04)

http://kernelnewbies.org/Linux_2_6_5

  • Netpoll infrastructure

Netpollが導入された.
Netpollは,I/Oシステムとネットワークシステムが完全には利用できないみたいな不安定な環境でカーネルがパケットを送受信できる仕組みのこと.受信したパケットがnetpoll宛かどうかをTCP/IPのヘッダレベルでNICが判定し,カーネルの受信キューにパケットを積む前にNICが受信処理を行う.
組み込みデバイス環境で使ったりするらしい.
(see http://www.spa.is.uec.ac.jp/research/2006/hama/main.pdf)

2.6.6 (2004/05/10)

http://www.kernel.org/pub/linux/kernel/v2.6/ChangeLog-2.6.6

  • Network packet timestamping optimization

  • Binary Increase Control (BIC) TCP developed by NCSU. It is yet another TCP congestion control algorithm for handling big fat pipes. For normal size congestion windows it behaves the same as existing TCP Reno, but when window is large it uses additive increase to ensure fairness and when window is small it

TCPの輻輳制御アルゴリズムにBICが追加された.
BICについては前回のブログに少し書いてる."CUBIC: A new TCP-friendly high-speed TCP variant"を読んだ

  • IPv6 support in SELinux

  • A mechanism which allows block drivers to respond to queries about the congestion state of their queues

続きを読む