ウェブシステムの運用自律化に向けた構想 - 第3回ウェブサイエンス研究会

はてなエンジニア Advent Calendar 2017の2日目です。 昨日は、id:syou6162 さんによるAWS Lambda上で鯖(Mackerel)の曖昧性問題を機械学習で解決しよう - yasuhisa's blogでした。

この記事は、人工知能学会 合同研究会2017 第3回ウェブサイエンス研究会の招待講演の内容を加筆修正したものです。 講演のテーマは、「自然現象としてのウェブ」ということでそれに合わせて、「自然のごとく複雑化したウェブシステムの運用自律化に向けて」というタイトルで講演しました。 一応、他の情報科学の分野の研究者や技術者に向けて書いているつもりですが、その意図がうまく反映されているかはわかりません。

概要

ウェブシステムの運用とは、信頼性を制約条件として、費用を最小にする最適化問題であると考えています。 費用を最小にするには、システム管理者の手を離れ、システムが自律的に動作し続けることが理想です。 クラウドやコンテナ技術の台頭により、ウェブシステム運用技術の自動化が進んでおり、自律化について考える時期になってきたと感じています。 自律化のために、観測と実験による「Experimentable Infrastructure」という構想を練っています。 Experimentable Infrastructureでは、監視を超えた観測器の発達、実験による制御理論の安全な導入を目指しています。

1. ウェブシステムの信頼性を守る仕事

ここでのウェブシステムとは、ウェブサービスを構成する要素と要素のつながりを指しており、技術要素とは「ブラウザ」「インターネットバックボーン」「ウェブサーバ」「データベース」「データセンター内ネットワーク」などのことを指します。 特に、データセンター内のサーバ・ネットワークおよびその上で動作するウェブアプリケーションを指すことがほとんどです。 総体としてのウェブというよりは、単一組織内の技術階層としてのシステムに焦点をあてています。

ウェブシステムの最も基本的な機能として、「信頼性」があります。 信頼性を守る役割は「Site Reliability Engineer(SRE)」が担います。 SREはウェブ技術者界隈で市民権を得ている概念であり、Googleのエンジニアたちによって書かれた書籍「Site Reliability Engineering」[Bet17]に詳細が記されています。

信頼性にはいくらかの定義のしようがあります。 [Bet17]では[Oco12]の定義である「システムが求められる機能を、定められた条件の下で、定められた期間にわたり、障害を起こすことなく実行する確率」を採用しています。 信頼性というとつい100%を目指したくなりますが、信頼性と費用はトレードオフなため、信頼性を最大化するということはあえてしません。

さらに、信頼性をたんに担保することが仕事なのではなく、費用を最小化することが求められます。 この記事では、費用=コンピューティングリソース費用 + 人件費用 + 機会費用 *1としています。 SREは費用をエンジニアリングにより削減できます。例えば、コンピューティングリソース費用はソフトウェアの実行効率化、人件費用と機会費用はソフトウェア自動化などにより削減できます。 以上より、SREの仕事のメンタルモデルは、「目標設定された信頼性*2 *3を解くことであると言い換えられると考えます。 特にSREでは、定型作業(トイルと呼ばれる)を自動化し、スケールさせづらい人間ではなくコンピュータをスケールさせることが重要な仕事となります。

一般の人々からみれば、ウェブシステムは、十分自律動作していると言えます。 内部的に障害が発生したとしても、人々は何もせずとも、障害から回復し普段通りサービスを利用できます。 ただし、その裏では、信頼性を担保するために、数多くの人間の手作業や判断が要求されているのが現実です。 人間がなにもしなければ、およそ1週間程度*4で信頼性は損なわれることもあります。

我々のゴールは、最終的にコンピュータのみでシステムを運用可能な状態にすることです。 IOTS2016の開催趣旨が、このゴールを端的に言い表しています。

インターネット上では多種多様なサービスが提供されている。このサービスを提供し続け、ユーザに届けることを運用と呼ぶ。サービスが複雑・多様化すれば運用コストは肥大する。このコストを運用担当者の人的犠牲により「なんとか」してしまうことが「運用でカバーする」と揶揄される。日本では大規模構造物を建造する際に、破壊されないことを祈願して人身御供 (人柱) を捧げる伝統があるが、現在のインターネットの一部はこのような運用担当者の人柱の上に成立する前時代的で野蛮な構造物と言うことができる。

運用担当者を人柱となることから救う方法の一つが運用自動化である。運用の制御構造における閾値を明らかにすることにより、人は機械にその仕事を委託することができる。機械学習や深層学習により、この閾値を明らかにすることをも機械に委託することが可能となることも期待される。不快な卑しい仕事をやる必要がなくなるのは、人間にとってひじょうな福祉かもしれないが、あるいはそうでないかもしれない。しかし機械に仕事を委託することにより空いた時間を人間が他のことに使うことができるのは事実である。

本シンポジウムは、インターネットやネットワークのサービスの運用の定量的な評価を通じて、積極的に制御構造を計算機に委託することで人間の生産性を向上させ社会全体の収穫加速に結びつけることを目的とする。

IOTS2016 開催の趣旨より引用 http://www.iot.ipsj.or.jp/iots/2016/announcement

2. ウェブシステム運用の現状

国内のウェブシステムの運用技術の変遷

2010年以前は自作サーバ時代[rx709]でした。 秋葉原でパーツを購買し、組み立てたサーバをラックに手作業で配置していました。 自作だと壊れやすいこともあり、冗長化機構やスケールアウト機構など信頼性を高める基礎機能が浸透しました。[ito08] のちにベンダー製サーバやクラウドへ移行が進みます。 このころから、XenやKVMなどのハイパーバイザ型のサーバ仮想化技術の台頭により、徐々にサーバをモノからデータとして扱う流れができはじめます。

クラウド時代

2011年にAWS東京リージョンが開設され[awshis]、有名ウェブ企業が移行しはじめます[coo15]。 クラウドの利用により、必要なときにすぐにハードウェアリソースが手に入り、従来の日または月単位のリードタイムが一気に分単位になりました。 さらに、単にハードウェアリソースが手に入るだけでなく、クラウドのAPIを用いて、サーバやデータベースをプログラムから作成することが可能になりました。 これを利用し、負荷に応じて自動でサーバを増やすなどの動的なインフラストラクチャを構築できるようになってきました。

コンテナ型仮想化技術

2013年のDocker[doc13]の登場により、ソフトウェアの動作環境を丸ごとパッケージ化し、さまざまな環境に配布できるようになりました。 コンテナ型仮想化技術そのものは古くから存在しますが、Dockerはコンテナの新しい使い方を提示しました。 コンテナ環境は、ハイパーバイザ環境と比べてより高速に起動する*5ため、より動的なインフラストラクチャの構築が可能になりました。

この頃より、Immutable Infrastructure[imm13][miz13][mir13][sta13]またはDisposable Infrastructureの概念が登場し、サーバを使い捨てるという発想がでてきました。

サーバレスアーキテクチャ

2015年ごろから、サーバレスアーキテクチャ[mar16] [nek16]という概念が登場しました。 これは本当にサーバがないわけではなく、サーバの存在を意識しなくてよいような状態へもっていくためのアーキテクチャと技術選択を指します。

サーバレスアーキテクチャでは、具体的なサーバやコンテナの存在を意識することなく、抽象化された複数のサービスを組み合わせて、ビジネスロジックを実装するようになります。 ここでのサービスは、フルマネージドサービスと呼ばれることが多く、データベースサービス(Amazon DynamoDB、Google BigQueryなど)、CDNサービス(Akamai、Amazon Cloudfrontなど)、Functionサービス(AWS Lambda、Google Function)などを指します。 理想的なフルマネージドサービスは、裏側にあるサーバの個数や性能ではなく、APIの呼び出し回数や実行時間、データ転送量といったよりアプリケーションに近い単位でスケーリングし、課金されます。 ウェブサービス事業者がこれらの抽象層を実装するというよりは、クラウドベンダーがサービスとして提供しているものを利用することがほとんどです。

Site Reliability Engineering(SRE)の登場

2015年から日本のウェブ業界にSREの概念が浸透し始めました[mer15]。 SREにより、ウェブシステムの「信頼性」を担保するエンジニアリングという、何をする技術やエンジニアなのかがはっきり定義されました。 それまでは、システム管理者、インフラエンジニアや下回りといったざっくりした用語で何をする技術で何をする人なのかが曖昧な状態であることに気付かされました。 トレードオフである信頼性を損なわずに変更の速度の最大化する*6上で、ソフトウェアエンジニアリングを特に重視しているのも特徴的です。

SRE自体は技術そのものではなく、SREによりウェブシステムの運用分野に、体系的な組織開発・組織運用の概念が持ち込まれたことが、大きな変化だと考えています。

変遷まとめ

昔はハードウェアを調達し、手作業でラッキングしていました。 現在では、サーバの仮想化技術の発達とアプリケーション動作環境パッケージングの概念、さらにサーバレスアーキテクチャの考え方とサービスの浸透により、よりダイナミックで抽象的なインフラストラクチャが構築可能になりました。 このように、インフラストラクチャがソフトウェアとして扱いやすくなり、ソフトウェアエンジニアリングを重視した組織文化の浸透と合わさり、この10年でソフトウェアによる運用自動化が大幅に進んできたと言えます。

しかし、これでめでたしめでたしかというとそういうわけではありません。

3. ウェブシステム運用の課題

本当は怖いウェブシステム運用

このテーマについては、今年のIPSJ-ONEにて「高度に発達したシステムの異常は神の怒りと見分けがつかない」[yuu17]で話しました。

ウェブシステムでは、異常が発生しても原因がわからないことがあります。 原因がわからず再現させることも難しい場合、システムの振る舞いがまるで「自然現象」に感じられます。*7 自動化が進んでいるからこそ、不明であることが異常に対する「恐怖」をうみます。 これは、システムに対する変更が安全かどうかを保証することは難しいためです。 その結果、振る舞いの解明に多くの時間をとられたり、わざと自動化を避けて、人間の判断をいれようとします。

実際、異常のパターンは様々です。 講演では、以下の2つの例を話しました。

  • ハードウェアのキャパシティにはまだ余裕があるにもかかわらず、1台あたりの処理能力が頭打ちになり、自動スケールするが自動スケールしない別のコンポーネントが詰まるケース
  • データベース*8のアクティブ・スタンバイ構成*9において、ネットワーク分断により、確率的にどちらかのマスターにデータが書き込まれ、データの一貫性を損失するケース

前者は、自動化していても自動で復旧できず、後者は自動化したがゆえに新たな問題が発生したケースです。 書籍「Infrastructure As Code」[kie17]では、1.3.5節 オートメーション恐怖症にて、"オートメーションツールがどういう結果を生むかについて自信が持てないため、オートメーションツールに任せきりになるのは怖かった。"と書かれています。

ウェブシステムの複雑性

ここまできて、なぜウェブシステムは自然現象のように感じられるか、複雑さの要因はなにかについて、体系的に整理し、分析しようと真剣に考えたことはありませんでした。 そのための最初の試みとして、自分の経験を基に以下の3点を挙げてみます。

  • ソフトウェア依存関係の複雑さ
  • 分散システムとしての複雑さ
  • 入力パターンの複雑さ

ソフトウェア依存関係の複雑さ

ウェブシステムは、多数のソフトウェアの重ね合わせにより構成されます。 言語処理系、OS、ドライバ、共有ライブラリ、ミドルウェア、アプリケーションライブラリ、アプリケーションなどです。 さらに、これらがネットワーク越しに接続され、さまざまなプロトコルにより通信します。

このような状況では、ソフトウェアの依存関係や組み合わせの問題が発生します。 具体的にはバージョンアップ問題やプロトコル互換問題、依存地獄問題[yuu15]などを指します。

例えば、あるソフトウェアをバージョンアップすると、そのソフトウェアに依存したソフトウェアが動作しなくなることがあります。 データベースのバージョンアップにより、プロトコルなどの仕様変更に追従していないアプリケーションが動作しなくなるなどは典型的な例でしょう。 動作したとしても、性能が低下し障害につながるということもあります。

分散システムとしての複雑さ

信頼性のあるウェブシステムは、基本的に分散システムとして構成されています。 複数のノードが相互に通信するということは、単純にノードやリンクが増加すればするほど、システムとしては複雑になります。 さらに、分散システムは、ハードウェア故障、ネットワークの切断・遅延といった物理的制約がある中で、信頼性を担保しなければいけません。 部分的な故障を許容しつつ、自動でリカバリする仕組みは複雑です。 分散システムの難しさについて書かれた文献として、「本当は恐ろしい分散システムの話」[kum17]が非常に詳しいです。

入力パターンとしての複雑さ

ウェブシステムの入力パターン(ワークロード)は一定でもなければ、ランダムでもないことがほとんどです。 ただし、サービスの特性により、朝はアクセスは少ないが、夜に向けてアクセスが徐々に増加するといった一定の傾向はあります。 大まかな予測はできることがあるものの、むずかしい。 人間、検索クローラ、スパマーなどの活動に応じてシステムへの入力パターンは突発的に変化します。これは、制御工学でいうところの外乱に相当します。 実際、この突発的な変化が障害の原因になることはよくあります。

入力パターンの突発的変化(外乱)は、人間や社会の変化によることもあるため、システム側での予測は困難です。

システムの複雑さの度合い

複雑さを分析するのは、要因に加えて度合いの評価も必要です。 度合いについてもせいぜいサーバ台数やサービス数程度でしかみてきませんでした。

そもそも一般的に複雑さをどう定義しているかについて、複雑性科学の書籍[mel11]を参考にしました。 [mel11]には、複雑さについての一般的な定義はなく、これまでいくつかの指標が提案されたことが書かれていました。 提案された指標は、サイズ、エントロピー、アルゴリズム情報量、論理深度、熱力学深度、計算能力、統計的な複雑性、フラクタル次元、階層度がありました。 この中で、比較的ウェブシステムに当てはめやすいのは、サイズと階層度です。

前述の分散システムとしての複雑さに対して、サイズと階層度*10の概念を適応してみます。

