サーバ「管理」ツールとしての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によりコンポーネント間を接続した、階層構造のデータストアアーキテクチャの設計と実装を解説します。

2018/06/05 追記: この記事の内容をWSA研#2でより一般的なアーキテクチャレベルでの貢献として書き直しました。 サーバレス時代におけるヘテロジニアス時系列データベースアーキテクチャ - ゆううきブログ

はじめに

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

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

背景

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

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

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

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

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

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

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

次の問題は、データ保持期間を増やすと金銭的なコストが激増するというものです。前述したようにスケールアウトのための運用コストが大きいことから、書き込みI/Oをスケールアップするために、NANDフラッシュメモリを利用しています。非常に性能が高い一方で高価なハードウェアなので、容量単価が大きいという特徴があります。 したがって、データ保持期間を増やすと、ディスク上のデータ量が増加し、安価なハードウェアにのせることに比べてお金がかかることになります。 一般の解決策は、圧縮してディスクに格納するか、既に書き込まれたデータを低速で容量単価の小さいストレージに移動するような仕組みにすることです。 前者の圧縮については、1で紹介されている差分符号化や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らのサーベイ論文2Open Source Time Series DB Comparison3にオープンソースの時系列データベース実装がまとめられています。

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

前述のサーベイ論文[^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とイベント通知というサーバーレスアーキテクチャらしい仕組みは、汎用的に利用できるアーキテクチャだと考えています。(2018/05/04追記: TimeFuzeアーキテクチャ構想 - 処理とデータとタイマーを一体化したデータパイプライン - ゆううきブログ として一般化して提案しました。)

実装

実装についての詳細は、スライドを参照してください。時系列データベース固有の話になります。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

追記


  1. 時系列データベースに関する基礎知識と時系列データの符号化方式について - クックパッド開発者ブログ

  2. Andreas Bader, Oliver Kopp, Michael Falkenthal. “Survey and Comparison of Open Source time Series Databases”. In proceedings of BTW 2017.

  3. Open Source Time Series DB Comparison

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回目を立ち上げるのももちろん大変なことだったと思いますが、その後「継続」することは、立ち上げ時の苦労とはまた異なる種類の苦労をされたのだろうと、「運用」を生業とする者としてご推察します。本当にありがとうございました。

2016年のエンジニアリング振り返り

はてなに入社して3年経った。

3年というのは節目と言われる。働き方や考え方の軸が多少変わってきたように思う。 技術観点では、仕事が少しつまらなくなっていた時期があった。 技術的におもしろいことより、つまらないことのほうが優先度が高くなってしまうというよくあるやつだと思う。 もしくは、おもしろいところまで到達できずに、仕事としては十分な成果になってしまうということもあった。

去年の振り返りに、来年はコードを書くと書いていて、多少はできたものの実感としてはあまりできていない。これからのオペレーションエンジニア/SREは、ソフトウェアエンジニアリングによる問題解決ができないと時代に取り残されてしまうという危機感がある。

技術的挑戦を続けていくためには、自分だけでなく、周囲の環境も変えていかないといけないと思い、マネジメントし始めたり、リーダーシップをとったり、直接的な採用活動をするような局面が増えてきた。 それはそれで自分でやると言ってやっていることだし、悪くはないのだけど、これ自分でやっていればアウトプットもできるのになと思いながら、任せないといけないことも多かった。 慣れないことをやっていると、大したことやってなくても疲れるもので、アウトプット活動が止まってしまった時期もあった。

一時的にそういった期間があることは仕方ない、いつも最高の環境があるわけじゃないので、意識的に環境に作用できるようになれれば、それは自分の強みになると言い聞かせてきた。 最終的には、社内外の期待をかけてくださる方々にいくつかのきっかけをいただいて、来年は技術的に挑戦できる年になるという気持ちで年を越せそうだ。 サーバモニタリング向け時系列データベースの探究 / The study of time-series database for server monitoring // Speaker Deck に書いた設計を自分の代表的プロダクトにすべく、プライベートリポジトリでちまちまとコードに落とし込んでいたりする。 このあたりは、今年大変仲良くしていただいたid:matsumoto_rさんと、あとはMackerelチームのプロデューサーである id:sugiyama88 さんのおかげだ。

振り返ってみると、今年は次の挑戦のための土台づくりに専念した年かもしれない。

成長

内在的な技術的成長は多少はあったものの、アウトプットにつながっていないものは成長とみなさないことにしているので、今年は技術的成長があまりなかったといえる。

あえていえば、実際の運用で導入したKeepalivedのシンタックスチェッカーや今書いているソフトウェアの要素であるGraphiteの式のパーザーなど、これまで書けなかったタイプのコードを書けるようになった。トークナイザーとYACCを書いてるだけはあるけど。

あとは、はてなのこれまでのスタックにはないようなアーキテクチャを構築する機会がいくつかあり、アーキテクチャで解決するという手持ちの札が増えてきたように感じる。

技術以外の成長では、多少マネジメントをしたり、プロジェクト管理みたいなことを覚え始めた。SRE本の第18章"Software Engineering in SRE"にも書かれているが、インフラの領域でも、ソフトウェアエンジニアリングを本格的にやるなら、サービス開発のプロジェクト知見も必要なので、役に立つと思う。周囲の方々の助けもいただいて、多少の手応えもつかめた。

そして、今年一番の収穫は、ストーリーをつくるという意識かもしれない。 ストーリーというとプレゼンのようなものを連想する。もちろんプレゼンも含むのだけど、もっとこう自分の技術の取り組みのストーリーみたいなものを考えるようになった。 普通のエンジニアである僕がなぜか来年に登壇することになったIPSJ-ONEの演題を考えたり、研究会での登壇があったり、あとまだ公開されていない何かのためにまつもとりーさんの論文や口頭発表、ブログを読み返したことが影響している。 博士課程では、複数回のジャーナルを通して、最後にそれらをまとめるということをやるようなので、その手法にも影響を受けている 博士課程の予備審査にいってきました - 人間とウェブの未来, NAKAHARA-LAB.NET 東京大学 中原淳研究室 - 大人の学びを科学する: 博士論文とは「構造を書くこと」である!?。 自分のストーリーがあれば、次に何を勉強するかも決まってくる。 僕のように瞬発力とか手の速さで勝てない凡人は、ひたすら考えて、自分のやってきたこと、これからやることを繋いでいくしかない。

アウトプットまとめ

いつものようにブログやOSS活動を振り返る。

ブログ

いつものブログに加えて、今年から技術メモとしてGitHubにちまちまとメモを残していたりする。

順位 エントリ
1位 Linuxサーバにログインしたらいつもやっているオペレーション - ゆううきブログ
2位 ウェブアプリケーション開発に新言語を採用したときにインフラで考えたこと - ゆううきブログ
3位 Googleが数千台もある10年前のLinuxディストリをライブアップグレードした話 - ゆううきブログ
4位 自作Linuxコンテナの時代 - ゆううきブログ
5位 インフラエンジニアがGo言語でオペレーションツールを書くことについて - Hatena Developer Blog
6位 2015年の心に残った技術エントリ - ゆううきブログ
7位 ISUCON予選突破を支えたオペレーション技術 - ゆううきブログ
8位 2016年ウェブオペレーションエンジニアの新卒研修 - Hatena Developer Blog
9位 情報処理学会でウェブオペレーション技術について招待講演した話 - ゆううきブログ
10位 2016年はてなWebオペレーションエンジニアのアウトプット - Hatena Developer Blog
11位 ウェブオペレーションエンジニアになるまでの思い出 - ゆううきブログ
12位 リモートワークにおけるSlack Call活用と終業15分前の雑談 - Hatena Developer Blog
13位 nginxのステータスコード444 · yuuki/yuuki
14位 nginxのステータスコード499 · yuuki/yuuki
15位 Keepalivedのシンタックスチェッカ「gokc」を作った - ゆううきブログ
16位 リバースプロキシのコンフィグテスト

合計 8000 users+くらい。

発表

合計8件。

OSS

f:id:y_uuki:20161231215324p:plain

Graphiteへのパフォーマンス改善PR。

go-sql-driver/mysqlのMySQL 4.0対応をして、MySQLプロトコルの知見を深めたりした。 https://github.com/yuuki/go-sql-driver-mysql

他には、ISUCON6予選の出題側としてGo実装とインフラまわりの手伝いをやったりした。

まつもとりーさんの振り返り にも書いていただいているように、今年はペパボさんのエンジニアの方々と交流させていただくことが多く、大変刺激になりました。 はてなの技術力が非常に高いと称していただいていますが、僕としてはどちらかというと我々はまだまだだなあと思っていて、ペパボさんの取り組みは、前へ進む勢いを感じさせられるというか、一本筋が通ってみえるというかそんな気がしています。 我々はまだ場当たり的に問題を解決していってて、このままではいけないと思い、はてなシステムについて考えてたりしている。

あとがき

他のエンジニアの方々の振り返りを眺めていて、たぶん自分より若い人の台頭がすごいなあと感心している。

悔しいので、負けないようにがんばろう。

今年もお世話になりました。来年もよろしくお願いします。