サイズについては、例えば、はてなのシステムサイズは以下のようなものです。GoogleやAmazonであれば、おそらくこの100~1000倍の規模でしょう。

  • サービス数: 100+ (内部向け含む)
  • ロール数: 1000+
  • ホスト数: 1000+
  • プロセス/スレッド数: 10000+
  • SRE数*11: 10人弱

階層度を特に入れ子のレベルによって定義した場合、プログラム実行単位については10年前であれば例えば以下のようになります。

  • レベル1: プロセス/スレッド
  • レベル2: サーバ (複数のプロセスの集合体)
  • レベル3: ロール (クラスタやロードバランサ配下のサーバ群)
  • レベル4: サービス: (ロールまたはマイクロサービスの集合体)
  • レベル5: プラットフォーム: (複数のサービスの集合体)
  • レベル6: ウェブ

一方、現在であれば例えば以下のようになります。 ただし、サーバレスアーキテクチャを採用していれば、レベル1~3までは無視できる一方で、マイクロサービスなど層を増やす変化もあります。

  • レベル1: プロセス/スレッド
  • レベル2: コンテナ
  • レベル3: サーバ (複数のプロセスの集合体)
  • レベル4: ロール (クラスタやロードバランサ配下のサーバ群)
  • レベル5: マイクロサービス
  • レベル6: サービス: (ロールまたはマイクロサービスの集合体)
  • レベル7: プラットフォーム: (複数のサービスの集合体)
  • レベル8: ウェブ

実際には、複雑さを評価するためには、複雑さの要因ごとに複数の指標と複数の観点をもつ必要があると考えます。 ウェブシステムがどのような複雑さをもつのか、特に他分野の方に伝えるには、SREの分野では、蓄積が不足していると感じます。

4. ウェブシステムの自律運用へのアプローチ

ここまで、ウェブシステム運用技術の変遷とウェブシステムの複雑さについて書いてきました。 ここでは、これらを踏まえ、課題を解決するためのビジョンである観測と実験による「Experimentable Infrastructure」について述べます。

観測

前述の運用技術の進歩により、インフラストラクチャが抽象化され、プログマブルかつシンプルに扱えるようになってきました。 しかし、要素数を増やす方向へ技術が進んでいるため、依然としてシステム全体の挙動を人が理解することが難しいと感じます。 したがって、系全体の精緻な理解を助ける観測器が必要です。

ここでの観測とは、ウェブシステムの「過去と現在」の状況を人間またはコンピュータが自動的かつ継続的に把握することです。 20年近く前からサーバ・ネットワークを「監視」するためのツール*12が開発されてきました。

従来や現在の監視ツールは、サーバに対する定期的なpingやメトリックの時系列グラフ化をサポートしています。 しかし、これだけでシステムの振る舞いを分析できるかというとそうではありません。 監視ツールを頼りにしつつも、SREはシステムのネットワークグラフ構造を調べ、ログを眺め、アプリケーションコードをgrepし、脳内でシステムに対してどのような変更があったかを思い出すといったことをやっています。

このように、まだまだ監視ツールだけではわからないことがあるのが現状です。 従来の監視はもちろん、ログやイベントデータの収集に加えて、構成要素と要素間の関係の把握などが求められます。*13

自分が開発に関わっているサーバ監視サービスであるMackerel[mac]については、[yuu17-2]に書いています。Mackerelには世の中のウェブシステムの観測結果が集約されているデータベースとしてとらえると解析対象としておもしろいんじゃないかと思います。

制御

システムが自律的に動作し続けるためには、システムの異常を自動で制御する必要があります。

ナイーブな制御

例えば、クラウドの台頭によりサーバの生成と廃棄がプログラマ化されたため、メトリックの変動に応じてサーバの個数や性能を自動調整できます。 さらに、なんらかの理由により不調なプロセスやサーバをすぐ捨てて、新しいものを生成することも可能です。 現在では、このあたりがウェブシステムの現場で浸透中の制御になるかと思います。 しかし、制御のためのパラメータはエンジニアの経験を元に値を設定しており、制御がうまく動くかどうかはエンジニアの技芸に頼っていると言えます。 ナイーブな自律制御のままでは、すべての課題はクリアできません。

待ち行列による制御

よくあるアイデアの一つに、待ち行列理論の利用があります。情報ネットワークの分野では、よく利用されている理論です。 ウェブシステム全体と、サブシステムをそれぞれ入れ子構造の待ち行列としてモデル化できます。 待ち行列解析により、到着分布と処理分布を観測しつづけることで、必要なコンピューティングリソースを割り出し、自動的にリソースを配分し続けられます。 例えば、最も簡単なリトルの法則を応用すると、[myu16]のように中期的なキャパシティプランニングに利用できます。

しかし、待ち行列理論の課題は、仮定する分布の範囲外の予測のできない突発的な外乱に対応しづらいことです。 到着分布は、前述したように入力の複雑さにより、予測することは難しいでしょう。 さらに、到着分布が予測可能であっても、ネットワークのパケット処理とは異なり、ウェブシステムでは、処理の重たい入力とそうでない入力の差が大きい*14ため、処理分布が予測できない可能性もあります。

外乱は事業機会となることがあるため、分布から外れた異常値だからといって無視はできないという事情があります。 そこで、最近はフィードバック制御に着目しています。*15

フィードバック制御

フィードバック制御は、大規模で複雑なシステムを、たとえシステムが外乱に影響を受けようとも、あるいは、限られた資源を有効利用しつつ、その性能を保って動作させるための手法です。 [phi14]より引用

フィードバック制御に着目した理由は、制御対象はブラックボックスであり、中身は不明でよいという点です。 これは、SREがアプリケーションの中身を知らずに観測結果だけをみて障害対応する様子に似ていると感じました。 待ち行列理論ではできないダイナミクスを扱えるのも特徴です[ohs13]。 現実のウェブシステムでは、解析的にモデルを導出するのは難しいため、パラメータの決定には「実験」による計測が必要です。

ウェブシステムに対して、フィードバック制御の導入イメージは例えば、以下のようなものです。 制御入力は、明示的に変更可能なパラメータです。例えばサーバの台数やサーバのキャッシュメモリ量などがこれに相当します。 制御出力は、制御対象パラメータです。例えば、応答時間やエラーの数などになります。 制御出力を監視し続け、目標値から外れたら制御入力を変更し、元に戻すような操作を、システムモデルに基づいて行います。 具体的には、制御入力に対して伝達関数を適用し、制御出力を得ます。 ただし、伝達関数の同定やチューニングは、実システムで応答をみる必要があります。

ただし、ここで述べているのは古典的な制御理論の話であり、単一入出力しか扱えません。 実際には、複数の入力と出力を扱ったり、階層的なシステムに対する制御をやりたくなるでしょう。 そちらは、現代制御理論やポスト現代制御理論と呼ばれる発展的な理論の範疇のようです。

制御理論をウェブシステムの運用に組み込む研究には、[jen16]などがあります。 [jen16]は、データベースクラスタ内のサーバのスケーリングについて、フィードバック制御、強化学習、パーセプトロン学習の3つの手法を比較しています。 さらに、フィードバック制御(PID制御)をApache Sparkのバックプレッシャー機構に組み込む実装もあります[spapid]

実験

待ち行列にせよ、フィードバック制御にせよ、機械学習的なアプローチにせよ、実システムの応答結果を継続的に得ることが必要です。 おそらく、統一的なモデルなどはなく、システムやサブシステムごとに異なるモデルとなると考えています。

しかし、単純に平常時のシステムを観測し続けるだけでは、良好な制御モデルが得られない可能性があるのではないかと考えています。 というのは、システムごとのモデルとなると、過去に限界値や異常値に達したデータの数が少ないため、学習データが足りない可能性があります。 *16 特に新システムであればデータはゼロなので、本番環境の蓄積データだけでは、制御パラメータを決定できません。

ワークロードのない状態では、例えば1台あたりのサーバの限界性能というのは実際に限界まで負荷をかけないとわからないことが多いでしょう。 素朴に考えると、手動で実験してデータをとることになってしまいます。

そこで、実験の自動化を考えます。 システムには日々変更が加えられるため、継続的な実験が必要であり、手作業による実験は人手が必要で結局長続きしないためです。

分散システムの自動実験の概念としてNetflixが提唱するChaos Engineering[cha17]があります。 Chaos Engineeringは、本番環境にて故意に異常を起こす逆転の発想です。 例えば、サーバダウンなどわざと異常を起こすことで、システムが異常に耐えられるのかをテストし続けます。 Chaos Engineering自体は、先に述べたように制御モデルのパラメータ推定についての言及はありませんが、ビジョンの構想に大きく影響を受けました。*17

しかし、一般に言われていることは、本番環境で異常を起こしたり、限界まで負荷をかけるのは不安であるということです。 実際、書籍「Chaos Engineering」[cas17]では、監視の環境整備、異常時の挙動の仮説構築、実験環境での手動テスト、ロールバックなどの基盤を整えた上で十分自信をもった状態で望むようにと書かれています。 したがって、実験そのものは自動化されていても、まだまだそれに至るまでに人間による判断を多く必要とするようです。

ここで、クラウドやコンテナ技術など限りなく本番に近い環境をオンデマンドに構築する技術が発達してきていることを思い出します。 自分の考えでは、実験環境を高速に作成することで安全に実験を自動化する技術を突き詰め、効率的にデータを取得し、パラメータを決定するというアプローチを考えています。*18

これら以外に手動で実験するケースについても、実験という概念に内包し扱おうしています。 動作テスト、負荷テスト、パラメータチューニング(OSやミドルウェアのパラメータ)などです。*19

Experimentable Infrastructure

以上のような観測と実験の自動化アプローチには、近代科学の手法の自動化であるというメタファーが隠されています。

書籍「科学哲学への招待」[noe15]に近代科学の歴史とともに、仮説演繹法について紹介されています。

  • (1) 観察に基づいた問題の発見 (観測)
  • (2) 問題を解決する仮説の提起
  • (3) 仮説からのテスト命題の演繹
  • (4) テスト命題の実験的検証または反証 (実験)
  • (5) テストの結果に基づく仮説の受容、修正または放棄

仮説演繹法のループを高速に回し、自律的に変化に適応し続けるシステムを「Experimentable Infrastructure」と呼んでいます。 コンピュータに仮説の提起のような発見的な手法を実行させるのはおそらく難しいと思います。 しかし、ウェブシステムの観測と実験により判明することは、世紀の大発見ということはなく、世の中的には既知のなにかであることがほとんどなので、仮説提起をある程度パターン化できるのではないかと考えています。

5. 自律運用の壁とウェブサイエンス

仮に前述のアプローチがうまくいったとしても、さらにその先には壁があります。 自律運用の壁は、「ハードウェアリソース制約」と「予想できない外乱の大きな変化」です。

前者は、予め用意したリソースプールでさばける以上の負荷には耐えられないことです。 クラウドにも上限は存在します。

後者は、サーバの増加などには必ずディレイが存在するため、外乱の大きさによっては、フィードバックが間に合わないケースもありえます。 これについては、以下のようにウェブサイエンスの研究成果である状態予測をフィードフォワード制御に利用するといったアイデアもあるかもしれません。

自発的に発展するサービスの特徴を捉え、例えば、Web サービスが今後発展していくのか、元気をなくしていくのか、そうした状態予測を目指し、自律的な人工システムのダイナミクスを捉える普遍的な方法論をつくり、自然科学としての人工システム現象という分野の確立を目指している。

106 人 工 知 能 31 巻 1 号(2016 年 1 月)「ウェブサイエンス研究会(SIG-WebSci)」 発足

自律運用

ここまでの「自律」の定義は「自動修復」「自律運用」でした。 自律運用では、与えられた制約条件=信頼性 を満たすように自律動作することを目指しています。 信頼性を自律的に満たせれば、費用のうち人件費はある程度削減できます。 しかし、信頼性の条件設定、アーキテクチャの決定、ソフトウェアの効率化などは依然として人の仕事です。

自律開発

自律運用に対して、自律開発*20という考え方があります。 これは、この記事の文脈では例えば、自律的に費用を最小化するようなシステムを指します。

自律開発には、進化・適応の概念が必要であり、分散システムアーキテクチャの設計・改善やソフトウェア効率改善の自律化などを含みます。 おそらくソフトウェア進化の研究[oom12]などで進んでいる分野だと思います。

運用から解放されたその先

ここまではウェブシステムの運用という工学的モチベーションの話でした。

IPSJ-ONEの記事[yuu17]にて、以下のような宿題がありました。

しかし、最終的に自分のやっていることが世界にとってどういう意味があるかということを加えるとよいという話もあったのですが、ここは本番では組み込めなかった部分です。自動化されて、運用から解放されて、遊んで暮らせるようになるだけなのか?という漠然とした疑念はありました。科学の歴史をみてみると、例えば未知であった電気現象を逆に利用して、今ではコンピュータのような複雑なものを動かすことができるようになっています。そこからさらにメタなレイヤで、同じようなことが起きないかといったことを考えているのですが、これはこれからの宿題ということにします。

いまだにこの宿題に対する自分の中の答えはありませんが、ウェブサイエンス研究会発足の文章を拝読して、ぼんやりと基礎科学の貢献という道もあるのだなと思いあたりました。 人の手を介さずに動き続けるウェブシステムを探究することで、複雑な系に対する統一的な法則を発見し、基礎科学へ貢献できないかどうかといったことを考えながら、老後を過ごすのも、昔からシステムが好きな自分にとってよいのかもしれません。

ネットワークの技術階層を含む Webの存在そのものを新しい「自然現象」として捉え、例えば、その「生態系」としての構造を明らかにすることで、普遍的なダイナミクスやパターンを明らかにし、従来の自然科学・人文科学の考えを発展させることを目指している。

106 人 工 知 能 31 巻 1 号(2016 年 1 月)「ウェブサイエンス研究会(SIG-WebSci)」 発足

6. 議論

講演後にいただいた質問や議論から、SREの分野は、まだまだサイエンスというより、エンジニアの技芸の上に成り立っているなと感じました。

例えば、以下のようなデータ解析の内容をSREの分野で書かれた文献を今のところ僕は知りません。

  • ウェブシステムの複雑さの定義と解析
  • ウェブシステムの階層構造の変化の解析
  • ウェブシステムの階層ごとの到着分布、処理分布の解析
  • ウェブシステムの自律度合いの定義と解析
  • 異常のパターン分析と体系化

これは、以下の2点が要因としてあると考えています。

  • SREとデータ解析の関心やスキルセットのミスマッチ
  • データ収集が困難
    • 数年前までは、自社システムを自社内で観測してデータを溜め込んでいるだけであったため、多数のウェブシステムの情報をまとめて解析することができなかった

後者については、Mackerelを持っていることは強みなので、うまく活用していきたいと思います。 このようなデータ解析の観点でみると、観測には前述した項目よりもっと先があることを想起させられます。

7. まとめ

ウェブシステムの運用は、ここ10年で自動化が進んでおり、物理的な世界からソフトウェアの世界になってきました。 ウェブシステムは複雑ではあるが、現代のところコンピュータだけで自律した系ではありません。 ウェブシステムという人工物を自然のように振る舞わせ、人間を運用から解放したいというのが最終的な目標です。

参考文献

発表スライド

あとがき

そもそも、ウェブサイエンス研究会に招待していただいたきっかけは、IPSJ-ONE 2017 高度に発達したシステムの異常は神の怒りと見分けがつかない - IPSJ-ONE2017 - ゆううきブログ の登壇にてご一緒した鳴海先生に声をかけていただいたことです。 さすがに、場違いではとも思いました。というのも、僕が実際やっていることは、時系列データベースの開発であったり、10年前から続くウェブシステムの運用効率化などであり、技芸であって科学ではない*21からです。 しかし、はてなシステムを構想するにあたって、地に足がついてなくてもいいから、無理やり未来を考えるいい機会になると捉え、登壇を引き受けさせていただきました。 今回の研究会のテーマは、「自然現象としてのウェブ」ということで、本当に何を話したらよいかわからないテーマで相当苦戦しましたが、その結果、IPSJ-ONE登壇で考えたことの言語化を進められました。*22 途中、妄想のような話もあり、他の分野の専門家からみれば眉をひそめるような表現もあるかもしれませんが、一度考えたことを言語化しておくことでまた次のステップに進めると考えています。

普段はどうしても目の前の課題に熱中しがちで、未来のことを考えようとはなかなか思いません。 概念や思想だけではなかなかそれを取り入れようとは考えず、それを実現するソフトウェアなりハードウェアが目の前にあらわれ使える状態になってはじめて目を向けることになります。 例えば、AWSもDockerもないと仮定して、Immutable Infrastructureの考え方に触れたとしても、到達までの道筋がすぐにはみえないため、諦めて考えないようにしてしまいそうです。

発表後に、研究会の幹事である橋本先生に、技術者はどこまで先を考えているものなのか、と質問をいただきました。 少なくとも、日本のウェブの技術者界隈で、未来の技術ビジョンを設定し、それに進もうとしている様子が外からみえることはなかなかありません。 ペパボ研究所が掲げるなめらかなシステム *23が僕の知る唯一の例です。 Real Worldでは、一歩前に進むだけでも本当に様々な問題が起き、とにかく目の前のことを倒すことが求められるので、とても未来どころではなくなるというのが現状かもしれません。

未来を考えるのは、研究者の場合は当たり前に求められるという印象があります。 しかし、SREの分野では、研究者のコミュニティが他と比べて未発達なようにも思います。 近い分野である情報ネットワークに関しては、日本でも様々な研究会がありますが、僕の知る限りでは、日本では直接的にSREの分野を扱う研究会は存在しないようです。*24

そこで、ウェブシステムアーキテクチャ研究会、(#wsa研)というものを立ち上げようとしています。 第1回は京都開催にもかかわらず、全員発表型で10人以上の参加者が既に集まっています。 今回の講演の準備をするにあたって、我々の分野で未来を議論するための既存の枠組みや土台があまりないことを改めて実感しました。*25 WSA研では、未来を考えるために、現状を体系化し、そこから新規性や有用性を追求していこうと思います。

*1:発表時には機会費用を含めていませんでしたが、機会費用を大幅に増加させて前者2つを削減できるため、この記事では含めました

*2:厳密にはService Level Objective(SLO)))を制約条件として、費用を最小にする最適化問題」((信頼性と費用はトレードオフであり、信頼性を定期的に見直す必要があります。高すぎる信頼性のために想定より費用が最小化できないとなれば、例えば四半期ごとに信頼性目標を下げるといった運用が必要です

*3:[Bet17]では、「サービスのSLOを下回ることなく、変更の速度の最大化を追求する」という表現になっています。今回は、自分自身のメンタルモデルに近い、費用の最小化という表現を選択しました。

*4:これはサービスによって大きく異なります。 これまで運用してきたサービスの肌感覚では、1週間程度。運が良くて1ヶ月程度。

*5:実質OSのプロセスの起動

*6:前述の信頼性を維持し費用を最小化するという話

*7:#wakateinfraの中でも、超常現象などと呼んでいました

*8:ここではMySQLとPostgreSQLを想定

*9:ここでは、VRRPによるクラスタリングを想定

*10:書籍「システムの科学」[her99]の第8章 階層的システム

*11:実際の職種名はWebオペレーションエンジニア

*12:NagiosやZabbixなど

*13:6. 議論 にてさらなる観測の可能性について記述

*14:いわゆる地雷URLなど

*15:[yuu17-3]でも紹介した

*16:異常検知であれば平常時だけを知っておけば問題なさそうだが、その他のアプローチの場合はどうか

*17:書籍[cas17]では、奇しくもフィードバック制御の伝達関数の話が例としてでてくるが、予測モデルを構築することは困難なので、実験しましょうということが書かれているのみでした。

*18:本番環境の負荷の再現が壁になるだろうとは思います。

*19:パラメータチューニングの自動化については、おもしろい例[mir13-2]があります。

*20:おそらく一般的に使われている言葉がソフトウェア工学の世界などにあるかもしれません

*21:John Allspaw、Jesse Robbins編、角 征典訳,ウェブオペレーションーーサイト運用管理の実践テクニック,オライリージャパン より引用

*22:発表自体は、タイムコントロールに久々に大失敗して、途中スライドスキップして残念な感じになってしまいましたが、この記事で補完できればと思います。

*23:研究所なので技術者界隈とは呼ばないかもしれません

*24:海外ではUSENIXのLISAやSREconなどがあります

*25:SRE本は本当に稀有な存在

サーバ「管理」ツールとしてのMackerelの起源

この記事は、SaaSのサーバ監視サービスMackerelを起源を遡り、そこから現在の姿に至った経緯をはてな社内のエンジニアに共有するためのものです。 なお、ここに書かれていることは、Mackerel開発チームの公式見解ではありません。

概要

Mackerelは、もともとは2007年ごろに開発されたはてなの社内のサーバ管理ツールであり、動的なインフラストラクチャに対応するために、現在でいうところのInfrastructure As Codeを目指したものです。 そこから2013年にSaaSのサービスとして開発され、コードベースとアーキテクチャは全く新しくなり、監視機能を備え、サーバ「監視」サービスと呼ばれるようになりました。 しかし、はてな社内では、プログラマブルなAPIを備えたサーバ「管理」サービスとして、Mackerelを中心にしたインフラストラクチャを構築しています。 Mackerelの起源は、サーバ「監視」というよりはむしろサーバ「管理」にあったということをここに書き残します。

社内Mackerelの誕生

社内Mackerelは、はてな前CTOのstanakaさんを中心に2007年ごろに開発が始まりました。 社内Mackerelのコードベースは、mackerel.ioのそれとは全く別のものであり、社内Mackerelにしかない機能もあれば、Mackerelにしかない機能もあります。 しかし、共通する思想はあり、その思想は現代のインフラストラクチャ管理にも通ずるものであると考えています。

はてなでは、2007年前後にXen Hypervisorによるサーバ仮想化技術が導入[1]され、物体としてのサーバに加えて、データとしてのサーバを管理し始め、人手による管理コストが増大しました。 具体的には、サーバの増減に合わせて、人がIPアドレス管理表を更新したり、ホスト名やIPアドレスが記載された各種設定ファイルを更新して回るとといった手間が増えたことを指します。

これらについて、全体のサーバ数が少ないかつ、個数の増減頻度が小さければ、人手による管理にもコストはかかりません。 一方で、クラウド環境のように、データとしてサーバを扱うのであれば、プログラマブルに管理するためのデータベースが必要です。 そこで、社内Mackerelが誕生しました。

このように、社内Mackerelはサーバ監視というより、プログラマブルなインフラストラクチャ管理を目指したツールでした。 実際、社内Mackerelのことをサーバ「監視」ツールではなく、サーバ「管理」ツールと呼んでいました。

社内Mackerelの特徴

構成レジストリ

社内Mackerelの主な管理単位は、「ホスト」であり、ホスト名やIPアドレスはもちろんOSの種別やCPU数、メモリ量などのソフトウェア、ハードウェア情報も含みます。 加えて、ホストは以下の属性情報を持ちます。

  • 「サービス」「ロール」
  • ホストステータス ('working', 'standby', 'maintenance', 'poweroff', 'destroyed')
  • 拠点、ラック、電源、ネットワークなど

これらはビューとして人が参照するだけでなく、REST APIにより各種ツールと連携し、インフラストラクチャ要素の情報を一元化しています。 具体的には、以下のように各種ツールのパラメータを、MackerelのAPI経由で動的取得し、ホスト名やIPアドレスの多重管理を防いでいます。

  • 1: 死活監視ツールの設定ファイルの自動生成[2]
  • 2: 内部DNSのゾーンファイルの自動生成
  • 3: デプロイツールからのデプロイ対象ホストの動的取得
  • 4: 構成管理ツール(Chef)の適用cookbookを対象ホスト名から動的解決

1について、サーバ監視のうち、死活監視についてはNagiosを利用します。同一ロールであれば、同じ監視設定を流用できることと、ホストステータスがworking以外に変更すれば監視を外すといった動的なプラットフォームに適した運用が可能になります。1 2について、ホスト単位のレコード以外に、ロール名ベースのDNSラウンドロビン用FQDNとVIP用FQDNがあり、サービスディスカバリに利用します。 3について、Capistranoでホスト名を静的に設定したところをAPIによりロール名だけ設定しておけば、対象にデプロイできます。ホストステータスをmaintenanceにしておけば、デプロイ対象から外れます。 4について、ホスト名を与えれば、ホストに紐付いたロールから適用するcookbookを自動解決できるようになります。

このようなツールは、CMDB[3]に似ていますが、資産管理機能は含みません。 どちらかといえば、書籍「Infrastructure As Code」[4] 3.4節 構成レジストリに近いものです。 構成レジストリは、インフラストラクチャ要素についての情報集積庫であり、書籍では、構成レジストリと例として、Zookeeper/Consul/etcdや、Chef Server/PuppetDB/Ansible Towerなどが挙げられています。

メトリックの時系列グラフ

社内Mackerelには、前述の構成レジストリ機能に加えて、メトリックのクローリングと、メトリックを時系列グラフ表示する機能があります。 メトリッククローリングはPrometheusなどと同様にPull型のアーキテクチャであり、クローラーがOSの基本メトリックはSNMP、MySQLなどのミドルウェアについてはミドルウェアのプロトコルでメトリックを取得し、RRDtoolに保存します。

社内Mackerelの時系列グラフ機能の実装については、過去の発表資料[5]で詳しく説明しています。

今から振り返ると、メトリックの収集、保存、可視化については、Repiarableにするために、専用のツールに任せ、ビューだけ統合したほうが良かったと考えています。 社内Mackerelの開発開始が2007年より、もう少し後であれば、Collectd[6]で収集し、Graphite[7]に保存するといった選択肢もありました。

メトリックに限らず、当時Nagiosには使いやすいAPIがなく強引な手段で統合している実装をみると、Infrastructure As Codeを志向しようには世の中が追いついていなかった様が伺えます。

SaaSとしてのMackerelへ

2013年にMackerelの開発が始まり、2014年に正式リリースされました。 SaaSとして提供するにあたって、グラフツールではないため、メトリックに着目するより、「ホスト」に着目するといった主要な思想は、社内Mackerelから引き継がれました。他にも以下の要素が引き継がれました。

  • サービス・ロールの概念
  • ホストステータスと監視のon/offとの連動
  • 統合されたホスト管理とメトリックグラフビュー

特にサービス・ロールはMackerelの中心概念であり、社内Mackerelとほとんど同じものです。 私見ですが、サービス・ロールは、人間が決定しなければいけないラベル付けだと考えています。 インフラストラクチャに紐づくラベルは多種多様ですが、大きく分類すると、自動で付与できるものと、それ以外の人が判断して付与するものがあります。 自動で付与できるものは、OS、ハードウェア、ネットワーク、インストールされたミドルウェア、クラスタ内の役割などの情報です。 一方で、どのサービスのホストなのか、どのロールなのかといったことは、人間が最初に決めて付与しなければなりません。自動で決定できるとしても、人間が与えたルールにしたがい決定することが多いと思います。2 このラベルを用いて、リポジトリのディレクトリ構成を管理していることもあります。[8]

以上のように引き継がれた機能がある一方で、ラックや仮想ホストの管理など、クラウド時代にそぐわない機能は削ぎ落とされました。 逆に、死活監視やメトリック監視、外形監視機能は統合されました。

メトリックの収集と時系列グラフについては、SaaSとして提供するために、NAT超えを意識し、Pull型ではなく、Push型のアーキテクチャになっています。 ホストにインストールされたmackerel-agentのリクエストを[9][10]に書いた時系列データベースに格納します。

このように、Mackerelは監視機能をひと通り備えているため、サーバ「監視」サービスと銘打っていますが、その起源は、サーバ「管理」サービスです。 実際、サーバ「管理」ツールとしてのMackerelをうまく活用していただいている例[11]があります。 このブログでも、AnsibleのDynamic Inventoryとの連携[12]やServerspecとの連携[13]など、サーバ「管理」ツールとしてのMackerelをいくつか紹介しています。

その他のサーバ「管理」ツール

社内Mackerelと同じサーバ管理ツールは、他にもあり、ここでは、Collins、Yabitzなどを紹介します。

Collins

Collins[14]はTumblrで開発されているインフラストラクチャ管理ツールです。

Collinsの特徴は、Assetsベースのデータモデルです。Assetsはなんでもよく、事前設定されているものはServer Node、Rack、Switch、Router、Data Centerなどです。 さらに、このAssetsオブジェクトに対して、キーバリュー形式のTagsを付与できます。Tagsはハードウェア情報などユーザが管理できないものはManaged,外部自動化プロセスから動的に設定されるものはAutomated、ユーザによって設定されるものはUnmanagedというように3種類のタイプを持ちます。

このように、Collinsは抽象的なデータ表現により各種インフラストラクチャ要素を管理しており、具体的なデータ表現をもつ社内Mackerelとは対照的です。

Yabitz

Yabitz[15]は、旧ライブドアで開発されたホスト管理アプリケーションです。READMEでは、「ユーザ(多くの場合は企業)が保有するホスト、IPアドレス、データセンタラック、サーバハードウェア、OSなどの情報を管理するためのWebアプリケーション」と説明されています。 機能一覧を見るかぎり、社内Mackerelによく似ています。

いずれのソフトウェアも、死活監視機能やメトリック可視化機能は備えておらず、構成レジストリとしての役割に専念しているという印象です。 他にも、Zabbixのホストインベントリなどがサーバ管理機能にあたると思いますが、Zabbixはあまりに機能が多いので、よく知らずに言及することを控えました。

これからのサーバ「管理」ツール

昨今のインフラストラクチャの進化は激しく、従来のサーバ・ネットワーク機器といった管理単位よりも、さらに動的な要素やそもそもサーバとしての体裁をなしていないインフラストラクチャを管理していく必要があります。

コンテナやマネージドサービスの台頭により、管理の単位はもはやサーバだけではなく、プロセス、クラスタ、Functionなどに置き換わりつつあります。 実際、[9]のアーキテクチャの管理では、Lambda FunctionやDynamoDB Tableなどがホストとして登録されています。

サーバ構築と運用コストが激減し、インフラストラクチャ要素をデータのように扱えるようになってきた結果、マイクロサービスやサーバレスアーキテクチャのような要素同士の関係がより複雑になりやすいアーキテクチャが広まってきました。このようなアーキテクチャのもとでは、1つ1つの要素を管理するというより、関係そのものを管理する必要がでてくると考えます。 ネットワークグラフをTCPコネクションを追跡して可視化する実験[16]に既に取り組んでいました。

さらに、関係性の管理というとまず可視化が思い浮かびます。しかし、人がみて判断するその先として、関係性を表現したグラフ構造をAPIで操作することにより、システムの自動化につなげられないかを考えています。例えば、自動化が難しいレイヤとしてキャパシティプランニングやコストプランニングがあります。書籍「SRE サイトリライアビリティエンジニアリング」[17]の18章「SRE におけるソフトウェアエンジニアリング」にて、サービスの依存関係とパフォーマンスメトリックなどを入力として、キャパシティプランニング計画を自動生成するソフトウェアが紹介されています。 サービスの依存関係を管理するのが普通は大変で、書籍によると執筆時点では人間が設定を書いているように読めます。しかし、関係性のグラフ構造をプログラマブルに操作できるのであれば、プランニングを自動化しやすくなると思います。

参考文献


あとがき

先日、はてなWebオペレーションチームのテックリード - Hatena Developer Blog にて、Mackerelチームとの研究会をやるという話を書きました。 モニタリング研究会では、モニタリングの過去・現在・未来をテーマに、まず過去を知ることから始めており、その一環としてMackerelの源流をまとめました。

Mackerelのサーバ監視以外の「管理」の側面と「管理」と「監視」を統合し何ができるのかという点については、まだ十分に伝えられていないと思っています。 Mackerel本について、mattnさんに書いていただいた書評(Big Sky :: 「Mackerel サーバ監視[実践]入門」を読んだ。)に

これは本書で知ったのですが、どうやら はてな社は Mackerel をホスト管理としても使っている様で、数千いるサーバのロールをうまくラベリングして運用されているとの事でした。

とあり、このような運用手法を伝えていきたいですね。

このように、人の脳やExcelでは管理しきれない未知を明らかにするという観点で、「管理」や「監視」といった概念が組み合わさって、「観測」となり、その先にどのようなインフラストラクチャをつくっていくかをこれからやっていきます。


  1. mackerel2という社内Mackerelの改良版では、タグという概念で、複数のロールをさらに集約し、同じ設定で監視できるようになっています。これは、ロールはMySQL

  2. 例えば、サービスごとにネットワークレンジが決まっているなど。

コスト効率の悪いLambdaアプリケーションの性質に関する考察

概要

Lambdaは100msの実行時間単位でオンデマンドに課金されるため、立ち上げっぱなしのEC2インスタンスよりも、料金が安くなる可能性があることが一般に知られている。 しかし、以下の性質を満たすアプリケーションでは、EC2インスタンス上に構築したケースと比較して、Lambda上に構築したほうがコスト効率が悪くなるのではないかと考察してみた。

  • Lambda functionの実行時間のうち、ネットワークI/O時間が支配的である
  • Lambda functionの実行終了を同期的に待たなければならない
  • 複数のレコードをLambda functionの引数に渡すことができない

Lambdaの基本コスト構造

まず、Lambdaのコスト構造を把握する。 Lambdaの料金表[1]によると、「functionに対する合計リクエスト数」と「functionの合計実行時間」に応じて料金が発生する。

後者の合計実行時間は、静的に割り当てたメモリ量により100ミリ秒単位の価格が異なる。 したがって、実際にコストを試算するときは、前述の2つの料金項目に、「静的割り当てメモリ量」を加えた3つの料金項目があると考えるとよい。 LambdaのPricing Calculator[2]もそうなっている。

LambdaとEC2の時間あたりのランニングコスト比較

LambdaとEC2の時間あたりのランニングコストを比較する。 functionの平均実行時間を料金単位と同等の100msと仮定して、1時間分つまり18000リクエストしたあたりのコストを、代表的なメモリ量別に算出すると以下のようになる。

  • 128MB: $0.015/hour
  • 832MB: $0.056/hour
  • 1536MB: $0.097/hour

このうち、「functionに対する合計リクエスト数」料金については、$0.0072であり支配的ではない。

EC2 ap-northeast-1の料金表[3]から、Lambdaと同じ論理2コア[4]の代表的なインスタンスの1時間あたりのコストを以下のようになる。

  • t2.medium: $0.0608/hour
  • t2.large: $0.1216/hour
  • m4.large: $0.129/hour
  • c4.large: $0.126/hour

LambdaのFAQ[5]にあるように、メモリ割り当て量に対してCPU性能が比例することから、EC2インスタンスとの直接的な比較は難しいが、だいたい同程度かEC2が少々割高であることがわかる。

Lambdaに不向きなアプリケーション性質

アプリケーション例

前述のように、LambdaとEC2のランニングコストにそれほど差はないことを考えると、EC2よりも細かい粒度で消費コンピューティングリソースを制御できるLambdaのほうが有利なケースは、数多くあり得る。

一方で、例えば、次のようなアプリケーションをLambda functionとして実装することを考えてみる。 画像URLを渡すと、画像をフェッチし、特定の画像変換を実行するような画像変換プロキシを考える。 このプロキシに対して、多数のユーザがブラウザからリクエストするようなユースケースを想定する。 レスポンスは同期的に画像を返す必要があるため、以下の図のようにAPI GatewayをLambdaの前段に挟むことになる。 その場合、実際はさらに前段にCDNを挟むことになるだろう。

|-----------------------------------------------------------|
|                                      internet             |
| --> (CDN) --> API Gateway --> Lambda --------> image file |
|                                                           |
|-----------------------------------------------------------|

このようなアプリケーションは、以下の2点でコスト的に不利になる。 まず、Lambda function内でインターネット経由で画像をフェッチするため、function実行時間の料金は嵩みがちになる。 画像フェッチ処理と画像変換処理を並行実行し、実行時間を削減することも難しい。 次に、I/O多重化により、1回のfunction実行において、同時画像フェッチ数を増やし、同じ実行時間料金での処理効率を高めることも難しい。 なぜなら、ユースケース上、同じHTTPリクエストに複数の画像URLを含められず、functionの引数に複数の画像URLを渡せないためだ。

一方、EC2インスタンス上で同じようなアプリケーションを実装すると以下の図のようになる。

|----------------------------------------------------------|
|                                    internet              |
| --> (CDN) --> ALB(or NLB) --> EC2 ----------> image file |
|                                                          |
|----------------------------------------------------------|

EC2実装では、インスタンス上でWebサーバが動作する。Lambda実装とは異なり、複数のリクエストを1つのインスタンスで並行して受け付け、I/O多重化できる。 もちろん、1スレッドで動作するシリアルモデルなWebサーバであれば、同時に1つのリクエストしか受け付けられないが、古典的なWebサーバであっても並行モデルを採用していることがほとんどである。5 したがって、EC2実装(Webサーバ実装)では、同じランニングコストに対して、複数の画像をフェッチできることになり、Lambda実装と比較してコスト効率がよい。

アプリケーション例のコスト試算例

実際、コストがどの程度になるのかという感覚を掴むために、仮のパラメータで試算をしてみる。

平均実行時間 1000ms、平均リクエスト数 1000req/sとなるケースを考える。 一番小さいメモリ割り当て量である128MBのとき、月額は約$6000となる。 実行時間を小さくするために、メモリ割り当て量をN倍に増やしCPU性能を大きく設定すると、画像変換処理は速くなるが、ネットワークI/O部分の時間はかわらないため、実行時間は1/Nにはならない。 平均リクエスト数が10倍であれば、コストは10倍になり、平均リクエスト数が1/10であればコストも1/10になる。

この計算例を軸に、見覚えのあるアプリケーションの負荷パラメータをあてはめて、比例計算すると、だいたいのコスト感覚をつかめると思う。

考察

以上の例から一般化し、コスト観点でLambdaに不向きなアプリケーション性質は以下のようになるという考察をしてみた。

  • 1: Lambda functionの実行時間のうち、ネットワークI/O時間が支配的である
  • 2: Lambda functionの実行終了を同期的に待たなければならない
  • 3: 複数のレコードをLambda functionの引数に渡すことができない

3について、単にバルク処理できるエンドポイントを生やすだけで解決するのであれば、問題にならない。 1と3について、ネットワークI/O時間が支配的であっても、1レコードの処理をI/O多重化し、実行時間を小さくできるのであれば問題にならない。 さらに、2.と3.については、非同期でよいということであれば、Kinesis Streamsのようなキューを挟めば、Lambda function側でレコードを複数同時に処理し、I/O多重化できる。

しかし、コスト的に不利な性質のアプリケーションであっても、Lambdaの採用により初期構築の手間とその後の運用の手間を大幅に削減しやすいため、実際にコストを試算してみて採用可否を判断することが望ましい。 前述のLambda実装では、CDNのキャッシュヒット率を向上させることで解決できるなど、アーキテクチャ全体でコストを抑えれば問題ないケースもある。

LambdaのようなFaaSにおいて、実時間ではなく、CPU時間で課金するモードがあればおもしろいかもしれない。

今回の話は、Lambdaアーキテクチャの問題ではなく、Lambdaのコストモデルの問題といえる。 EC2上でLambdaのようなものを自前で運用したとして、同じインスタンス上に複数のfunctionコンテナが動作する状態と、マルチプロセスモデルのWebサーバが動作している状態とでは、ランニングコストあたりの集積度という観点ではさほど変わらない。

あとがき

このような性質をもつアプリケーションが現実にどれくらいあるかわからないが、実際、Lambdaでコスト試算してみると高くなったという話を社内で聞いたので、少し一般化してこのような考察をしてみた。 これを社内wikiで共有して人気だったので、社外共有することにした。 最近は、はてなWebオペレーションチームのテックリード - Hatena Developer Blog にあるようにアーキテクチャ相談をしているため、その活動の一環ということになる。

参考文献

RedisサーバのCPU負荷対策パターン

Redisは多彩なデータ構造をもつ1インメモリDBであり、昨今のWebアプリケーションのデータストアの一つとして、広く利用されている。 しかし、一方で、性能改善のための手法を体系的にまとめた資料が見当たらないと感じていた。 実際、最初にCPU負荷が問題になったときにどうしたものかと悩み、調査と試行錯誤を繰り返した。 そこで、この記事では、自分の経験を基に、RedisサーバのCPU負荷対策を「CPU負荷削減」「スケールアップ」「スケールアウト」に分類し、パターンとしてまとめる。

背景

Redisのハードウェアリソース使用の観点で重要なことは、Redisサーバはシングルスレッドで動作する(厳密には他にもスレッドあるがクエリ処理をするスレッドは1つ)ことと、Redisサーバ上のデータをメモリ容量を超えて保持できないことだ。 前者については、シングルスレッドで動作するということは、マルチコアスケールしないということであり、特定CPUコアのCPU使用率がボトルネックになりやすい。 後者については、Redisはディスクにデータを永続化2できるとはいっても、MySQLやPostgreSQLのようなメモリ上のバッファプールにデータがなければ、ディスク上のデータを参照するといったアーキテクチャではないため、基本的に全てのデータをメモリ容量以下に収めなければならない。

設定やクラスタ構成によっては、ディスクI/OやネットワークI/Oがボトルネックとなることもある。 例えば、Redisのデータの永続化方式[^2]としてRDBを利用していると、Redisサーバのメモリ使用量が大きいほど、ディスクにフラッシュするタイミングで、ディスクIOPSを消費する。 また、マスター・スレーブ構成をとる場合、スレーブのマスター昇格時の再同期処理でネットワーク帯域の使用がバーストすることがある。 さらに、Redisは低レイテンシで応答するため、1台あたりのスループットを高くしやすく、ホスト1台あたりのネットワーク帯域が大きくなることもある。

これらの中でも、特にCPU利用率と戦ってきた経験があり、Mackerelというサービスでは、以下のように、なぜか毎年CPU利用率と戦っていた。

f:id:y_uuki:20170910003050p:plainf:id:y_uuki:20170910003102p:plainf:id:y_uuki:20170910003054p:plain
issue

RedisのCPU負荷対策パターン

以下の図は、CPU負荷対策パターンをカテゴリごとにまとめたものになる。

RedisのCPU負荷対策のカテゴリとして、CPU負荷削減、スケールアップ、スケールアウトがある。 CPU負荷削減は、Redisサーバが実行する処理そのものを減らし、スケールアップはハードウェア性能そのものを向上させ、スケールアウトは複数のCPUコアやサーバに処理を分散させる。 実際には、各カテゴリの対策を組み合わせることが多いだろう。

CPU負荷削減

CPU負荷削減のためのテクニックとして、「multiコマンド」「Redisパイプライン」「Luaスクリプティング」「Redisモジュール」がある。

multiコマンド

MGETMSETMSETNXなどの複数のキーに対して操作するコマンドを使うことで、余分なCPU処理をせずにすむ。 これは、multiコマンドを使わずに複数回コマンドを発行することに比べて、リクエスト/レスポンスの往復回数が減り、Redisサーバ側でリクエスト受信とレスポンス送信のためのオーバヘッドを削減できるためだ。

Redisパイプライニング

Redisパイプライニング3は、クライアントがレスポンスを待たずにリクエストを投入しつづけることで、ラウンドトリップタイムを削減する。 ドキュメント[^4]には、ラウンドトリップタイムだけでなく、Redisサーバ上でのトータルでの処理量を削減できると書かれている。

ただし、全てのコマンド発行パターンにおいてパイプライニングが有効なわけではない。 Read After WriteとWrite after Readのような、レスポンスに含まれる結果を利用して次のコマンドを発行する場合には、レスポンスを待たないパイプライニングは有効ではない。 パイプライニングにおいて、クライアント側のコマンド実行順序は保証されるため、Write after Writeのパターンでは有効だ。 Read After WriteとWrite after Readのパターンで処理効率を向上させたい場合、後述するLuaスクリプティングを使うとよい。

一部のRedisクライアントは、pipelineという名前がついたインタフェースであっても、Redisプロトコルレベルでのパイプライニングではないことがある。 Redisプロトコルレベルでのパイプライニングを真のRedisパイプライニングと同僚と呼びあっている。 真のRedisパイプライニングかどうかは、tcpdumpでコマンド発行の度に同期的にQUEUD応答が返ってくるかどうかで確認できる。

パイプライニング自体は伝統的な技術であり、CPUプロセッサの命令パイプラインが有名だ。 ネットワークプロトコルの中では、例えばHTTPパイプライン4がある。Redisのドキュメント[^4]には、多くのPOP3実装でパイプライニングをサポートしていると書かれている。

Luaスクリプティング

Luaスクリプティング5は、Redisサーバに対してLuaコードを送り込んで実行できる機能だ。 具体的には、クライアントとサーバ間でコマンド発行と応答を往復させなければ一連の処理を、Luaで記述することでRedisサーバ上で処理を完結できる。

Redisパイプライニングのドキュメント[^4]の「Pipelining VS Scripting」の項を読むと、パイプライニングを利用できるような多くのユースケースにおいて、より効率的に処理を実行できると書かれている。 さらに、Redisパイプライニングでは対応できないコマンドパターンにおいて、ラウンドトリップタイムを改善できる。 Luaスクリプティングにより、例えば、HASH型に対するmultiコマンドは実装されていませんが、Luaで複数キーに対応したHMSET(MHMSET)のような処理をする関数を実装できる。

しかし、Luaコードの処理内容によっては、もともとクライアント側で計算していた処理をRedisサーバ側で実行するため、RedisサーバのCPU利用率が増加する可能性がある。

Redisモジュール(夢)

これまで、Redisのデータ構造として使えるのは、LIST、SET、HSET、ZSETなどの汎用のデータ構造だけだった。 しかし、Redis 4.0からRedisモジュール6が追加され、独自のコマンドとデータ構造をC拡張により追加できるようになった。 これを使えば、アプリケーションに最適なコマンドを実装することで、CPU利用率に限らず、その他の性能も大幅に向上させることが理屈上は可能である。

自分では使ったことも作ったこともないが、Redis Modules Hubには検索エンジンやJSONフォーマットをサポートするような拡張が公開されている。

スケールアップ

スケールアップは、CPUをのクロック周波数を高いもに変更するか、CPUアーキテクチャの世代を新しいものにするかといった選択肢がある。 経験上、CPUのクロック周波数に対してほぼ線形にRedisサーバのCPU使用率が変化する。

前述のようにRedisサーバはシングルスレッドで動作するため、CPUのコア数が大きいものを選んでも意味はあまりない。 ただし、ディスクの永続化方式にRDBを選択している場合、Redisサーバプロセスからforkされた子プロセスが、メモリ上のデータをディスクに書き出す処理をバックグラウンドで実行するため、最低でも2コアにしておくと安心だと思う。

ただし、EC2を利用する場合、EC2インスタンスのCPUコアは論理コアなので、RDB利用の場合、4コアのインスタンスを選ぶことをすすめる。7 物理CPUコア数は論理コア数(vCPU数)を2で割った値になる。 例えば、c4.2xlargeは表記上のコア数は8だが、物理コア数は4となる。

スケールアウト

Redisのスケールアウトのための手法として、「参照用スレーブ」「垂直分割」、「水平分割」、「Redis Clusterによる水平分割」がある。 「参照用スレーブ」「垂直分割」、「水平分割」は、Redisに限らずMySQLなどのRDBMSにおいても、一般的なスケールアウト手法として知られている。8 参照用スレーブは名前の通り、参照クエリのみ分散できる。 更新クエリを分散するならば、「垂直分割」または「水平分割」が必要になる。 垂直分割と水平分割は、CPU負荷の分散以外に、メモリ使用の分散にも利用できる。

参照用スレーブ

Redisはレプリケーションによりマスター・スレーブ構成のクラスタを作成できる。 はてなでは、主にスタンバイサーバの作成に利用しており、KeepalivedによりVIPベースで冗長化している。

スレーブは他にも用途があり、参照用スレーブは、アプリケーションから参照クエリを向けるためのスレーブだ。 スレーブに読み込みクエリを投げることにより、マスターの負荷をスレーブへ分散できる。

以下の図のように、クライアントから複数のスレーブに向けて参照クエリを投げます。複数のスレーブに向けてロードバランシングするための手段はいろいろあるが、TCPロードバランサを挟むか、DNSラウンドロビンによるロードバランシングが一般的だ。

|-------------------------------------|
|              |----> redis (slave)   |
|        read  |                      |
| client ------|----> redis (slave)   |
|        |     |                      |
|        |     |----> redis (slave)   |
|        |                            |
|        |----------> redis (master)  |
|        read/write                   |
|-------------------------------------|

何も考えずに、すべての参照クエリを参照用スレーブに向けるのは危険だ。 Redisのレプリケーションは基本的に非同期であり、レプリケーション遅延があるため、クライアントからは結果整合性をもつデータストアとして扱う必要がある。 具体的には、マスターにデータを書き込んだときにスレーブにデータが転送され書き込まれるのを待たずにクライアントへ応答を返す。 したがって、応答がクライアントへ返った後に、書き込んだキーの参照をスレーブへ向けても、該当データが書き込まれている保証がない。 参照用スレーブを使う場合は、アプリケーションロジックがデータの一貫性を要求しないものかをよく確認しておく必要がある。

垂直分割

垂直分割は、データの種類ごとに複数のRedisサーバを使い分ける手法だ。 下記の図のように、データの種類A、B、Cに必要なデータを異なるRedisサーバに書き込むことで、参照と更新クエリを機能ごとに分散できる。

|------------------------------------|
|              |----> redis (種類A)  |
|              |                     |
| client ------|----> redis (種類B)  |
|              |                     |
|              |----> redis (種類C)  |
|------------------------------------|

可用性のために、各Redisサーバは冗長化されている必要があり、1つのクラスタで処理を捌くより、無駄なサーバが増えやすいというデメリットがある。 また、特定種類のデータに対するクエリの負荷が大部分を占める場合、その種類のデータを別のRedisサーバに移しても、結局分散先のRedisサーバのCPU負荷が問題になることがある。

水平分割

水平分割は、ある特定の種類のデータをレコードごとに分割し、それぞれのレコードを別のRedisノードへ配置する手法だ。 水平分割のことをシャーディングやパーティショニングと呼ぶこともある。

レコード分割の手法は様々であり、後述するRedis Clusterのようなミドルウェア側で分割する機構がないもしくは利用しない場合は、データの性質を利用する。 例えば、RedisにユーザIDに紐付いたデータを格納するとして、ユーザのIDをノード数で割った剰余の値により、配置するノードをマッピングするといった方法がある。 下記の図はノード数が3の場合を示している。 他にはユーザ名の頭文字がaの場合はノード1、bの場合はノード2といった分割の方法もある。

|------------------------------------------|
|              |----> redis (id % 3 == 0)  |
|              |                           |
| client ------|----> redis (id % 3 == 1)  |
|              |                           |
|              |----> redis (id % 3 == 2)  |
|------------------------------------------|

水平分割は、垂直分割同様に、CPU負荷の分散以外に、メモリ使用の分散にも利用できる。

この手法のデメリットとして、ノードの負荷の偏りと、リシャーディングの困難さの2つがある。

ノードの負荷の偏りは、特定のユーザ(もしくはブログIDや記事IDなど)に関するコマンドは必ずマッピング先のノードに向けて発行されるため、特定のユーザの活動が非常に活発な場合、負荷が特定のノードに偏ってしまうという問題がある。

リシャーディングの困難さは、ノード数を増減させるオペレーション(リシャーディング)に非常に手間がかかることだ。 マッピングの手法にもよりますが、上記のような剰余ベースでマッピングしていると、リシャーディング時に既存のマッピングが変更される。 既存のマッピングが変更されると、すでに書き込み済みのデータを新しいマッピング先のノードに移動する必要がある。 最もナイーブな方法で移動するなら、新旧のマッピングで配置が変更されるレコードを抽出し、新マッピングにしたがってデータを移動するバッチスクリプトを実行するといった手段がある。 バッチスクリプトを流す時間だけサービス停止できればよいが、これをオンラインでやろうとすると大変だ。 例えば、新旧のマッピングを意識したアプリケーションに改修しつつ、裏で新マッピングへの移動スクリプトを流すといった泥臭い運用が待っている。

さらに、ノードの負荷の偏りを平滑化する(リバランス)となるとさらに困難なことになる。 このリシャーディングとリバランスの難しさから、できるだけ水平分割を選択しないようにしている。

しかし、Redisをキャッシュとして用いる場合は、リシャーディングはもうすこし簡単になる。 レコードにTTLを設定しておき、マッピングが変更された後に、たとえキャッシュミスしたとしても、キャッシュ元のなんらかのデータソースからデータを引く実装になっていれば、アプリケーションの動作としては問題ない。TTLにより、旧マッピングのデータがずっと残っているということもない。 キャッシュミスをできるだけ抑えたい場合は、consitent hashingを使うと、リシャーディング時にマッピングの変更を少なく抑えられる。

Redis Clusterによる水平分割

Redis Cluster9は、複数のRedisサーバに対してデータを「自動で」水平分割する。 前述の水平分割の場合は、アプリケーション開発者がレコードを分散する処理を書き、リシャーディング運用をしなければなかった。 Redis Clusterを使うと、自動でキーとノードのマッピングを作成され、アプリケーションからのコマンドは自動で分散される。 さらに、redis-tribというRedisが提供しているオペレーションスクリプトを使ってリシャーディングとリバランスができる。

しかし、Redis Clusterにはdatabaseと複数キーのコマンドの扱いに制限がある。

まずdatabaseについて、複数のdatabaseを使えない制限がある。databaseはRedisのキー空間を分ける機能で、キーの重複を考えなくてすむため、データの種類ごとにdatabaseを分割するといった使い方をする。すでにアプリケーションが複数のdatabaseを使っている場合、途中からRedis Clusterに移行するには手間が発生する。

次に複数キーコマンドについて、複数キーにまたがるコマンドは、全てのキーが同じノード上になければならないという制約がある。 そこで、hash tagsを使うと、キー文字列の中の部分文字列を中括弧で囲うことで、同じ部分文字列をもつキーであれば、同じノードにマッピングするようになる。 したがって、hash tagsにより、キーとノードのマッピングをある程度開発者がコントロールできる。 パイプライニングやLuaスクリプティングについても、基本的に操作対象の全てのキーが同じノード上にある必要がある。 ただし、一部のクライアント10では、同じノード上にキーがなくても、複数のノードにクエリを投げてマージするといった実装が入っていることがあるようだ。

これらの制限に加えて注意する点は、水平分割の項で説明したノードの負荷の偏りを、Redis Clusterを使ったからといって防げるわけではないということだ。 システムのワークロードによって、よくアクセスされるキーが存在すると、特定ノードへ負荷が偏ることは避けられない。 ただし、slotを別のノードへ移動させるコマンドにより、負荷の高いslotを移動させてマニュアルで負荷の偏りをある程度均すことは一応可能だ。

ちなみに、Redis Clusterは、Mackerelの時系列データベース11で利用している。 まだ投入して間もないため、運用ノウハウを積んでいくのはまだまだこれからというところだ。

その他

上記カテゴリのいずれにも該当しないチューニング例として、RPS12とCPU Affinityによるネットワーク割り込み負荷をRedisプロセスが処理するCPUコアとは別のコアへ分散がある。 この手法は、はてなのLinuxネットワークスタックパフォーマンス改善事例13の後半で紹介している。

スライド資料

あとがき

発表から少し時間が空いてしまいましたが、この記事の内容は、Kyoto.なんか #3での発表を加筆修正したものになります。

これらの内容は一人でやったものではなく、自分は主にアーキテクチャや作戦を考える係で、アプリケーションの実装は主に同僚のid:mechairoiさんとid:itchynyさんによるものです。 真のRedisパイプライニングについては、同じく同僚の id:ichirin2501 さんに教えてもらいました。

Redisは好きなミドルウェアで、[^11]でもお世話になっています。 オンディスクDBは、RDBMSならMySQLやPostgreSQL、分散データストアならHBase、Cassandra、Riak、Elasticsearchなどさまざまな選択肢がありますが、インメモリDBはウェブ業界の中では実績や使いやすさを考えるとRedis以外の選択肢をあまり思い付きません。

しかし、Redisがこれほど利用されているにもかかわらず、議論の土台となる運用をまとめた資料がないと感じていました。これは、単に自分が発見できていないだけかもしれません。 以前から、Webの技術を自分の中で体系化することに興味があり、過去の試みには 2015年Webサーバアーキテクチャ序論 - ゆううきブログWebシステムにおけるデータベース接続アーキテクチャ概論 - ゆううきブログ といった記事があります。 そこで、今回は、RedisのCPU負荷対策についてまとめまてみました。メモリやネットワークI/O全般について書ければよかったのですが、まだ知見が及ばないところも多いので、一旦CPUの部分のみにスコープを絞りました。

ところで、今年のはてなサマーインターン2017の大規模システムコースでは、Serfで使われているGossipプロトコルベースの自律分散監視や、グラフDBのNeo4jを用いた分散トレーシングといったとてもおもしろい成果がでています。

参考資料

時系列データベースという概念をクラウドの技で再構築する

サーバ監視サービスMackerelにおいて開発中の、高解像度・長期間のサーバメトリック収集を実現するための新しい時系列データベースDiamondを紹介します。具体的には、Amazon ElastiCache、Amazon DynamoDB、Amazon S3を組み合わせ、Amazon Kinesis StreamsとAWS Lambdaによりコンポーネント間を接続した、階層構造のデータストアアーキテクチャの設計と実装を解説します。

はじめに

先日開催されたAWS Summit Tokyo 2017にて、「時系列データベースという概念をクラウドの技で再構築する」というタイトルで登壇しました。 この記事では、講演した内容に加えて、時間の限られた講演では触れられなかった内容を併せて議論します。

タイトルに時系列データベースとあるので、時系列データベースの話をもちろん書きます。しかし、AWS Summitでの発表ベースの記事なので、「AWS」を用いたシステム設計という切り口で話を進めます。 具体的には、Write-Intensive Application(書き込み要求が支配的となるアプリケーション)をAWS上で設計するための議論の土台になればと考えています。 時系列データベースは比較的単純なデータモデルを扱うため、よいモデルケースになるのではないかと思います。 時系列データベースとしての機能比較や実装の詳細についてはまた別の機会に紹介します。

背景

Mackerelでは、成長しつづけるサービスのスケーラビリティの確保と、データ量とI/Oがひと桁またはふた桁増加するような機能実現が求められています。

Mackerelの重要な機能のひとつに、メトリックのグラフ化機能があります。 開発中の新時系列データベースは、このグラフ化機能について、より高解像度でより長期間のメトリック収集と保持を実現しようとしています。 さらに、1つのグラフに1000以上のメトリックが含まれている状況でも高速に表示できるようにするなどの性能向上や、それ以外に、メトリックの異常検知など将来的な機能追加に対するワークロードに対して対応できるようにしたいと考えています。

Mackerelにおけるメトリックの収集とグラフ化の仕組みの概要は、以下の図のようになっており、前述の機能要求にとって、時系列データベースというコンポーネントが重要になってきます。

時系列データベースとはそもそも何でしょうか。時系列データベースとは、時系列データを扱うことに特化したデータベースであり、サーバモニタリングやIoTの文脈でのセンサーデータの収集のために使われます。有名な実装として、OpenTSDBInfluxDBGraphite などがあります。地味にみえて、多くの実装が存在し、学術研究論文になっているものもあります。id:rrreeeyyy さんの 時系列データベースに関する基礎知識と時系列データの符号化方式について - クックパッド開発者ブログ [3]が詳しいので、一読することを薦めます。

Mackerelで運用している時系列データベースについては、Mackerelを支える時系列データベース技術 - ゆううきブログ [4]の記事にまとめています。2年前の記事ですが、現状でも概ねこの通りの構成で運用しています。1台あたりピーク時で150k write IOPSを叩き出しており、サーバモニタリング向け時系列データベースの探究 / The study of time-series database for server monitoring // Speaker Deck [5]で紹介したようにパッチをあててパフォーマンスを改善してきました。

ところが、MackerelのGraphite運用には、以下の4つの問題を抱えています。

まず、スケールアウトのための運用コストが大きいという問題です。書き込みI/Oのスケールアウト手法として、水平分割(sharding)が一般的です。Graphite自体にはconsistent-hashingにより、水平分割する仕組みがあります。しかし、シャードの増減により、ハッシュキーとノードのマッピングが変更されたときに、データを再配置するための仕組みはGraphite本体には実装されていないため、運用でカバーする必要があります。

次の問題は、データ保持期間を増やすと金銭的なコストが激増するというものです。前述したようにスケールアウトのための運用コストが大きいことから、書き込みI/Oをスケールアップするために、NANDフラッシュメモリを利用しています。非常に性能が高い一方で高価なハードウェアなので、容量単価が大きいという特徴があります。 したがって、データ保持期間を増やすと、ディスク上のデータ量が増加し、安価なハードウェアにのせることに比べてお金がかかることになります。 一般の解決策は、圧縮してディスクに格納するか、既に書き込まれたデータを低速で容量単価の小さいストレージに移動するような仕組みにすることです。 前者の圧縮については、[3]で紹介されている差分符号化やXOR符号化のテクニックをGraphiteに実装すれば、実現できそうではあります。しかし、これらのエンコーディングにより、ランダムアクセスできなくなるため、Graphiteのラウンドロビンデータベース構造とうまく噛み合うかがわからないところです。圧縮は基本的に、CPU負荷とトレードオフになるため、CPU負荷がボトルネックとなる可能性もあります。 後者のデータ移動については、Graphiteのラウンドロビンデータベースの、古いデータを新しいデータで上書きしていくという性質のため、別のストレージに効率よくデータを移動させることが難しく、データ構造を大きく変更する必要があります。

3つ目の問題は、データロスト耐性が低く、フル同期のための運用コストが大きいことです。アプリケーションから書き込みすると、Graphiteのメモリ上のキューにデータが非同期に書き込まれるため、サーバダウンによりキューの中身をロストしてしまいます。デュアルライトにより、別のサーバには書き込まれているので、そちらからデータを同期するなど、運用でカバーしなければなりません。これを解決するには、GraphiteにWAL(Write Ahead Log)を実装するなど、大きな変更を強いられます。

最後に、新規にメトリックが投稿されたときに、未来の領域をファイル作成時に確保するので、ディスク使用効率が低いという問題があります。これは2つめの問題と関連します。なぜこのようなデータ構造になっているかは http://graphite.readthedocs.io/en/latest/whisper.html?highlight=rollup#disk-space-efficiency に書かれています。このデータ構造は、コンテナのようなImmutable Infrastructureの概念と相性が悪く、非効率です。

これらの4つの現状の問題点と、冒頭に書いたような新しい機能要求を考慮すると、根本的なアーキテクチャの刷新が必要だと考えました。

既存の時系列データベース

まず、既存の時系列データベースの調査から始めました。Andreas Baderらのサーベイ論文[1]やOpen Source Time Series DB Comparison[2]にオープンソースの時系列データベース実装がまとめられています。

時系列データベースの概念

前述のサーベイ論文[^1]では、時系列データベースの定義は、以下のような性質を満たすDBMSのこととされています。

  • タイムスタンプ、値、属性(メトリック名など)で構成されるデータの行を格納できる
  • 時系列としてグループ化された複数の行を格納できる
  • データ行に対してクエリを発行できる
  • タイムスタンプまたは時間範囲をクエリに含められる

この定義を満たすものを実装するのはさほど難しくありません。しかし、実際には、分散/クラスタリング、Function、Rollup Aggregation(自動丸め)、タグ、複数のデータ解像度、クエリ言語などの時系列データベースにまつわる機能要求があります。 特に、今回のようなスケーラビリティを確保する目的であれば、分散/クラスタリングが重要になってきます。

時系列データベースの分類

時系列データベースのOSS実装は大きく2つの分類があると考えています。それは、他の汎用DBMS上に実装されたもの(TSDB on DBMS)と、時系列データベースに最適化したストレージエンジンをもつもの(TSDB standalone)です。 前者にあたるものは、例えば、OpenTSDBやKairosDBなどがあります。一方、後者にあたるものは、GraphiteやInfluxDB、Prometheusなどがあります。 前者のDBMSとして、HBase、Cassandra、Riak、ElasticsearchなどのいわゆるNoSQLが使用されます。もちろん、TimescaleDBのようにRDBMSであるPostgreSQLをベースにした実装も存在します。前述の時系列データベースとしての定義を満たすだけなら、ACID保証が必要ないので、NoSQLを利用するケースが多いようです。

時系列データベースを実装する側の視点でみると、TSDB on DBMSは、分散/クラスタリング機構を自前で実装せずにすむため、実装が楽というメリットがあります。一方で、TSDB standalineは分散/クラスタリング機構も実装しなければならないというデメリットがありますが、TSDB on DBMSと比較して時系列データベースに最適化できるというメリットがあります。

時系列データベースを運用する側の視点でみると、たいていのTSDB on DBMSはCassandraなどの分散システムとしての運用コストが高いといわれるDBMSを利用しており、一方で、TSDB standaloneは分散システムとしての信頼性を測るための実績が少ないといえます。実際には、製品ごとに細かくメリット・デメリットがありますが、大雑把にこのように認識しています。

さらに、Mackerelで採用するという視点でみると、既存の時系列データベースをそのまま採用するデメリットとして、Rollup Aggregationに対応していないものや、インタフェースが大きく変わるので変更コストがある、Mackerelの現在または将来のワークロードに対してスケールしない可能性があります。

新時系列データベースDiamondのアーキテクチャ

方針

調査の内容を踏まえて、既存の時系列データベースを採用せずに、Amazon DynamoDBなどのAWSマネージドサービスを採用し、マネージドサービス上に独自の時系列データベースアプリケーションを実装するという方針にしました。 マネージドサービスを採用したのは、時系列データベースの分散/クラスタリング機構に必ずついてまわるデータベースの検証・運用コストを削減するためです。はてなでは、特定ベンダーのロックインをなるべく避けるという技術選択方針がありますが、今回は、Mackerelの成長速度や将来の機能要求を考慮して、ある程度のロックインを受け入れることにしました。 独自に、TSDB on DBMSにあたるものを開発することにしたのは、ひとえにMackerelの要求を満たす実装がないか、要求を満たすかどうかの検証に時間がかかるためです。AWSマネージドサービス採用によりデータベースの検証/運用コストを削減できるので、その代わりの開発工数を確保できると考えました。

この大方針に沿って、Graphite運用の4つの問題点を解決できるか考えます。

まず、負荷分散の運用コストの解決です。前述したようにDynamoDBなどのフルマネージドサービスの利用により、運用コストを削減できます。水平分割のためのキーの設計を間違えなければ、お金を払った分だけI/Oスループットがスケールするので、検証も比較的楽になります。 ところが、DynamoDBのI/O課金が高いため、今のMackerelのI/Oを工夫せずにDynamoDBに向けるとインフラコストが大きく増加します。 したがって、個々のデータポイントを別々に書き込むのではなくまとめて書き込み、I/Oコストを下げる必要があります。 最終的には、前段にElastiCache(Redis)を配置し、ライトバックキャッシュ(講演ではバッファと呼んだ)として利用することにしました。

次に、データ保持期間についての解決です。DynamoDBのディスク容量課金は感覚的には安く感じましたが、1分解像度のデータを年単位で保持しようとすると、そこそこのコストがかかります。 S3のコストはスタンダードストレージでもDynamoDBの1/10程度に収まるので、ここでコストを浮かせておけば、他の機能拡充にコストをあてられるため、なるべくS3を使いたいと考えました。 とはいえ、S3のレイテンシはDynamoDBに比べて1桁以上大きいかつ、DynamoDBよりも内部的なパーティションあたりのスループットが低い可能性があります(要確認 リクエスト率およびリクエストパフォーマンスに関する留意事項 - Amazon Simple Storage Service)。 Mackerelの場合、古い高解像度データはほとんど参照されないという参照局所性があるので、参照回数が小さいデータの表示は多少遅くてもよいという性質があります。 そこで、この性質を利用し、ホットデータをDynamoDB、コールドデータをS3に配置することでコスト最適化することを考えました。

この時点で既に、階層型データストアアーキテクチャになることが決定しています。

3つ目に、データロスト耐性の向上です。これは簡単で、メモリ上のキューではなく、ディスク書き込みするメッセージキューを採用することです。OSSなら、KafkaやRabbitMQなどがこれにあたり、AWSでは、Amazon Kinesis Streamsになります。内部的な実装としてはキューではないと思いますが、キューのように利用できます。Kinesis Streamsは直近の24時間分のレコードをディスクに保存し、投稿したレコードをLambda functionの引数に渡すことも簡単です。Kafkaほどレイテンシが低いわけではないそうですが、どのみちMackerelのデータポイントはインターネットごしに投稿されるので、もともとユーザに反映されるまのでレイテンシが大きいので、無視できる程度のレイテンシの差だと考えました。キューというよりは、Using logs to build a solid data infrastructure (or: why dual writes are a bad idea) - Confluentに書かれているように、アプリケーションレベルのWAL(Write Ahead Log)を前段に配置していると捉えてもよいかもしれません。

最後に、ディスク使用効率の向上です。容量問題はS3の利用で解決できるので、ラウンドロビンデータベース構造にする必要がなく、未来の領域を確保することはしません。 圧縮や符号化については、RedisやDynamoDB上でレコード単位で圧縮しようとすると、レコードに対するデータポイントの更新時に、レコード内のデータを全て読み込んでからデータポイントを更新し、レコードの内容を差し替えるということをしなければならないため、余分なread I/Oとネットワーク帯域を圧迫する可能性があります。OSのファイルのようなseekできるストレージであれば、必要な分だけ読み出せるので、効率が良いと考えます。(しかし、これを書きながら、データサイズが小さくなっているのだから、ネットワーク帯域はさほど気にならない可能性もあるということに思いあたりました。)

さらに、時系列データを読み出すコンポーネントをMicroservicesとして開発しています。Graphite互換のインタフェースを実装し、新システム移行時のインタフェース変更コストを小さくしています。1つのクエリに1000以上のメトリックが含まれることがあるため、各メトリックのタスクを並行処理できるように、軽量スレッドでI/O多重化できるGo言語を実装言語として選択しました。

設計

前節の方針を元に、新時系列データベースのアーキテクチャの設計の概要を以下にまとめます。

  • 金銭コスト最適化のために参照局所性を利用する。データ参照頻度に応じて適切なデータストアへデータを移動させる
    • ホットデータをDynamoDB、コールドデータをS3に配置
    • ライトバックキャッシュのストレージとしてElastiCache(Redis)を利用
    • DynamoDBのTTL機能によりシームレスにデータ移動
  • Kinesis Streamsに対して先行ログ書き込みし、データロスト耐性と耐障害性の向上させる
  • Graphite互換インタフェースにより、各ストレージからデータ取得するWebアプリケーションを実装する

アーキテクチャ図を以下に示します。

このように複数のデータストアを組み合わせる場合、データをシンプルな手法で移動させるためのアイデアが必要です。 2017年2月にDynamoDBがTTLサポートされました。 TTLがexpireしたレコードをDynamoDB Triggers経由でLambda functionに渡すことができるため、簡単に古いデータをS3へ配置することができます。

この機能がリリースされたとき、ちょうどデータ移動の仕組みに困難さを感じていたところでした。当時は、タイムウィンドウごとにDynamoDBのテーブルを用意して、古いタイムウィンドウテーブルをテーブルごとDataPipelineによりS3にエクスポートすることを考えていました。いわゆる、MySQLで日時カラムに対してパーティションを切り、古いパーティションをパーティションごとDROPするという手法に近いと思います。 テーブルファイルがS3に配置されても、巨大なテーブルファイルに対してクエリするのは現実的ではないため、テーブルファイルをレコード単位に分割してS3に保存し直すような手間のかかる処理が必要でした。 テーブル単位による管理を強いられるため、アイテム単位で細かくデータ配置を制御したくなったときに、どうしようもなくなるという問題もありました。

古くなったデータを別のストレージに移動したいというのは一般的な要求なので、データベースのTTLとイベント通知というサーバーレスアーキテクチャらしい仕組みは、汎用的に利用できるアーキテクチャだと考えています。

実装

実装についての詳細は、スライドを参照してください。時系列データベース固有の話になります。Redis/DynamoDB上のスキーマ設計、Redisによるライトバックキャッシュ、Graphite互換アプリケーションのI/O多重化について簡単に紹介しています。

考察

Write-Intensive Applicationとして、例えばログ収集や、チャットメッセージングなどがあります。ログやチャットメッセージと違い、メトリックのデータポイントは固定長のデータなので、データ構造を最適化しやすいという特徴があります。したがって、可変なデータを書き込むアプリケーションの場合、時系列データベースでは考えなくてよかったようなデータ構造の工夫が必要かもしれません。

サービスの仕様変更により、データ構造を変更したくなったとき、3箇所にデータを保持していると、それぞれ変更を加えなければいけないというデメリットがあります。これについては、場合によっては、今あるデータ構造に追加の変更をするよりは、前段のKinesis Streamsから別のストレージに最適な構造で書き込むことも考えられます。

3層構造のデータストアアーキテクチャを提案しましたが、必ずしも3層にする必要はないと思います。ディスク使用が少なければElastiCacheとDynamoDB、書き込みI/Oが少なければ、DynamoDBとS3、readレイテンシを気にしなければ、RedisとS3というように、2層で対応できることもあるでしょう。 ちょうど手元のマシンでRAM+SSD+外部ストレージで大抵のことが事足りるように、3層あれば多くの状況でコスト最適化できると考えています。

DynamoDBにはDAXというライトスルーキャッシュが最近実装されました。もし、DAXにライトバックキャッシュが実装されれば、DiamondアーキテクチャのうちRedisの層は不要になるかもしれません。

その他、運用の観点で、スナップショットバックアップについては、ElastiCache自体にバックアップ機構があり、DynamoDBでは前述のDataPipelineによるS3への定期バックアップが可能であり、S3上のファイルは一旦作成されたら基本的に更新しないので、スナップショットバックアップはそれほど必要ないと考えています。

さらに、Lambda functionの実装にバグがあるとデータロストの危険性がないかについては、前段のKinesis Streamsに24時間(追加コストで1週間)データ保持されるので、原理上はそこからリカバリできるはずです。functionの実行がエラーになるケースでは、functionの実行がリトライされます。ただしこれには、functionの実行がべき等である必要があります。Diamondの場合、RedisとDynamoDBのデータ構造がタイムスタンプをキーとしたハッシュマップになっているので、データポイントの書き込みはべき等になっています。

Diamondアーキテクチャのデメリットとして、構成要素が多いことが挙げられます。Kinesis streamsもあわせると、4つのデータストアを併用することになります。一般に構成要素の数が増加すると運用コストも増加しますが、マネージドサービスの利用やCloudFormationのようなInfrastructure As Codeの利用により、運用・管理コストを抑えられると考えています。 しかし、Diamond自体のモニタリングは複雑になるため、モニタリング手法もあわせて提案する必要があると考えています。

資料

プレゼンテーション資料

参考資料

あとがき

「時系列データベースという概念をクラウドの技で再構築する」というタイトルは、少年ジャンプで連載中の食戟のソーマの第115話で、主人公 幸平創真の「親子丼という概念をフレンチの技で再構築する」という台詞から拝借したものです。 主人公は定食屋のせがれで、定食屋としての技術の限界を超えるため、フランス料理の技術を学び、日本の庶民の料理である親子丼を抽象化して、フレンチの技を用いて再構成するというお話になっています。 自分では、この話をとても気に入っていて、今回、ちょうど同じように、時系列データベースの構成要素を取り出し、マネージドサービスやサーバレスアーキテクチャなどのクラウドならではの技術を用いて再構築していると感じ、このタイトルをつけました。

今回話をしたかったのは、単に使っているOSSを列挙するようなものや込み入った実装の話ではなく、アーキテクチャの話です。 最近、アーキテクチャと実装の違いについて考えているのですが、まだあまりよい答えをもっていません。少なくともいえるのは、アーキテクチャには汎用性があり、実装するソフトウェアだけでなく他のソフトウェアにも通用するなにかがあるということです。 アーキテクチャの観点では、Webアプリケーションは参照局所性にあわせて複数のデータストアを併用するのは昨今では当たり前なので、今回のようなTTL + Lambda Functionのようにそこにもっとサーバレスというかイベント駆動な考え方を持ち込み、体系化できないかということを考えていたりします。

Mackerelでは、時系列データベース以外にも、おもしろいソフトウェアアーキテクチャを実現できるチャンスがまだまだたくさんあります。僕の場合は、最近は機械学習を支える効率のよいシステムやグラフDBのアーキテクチャに興味があります。 はてなでは、アーキテクチャを設計・実装し、本番投入し、運用まで持っていくことに興味があるエンジニアを募集しています。

また、今年のはてなインターンでは、Mackerelやはてなのインフラストラクチャを支えるシステムを開発できる、「大規模システムコース」と「クラウドサーバ管理システムコース」があります。 はてなサマーインターン2017

追記

2017/11/03のServerlessconf Tokyo 2017にて、リリース後の監視・運用の話を紹介しています。

matsumotoryさんの博士学位論文公聴会に参加し、見て聞いて考えたこと

先日、京都大学で開かれた id:matsumoto_r さんの博士学位論文公聴会に参加してきた。

博士学位論文公聴会というものを、そもそも僕はあまりよく知らなかった。とりあえず、博士の学位を取得するための最後のステップと認識している。厳密には、この後にもいろいろあるらしいのだが、その日のうちに学位取得の合否が決定され、晴れてmatsumotoryさんが合格された様子をその場でみることができた。本当におめでとうございます。

発表内容について

博士論文や当日の発表スライドはそのうちmatsumotoryさんが公開されると思う。発表内容自体は、当たり前だけど、以下の予備審査のスライドとほぼ同等のものだった。

Webサーバの高集積マルチテナントアーキテクチャに関する研究 / Studies on Highly Integrated Multi-Tenant Architecture for Web Servers // Speaker Deck

matsumotoryさんの研究内容のストーリー全体を俯瞰する発表を聴くのは、今回が初めてだった。 これまで、個々の要素、例えば、mod_mruby/ngx_mrubyやmod_process_securityなどは多少理解していたつもりだったものの、その背景には大きなストーリーがあり、それぞれの要素がどのように配置されているのかといった論理構造が浮かび上がってくるようだった。 同じITシステムの運用にたずさわっていても異なるコンテキストをもつ僕が、自然と論理構成を理解できて、その後の質疑応答の内容もほぼすべて理解できた。これは大きなことだと思う。

発表の冒頭にもあるように、対象となるシステムは、事業者がアプリケーションを制御できない状況設定におけるアーキテクチャの提案となっている。僕が普段相手にしているシステムは、事業者がアプリケーションを制御するのが当たり前になっている。自社で開発しているサービスをデプロイし、運用する事業形態だ。

これは僕にとって発表を聴く直接のメリットがないようにみえるが、その逆で、適応領域が異なるほうが、むしろ抽象的なアーキテクチャを議論するヒントになりやすい。 実際、これのおかげで、この前のIPSJ-ONEにおける発表では、matsumotoryさんが提唱されているなめらかなシステムとの対比から様々なヒントを掴み、自分の思考の枠組みを一歩超えるような体験ができた。

さらに、matsumotoryさんの貢献の一つである「Webサーバの高集積マルチテナント方式の基本概念と課題を体系的に整理」をみて、ITシステムの運用というのは、そもそも体系からつくらないといけない分野であることを再認識できた。 そして、自分が過去に書いた以下の記事では、事業者が制御可能な前提でのシステム運用の体系を作りたかったのではないかと思うようになってきた。

僕がこのようなことを考えていることそれ自体が、matsumotoryさんの貢献でもある気がする。

質疑応答について

質疑応答は1時間弱くらい続いていたかもしれない。あのmatsumotoryさんが若干怯えた様子で、応答されていたのをみて、博士課程の予備審査にいってきました - 人間とウェブの未来 に書かれていることを思い出した。

入学資格審査や中間発表では、研究の突っ込んだ話は当然のこと、「アカデミックはどこにあるのか」「普遍的な学術的貢献はなんなのか」「工夫なのかアイデアなのか」といったように非常に抽象度が高く、汎用性・一般性に基づいた質疑が飛び交います。

博士課程の予備審査にいってきました - 人間とウェブの未来

特にセキュリティの観点で、厳しい質問があった。それ以外にも、高集積というアイデアはWebホスティング以外にも適用できないか、これからのLinuxに必要な機能はなにかといった、抽象的な質問が飛び交っていた。

参加するからにはどうにか自分もそのレベルで、なにか今後のmatsumotoryさんの研究活動に貢献できるような質問をしようと久々に発表を聴きながら頭をひねって、以下のような質問をした。

  • 第2章と第3章で提案されているアーキテクチャについて、chroot以外にLinux Namespacesを用いてよりセキュアなシステムにすることは可能か
  • Linuxのアーキテクチャに依存したアーキテクチャならば、Linux自体のアーキテクチャの限界に引きずられた結果、提案されている高集積マルチテナントアーキテクチャの息が短くなることはないか。他のOS前提でのアーキテクチャのほうが、高集積マルチテナントに有利になることはないか。

これらの質問は、実装よりアーキテクチャで解決するという最近の自分の関心事から端を発している。 また、最近社内で輪読している詳解システム・パフォーマンスにて、Solarisが登場する場面が多く、普段さわっている実装がどこまでLinux依存なのかといったことを考えていたためである。

この文脈では、スレッド単位での制御がOSレベルで可能なアーキテクチャであればよくて、カーネル内部でのプロセス表現として知られているtask_struct構造体は、実体としてはスレッドを表現しているはず(要確認)なので、Linuxのアーキテクチャの限界にはおそらくあたらないのではということを軽くあとで会話した。この話はちょうど、輪読会でも議論した。詳解システム・パフォーマンス 3章「オペレーティングシステム」メモ - ゆううきメモ

ところで、その日のmatsumotoryさんのツイートによると僕が強烈な質問をしたことになっていて、おもしろかった。優しく見守った結果のはずなんですよ。

アウトプットについて

エンジニアのアウトプットとは何なのかということを最近というかずっと考えていて、今回、公聴会に参加して、一つの考えがまとまった。

普段からウェブエンジニアのブログ、プレゼン、OSSをみる機会は非常に多いのだけれど、「知識」と「経験」に関するアウトプットに恵まれている一方で、「思考」に基づいたアウトプットが少ないのではないかと感じている。つまり、「知ったこと」と「やったこと」そのもののブログやプレゼンは多いのだけど、自分で問いを立てて「考えたこと」を表現することがあまり重視されない傾向にあるのかもしれない。これは単に数あるアウトプットから僕がその人の考えを読み取れていないだけという可能性もある。

問いを立てて思考した結果は、なんらかの新規性を持っているように思う。 例えば自分の身の回りだと、mizzyさんのServerspecや、同僚であるitchynyさんのsjsp Mackerelにおけるフロントエンドのパフォーマンス改善の取り組み - Hatena Developer Blog であったり、はてなで採用されている実装のないフレームワーク ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011 であったりする。 これらに比べればずっと小さな成果だけれども、自分の場合だと Dockerとchrootを組み合わせたシンプルなコンテナデプロイツール - ゆううきブログ がこれに該当する。

今回の公聴会では、知識と経験をベースに思考を重ねたアウトプットをストーリーとしてみることができた。 そして、このようなアウトプットこそがエンジニアを大きく成長させるようなアウトプットだと確信するようになった。

自分で考えたことというのは、後ろ盾がない状態なので、人にみせるには勇気がいる。 しかし、小さな成果でもよいから、それを乗り越えて、その人らしいアウトプットをみれるとうれしくなる。 これから、自分もこういったアウトプットを志していきたい。

あとがき

matsumotoryさんのことは、自分のブクマを振り返ってみると、5年ほど前から存じ上げていたようだった。 5年前というのはちょうど、人間とウェブの未来 - Linuxエンジニアを辞めて大学院に入学しました の記事にあるように、大きな挑戦をされた年だった。

一方そのころの僕は、はてなでアルバイトし始め、ほそぼそとブログを書き始めた年で、その年の振り返りに

最後のやつ,いつも読んでるブログの,"人間とウェブの未来"の中の人にブクマしてもらえて,おおー ってなった. 忘年した2012年の記憶を掘り起こして忘年意味ないみたいな感じになった - ゆううきブログ

というようなことを書いて喜んでいたようだった。

その翌年は、大学院を中退し、そのままはてなに入社したのだけれども、そのときも、以下のような言葉をかけていただいたのをよく覚えている。

大学院の修了に失敗し、新卒入社にも失敗しました - ゆううきブログ

こんなに怒られるような寛容でない場所は辞めても良いと思う。ブログを見る限りこの方を必要とする企業は沢山あると思うので頑張って欲しい。

2013/11/21 14:05
(しかし、この記事は雑すぎて、新卒入社(4月入社)には失敗しただけではてなには入社しているということを伝える気のない書き方だった。このときは、会社の印象が悪化するのを気にして、はてなっていう単語を出さないように勝手に気遣った記憶がある。)

そこから気づけば、レンサバの常識を覆す「なめらかなシステム」に挑む──運用技術を学術研究して実際のサービスへ適用 - はてなニュース のように一緒にインタビューを受けたり、IPSJ-ONEでバトンを渡していただいたりした 高度に発達したシステムの異常は神の怒りと見分けがつかない - IPSJ-ONE2017 - ゆううきブログ

振り返ってみると、同じ組織に属したことはないし、直接お会いした機会はこれまでせいぜい6,7回程度しかない。 親炙と私淑という言葉を最近知ったのだけど、もともと私淑していたところを徐々に親炙するようになった、よい意味でインターネット的な関係といえるかもしれない。

以上のような背景のもとに、matsumotoryさんの一つの挑戦の集大成をみることができて、なんだかうれしくなってこの日記を書いた。

高度に発達したシステムの異常は神の怒りと見分けがつかない - IPSJ-ONE2017

名古屋大学で開催されたIPSJ-ONE2017 で登壇しました。 IPSJ-ONEというのは、情報処理学会の各研究会から選ばれた日本の若手トップ研究者17人が集まり、自身の研究を高校生でもわかるように発表するイベントです。 1000人ぐらい入る講堂で、しかもニコニコ生放送で配信されるというとても大掛かりなイベントです。

ちなみに、昨年は、同じ研究会からの推薦で、 id:matsumoto_r (matsumotory) さんが登壇されています。 IPSJ-ONE 2016で登壇してきた - 確実に時代は変わってきている #ipsjone - 人間とウェブの未来

発表

「高度に発達したシステムの異常は神の怒りと見分けがつかない」という、一見何の話かわからないやばそうな話なんですが、大真面目に話してきました。

スライドを以下に公開しています。ただ、スライドだと何の話をしているかおそらくわからないので、ニコ生のタイムシフト試聴でたぶん一週間くらいは動画をみれるようです。 http://live.nicovideo.jp/watch/lv289955409#6:40:12 (その後におそらくアーカイブされるはず?)

IPSJ-ONEの趣旨として、登壇者個人の成果や考えを表に出すことを求められており、実際には会社やチームでやっていることの中で、僕個人がやっていることについて話をしたつもりです。

5分の発表なので、10秒レベルで無駄を削って、エッセンスのみを抽出しました。特に、一般の方がわかるような内容であることに最重視しており、専門用語を使わないように徹底しました。

内容と補足

補足を追加した内容を以下に書き留めておきます。

インターネットのシステムは、24時間365日動き続けることが求められている一方で、人手による運用でカバーしている部分がたくさんあります。 これをどうにかしたいと思って、全自動化へ向けて頑張るんですが、全然うまくいきません。 なぜうまくいかないのか。それは、自動化に対する恐れがあると考えています。 実際に現場では、このオペレーションを自動化したいけど、本番にいれるのはかなり検証つまないと怖いよね、というような話を頻繁にしています。 もちろん、開発工数がとれないとか別の次元の話もありますが、怖さを取り除くまでに手間がかかると言い換えることもできます。

なぜそんなに恐れるのか。今のインターネットのシステムは、極めて高度に発達しており、生物/自然を連想するような複雑な系をなしています。 この自然は普段はおとなしいですが、時として天災のような異常となって我々人間に襲いかかってきます。 情報システムの場合は、システム障害ですね。 このような異常が発生するために、人は未知に対する恐れを抱いてしまいます。 これに似た構造があり、古来より人々は、例えば神話のような世界で、未知の異常を神の怒りと表現していました。 実際には、雷や地震といった災害になるかと思いますが、今ではこれらの現象が神様の仕業だと考えることは少ないように思います。 そのギャップを埋めたものは何かと考えたときに、思い浮かんだのが、「観測」でした。

「観測」することで、わからないものがわかるようになった事例はたくさんあります。 情報システムの世界では、「観測」にあたるものを監視と呼びます。 監視というのは、人間でいうところの健康診断に似ていて、人間の場合は、体重とか血圧とかその他もっと細かいメトリックを計測しチェックします。 ウェブサービスのような情報システムの場合は、CPU利用率とかメモリ使用量とかそういったものになります。 これらの情報をなるべく頻繁に大量に集めるためには、専用のデータベースが必要です。いわゆる時系列データベースというものになります。

このデータベースの課題として、お金と性能というトレードオフに取り組んでいます。 観測により、自然科学の観測のような世紀の大発見があるわけではなく、ソフトウェアのバグだとかリソースが足りないとか、相対的に価値の低いものしか得られないので、お金が重要になってきます。 発表では、簡単にアイデアを紹介していますが、より具体的には サーバモニタリング向け時系列データベースの探究 / The study of time-series database for server monitoring // Speaker Deck をみてください。(ところで、ここからさらに発展した内容を5月にそれっぽい舞台で発表することになっています。)

さて、観測の次にやりたいことがあり、それは「実験」です。要は、コンピュータシステムの未知について、科学のアプローチを実施しようというような考えです。 実験というのは、Netflixの Chaos Engineering から連想しています。 ただし、Netflixの一連のツール群を使っているという話はあまり聞きません。本番に導入するにはやはりハードルが高いと感じています。 そこで、観測して得たデータを使って、システムモデルを構築し、シミュレーションのような安全な手段で、システム特性を明らかにし、弱点などをシステム本体にフィードバックするというビジョンを考えました。 ここは本当にジャストアイデアの段階で、例えばモデルって具体的になんですかと言われても答えられません。今のところ、フィードバック制御の理論あたりからヒントを得られないかと考えています。

このようなシステムを作って、人間をコンピュータの未知への恐れから解放したい、そう考えています。

神の怒りというのは、比喩ではありますが、単なる比喩ではなく話の根幹となるような話作りを意識しました。 観測と実験という概念は、最初から考えていたわけではなく、神の怒りからの連想ゲームから、うまく情報システムに落とし込める概念を見つけられたと思っています。

ipsjoneとは関係なく、昨年matsumotoryさんが発表されたなめらかなシステムとは違うものを提案しようとずっと考えていました。 (なめらかなシステムについては レンサバの常識を覆す「なめらかなシステム」に挑む──運用技術を学術研究して実際のサービスへ適用 - はてなニュース)

残念ながら僕は名前を付けられませんでしたが、観測と実験というのをテーマにこれからやっていこうと思います。

いただいた反応

別分野の研究者の方からおもしろかった、スライドがよいとフィードバックいただいたのはうれしいですね。 その他、また聞きですが研究者の方から、一番おもしろかった、我々は神の怒りと戦っていかねばならん、というようなコメントをいただいたと伺っています。 システムビジョンにも共感の声があり、思った以上に伝わっているという手応えを得ることができました。

登壇に至るまで

さて、そもそもなんでお前がそんな場所で発表しているんだという話ですね。 僕はそもそも研究者どころか、修士課程を中退しています。 そこからはてなに入社し、いわゆる現場で運用をやっているエンジニアをやっています。今月にEOLを迎えるCentOS 5の撤退みたいな10年前のシステムを相手にすることもやっていたりしました。 技術者としての実績はそれなりに積んでいるつもりですが、アカデミアにおける実績はほとんどありません。なぜこんな人間が採択されたのかは、今だによくわかっていません。

きっかけは、昨年の登壇者である matsumotory さんに、昨年の6月ごろに、IPSJ-ONEというものがあるので、登壇しないかと声をかけていただいたことです。そのときは、雑にいいですよみたいなことを言っていました。 そのときは、いつもの話で大丈夫と言われた記憶があるのですが、ある意味いつもの話ではあるものの、とんでもない無茶振りであることが後々わかってきます。 その頃はIPSJ-ONEというものがどういうものかあまりわかっておらず、matsumotory さんがちょっと前に登壇したやつぐらいの認識でした。 家に帰って改めて2016年の動画をみていたら、おいおいこれなんか違うやつなんじゃない?、そもそもトップの研究者が集まるとか書いてるじゃんって思いはじめて、その話を忘れていた11月ごろに、matsumotory さんから声がかかってきました。

今、声をかけていただいたDMを見直してみると、めちゃめちゃ大風呂敷を広げた上で、それを回収するみたいな方法で頑張ったのでそれがよさそうと書かれていて、本当にその通りになって笑ってしまいました。加えて、y_uukiが一番得意でかつ、そこに本番までに+アルファできるようなものを提案できると最高とも書かれていました。これも、僕の認識ではその通りになっていて、結果的に、DiamonDBの実装に加えて、観測と実験によるフィードバックループを回すというアプローチの紹介ということになりました。

しかし、最終的に自分のやっていることが世界にとってどういう意味があるかということを加えるとよいという話もあったのですが、ここは本番では組み込めなかった部分です。自動化されて、運用から解放されて、遊んで暮らせるようになるだけなのか?という漠然とした疑念はありました。科学の歴史をみてみると、例えば未知であった電気現象を逆に利用して、今ではコンピュータのような複雑なものを動かすことができるようになっています。そこからさらにメタなレイヤで、同じようなことが起きないかといったことを考えているのですが、これはこれからの宿題ということにします。

そこから、IOT研究会のほうで講演をしないかとという話になったので、招待講演させていただくことになりました。その経緯はブログに書いています。 情報処理学会でウェブオペレーション技術について招待講演した話 - ゆううきブログ

ipsjoneの5分は、僕の発表経験でも3本の指に入る大変さだったので是非頑張ってくださいとも書かれていました。はい、がんばりましたよ。最初は、そんなに大変なことがわかっていなかったのですが、準備を進めるたびに、とにかく大変であることがわかってきました。僕の感覚では、6ヶ月連続200users以上のブログ書いたり、1000usersのブログ書くよりはるかに大変でした。

何が大変だったのか。今までのアウトプットは、自分がやったこととやったことを汎用的な視点で捉えるだけで済みました。 しかし、今回は逆に未来を描いて、自分がやっていることに紐付けるという、頭が痛くなるような手法でした。 しかも、それを一般向けかつLTで伝えることは、余分なものを削ぎ落として、専門家だからこそ難しく書きたくなるようなことを削っていく作業になりました

さらに、僕は自身のテーマというものが、そもそも固まっているわけではありませんでした。ざっくりWebオペレーション技術、SREといった職種/分野の専門性を高めているという程度で、会社の仕事とはまた別の軸で何をやりたいのか、ということを発表準備を通してつくろうとしていました。 テーマがなければ実績もないわけで、実績がある研究者はいいな〜と思いながら、matsumotoryさんの去年のスライドをみると、それまで積み上げてこられた実績が1ページにさくっと書いているだけで、あとはこれからやっていくことが書かれていました。

ようやく、matsumotoryさんの無茶振り具合がわかってきて、初見ハードモードで放り込まれたような気分になっていきました。matsumotoryさんには感謝しています。

ところでこれはトークが採択されたときのmatsumotoryさんの様子です。

f:id:y_uuki:20170321233516p:plain

あとがき

スライドだけみると、なんてことのない単なるライトニングトークにみえるかもしれませんが、その裏でいろいろと試行錯誤したことを記しておきたかったという気持ちで、この記事を書きました。

前述したように、最もこだわったことは、一般の方にちゃんとわかるように話をするということです。 見て動くような内容であれば、工夫をこらさなくてもなんとなく伝わります。我々がいつもやっているような、見えないものをどう伝えるのか、ということを深く考えさせられました。

実は、事前に社内の勉強会で発表したときは、驚くほど無反応でした。だいたい予想はしていたものの、考えすぎて全然だめなのかなーと思って、かなり落ち込んでいました。

しかし、そこから5分の制約を相手に、ボツスライドのほうが多くなるぐらいブラッシュアップを繰り返して、何度も練習して、全然専門家じゃないはずの両親がニコ生で発表をみていて、内容がわかった、わかりやすかったと言ってもらえたことで、発表してよかったと思えるようになりました。

フィードバックの数で言えば、普段書くブログのほうが圧倒的に多いです。しかし、普段のブログや発表はコンテキストを共有しているので、フィードバックが多いのは当たり前です。ちょうど id:Soudai さんが書かれているように、アウェーの舞台で伝えることに向き合った結果、伝えることの難しさ/うれしさを再確認できました。

敢えてアウェーで戦う事に意味があるって話 - そーだいなるらくがき帳

最後になりますが、ipsjone運営委員の皆様のご尽力には頭が上がりません。1回目を立ち上げるのももちろん大変なことだったと思いますが、その後「継続」することは、立ち上げ時の苦労とはまた異なる種類の苦労をされたのだろうと、「運用」を生業とする者としてご推察します。本当にありがとうございました。