StackStorm, 勉強会

 こんにちは、インフラ本部の大山裕泰です。
 2017/03/23 に開催された StackStorm勉強会 第2回 に参加してきました。以下で各セッションの紹介と感想を書かせていただきます。

Event-driven automation, DevOps way


(Speaker: Dmitri Zimine 氏 [Brocade (StackStorm Founder & CTO)])

セッション概要

    * StackStorm は何なのかという話。一言で言うと ‘IFTTT for DevOps’
    * StackStorm のワークフローエンジンの解説。
    * ユースケース・利用事例 (Mirantis, NETFLIX など)。運用プラットフォームとして StackStorm を利用し、更に従来のオペレーションをサービス化した話など

所感

 StackStorm は様々な使い方ができてしまうため、これが一体どういったもので、どういった使い方をするのがベストなのかはっきりとはわかりませんでしたが、各社の利用事例を踏まえ、StackStorm が最大限効果を発揮する使い方について理解できました。

“If 自動化するなら Then StackStorm を使おう!


(Speaker: 小島一憲 氏 [SoftBank])

セッション概要

 従来の運用を StackStorm で一部置き換えた事例と教訓話。人間がオペレーションする前提で組まれたマニュアルをそのまま自動化させると無理が生じるケースがある(ex. “手順:アラートが上がった際の機器の時間と標準時間の誤差を出す” など) ため、自動化を前提とした運用設計を組み直す必要があるかもしれない。
 構成管理システムと連携することでよりパワフルに使える話。イベントに対して構成管理システムを参照して、オペレーションを制御するような使い方など。

所感

 とても実践的で聞き応えのある内容でした。特に構成管理システムとの連携で話では、それぞれのシステムの特性を理解したうえで使い分ける運用技法は参考になりました。

インターネット・エクスチェンジで運用自動化に StackStorm を導入した話


(Speaker: 杉本周 氏 [インターネットマルチフィード])

セッション概要

 以前はマニュアルとスクリプトで機器のオペレーションを行っていた。これを共通のフォーマットで記述されたワークフローで運用手法を定義し、構成管理システムなどの外部システムと連携して、複雑な運用を自動化させるチャレンジを実施。
 StackStorm v1.5 から使用し、最新版 (2.2) まで逐次アップデートを問題なく行って来れた。

所感

 かなり初期から StackStorm を導入し、運用しながらのアップデートを随時行ってきており StackStorm 自体の運用ノウハウは相当溜まっているのではないかと思います。

StackStormを用いたネットワーク機器の制御


(Speaker: 北川裕介 氏 [日商エレクトロニクス])

セッション概要

 Excel で記入された NW 機器設定の指示書を読んで、機器の設定を行う仕組みを StackStorm + Ansible で作った話 (Ansible を利用した理由は、既に機器の構築用の Playbook 資産を活用するためとのこと)。

所感

 斬新な使い方でした。StackStorm のアクション/ワークフローの機能は一切使わずに、単に Excel パラメータを読んで Ansible に渡すだけの処理に StackStorm を使うという妙。

おわりに

 インターネットマルチフィードや SoftBank をはじめ、前回の勉強会で登壇されたリクルートテクノロジーズなど、国内に様々なユーザが居る事実に驚きました。
 我々も StackStorm による運用改善を試みており、こうした取り組みについて OpenStack 最新情報セミナーJANOG39.5 Interim Meeting で発表するので、是非いらしてください。

おまけ

 StackStorm 公式 Slack では、1,300 人以上の StackStorm ユーザが参加し、日々情報が交わされていますが、2017/03/28 から公式の日本語チャンネルが開設されました。

 (写真の ‘dzimine’ が StackStorm CTO のアカウントです)

StackStorm, 運用管理

 こんにちは、インフラ本部の大山裕泰です。
 ここでは StackStorm の開発・運用を実際に行っている人向けに、拡張モジュール (pack) のパラメータ設定のテーマで掘り下げた内容をお届けします。

 StackStorm がどんなもので、どういう問題を解決するかについては StackStormで始める運用自動化 ~入門編~ をご覧ください。

 このエントリによって、StackStorm pack の config.yaml と config.schema.yaml の違いや、どういったケースでどのような設定方法がベストかを理解できるようになると思います。

 冒頭で紹介した CodeZine の記事でも書きましたが、レガシーな pack の設定パラメータの記述方法として、対象 pack の config.yaml ファイルに記述する方法があります。その他に config.schema.yaml で設定されたパラメータを pack の設定コマンド st2 pack config から設定する方法と、StackStorm が参照するデータストア (本校執筆時点の StackStorm では MongoDB を使用) に設定する方法があります。
 ここでは AWS の StackStorm モジュール aws pack を例に、それぞれの方法の使い方や他の設定方法との違いについて解説して行きます。

config.yaml による設定

 config.yaml による設定は、最も単純な pack の設定パラメータの記述方法です。ユーザは config.yaml 編集し、StackStorm pack に任意のパラメータを渡すことができます。これにより、StackStorm の UI を通して外部サービスのイベントハンドリングやアクションを実行できます。
 以下は aws pack のデフォルトの config.yaml の全文になります。

---
# Note: credentials under "setup" object are deprecated and have been replaced
# with top-level values which have precedence and also work with dynamic config
# values.
setup:
  region: ""
  aws_access_key_id: null
  aws_secret_access_key: null

aws_access_key_id: null
aws_secret_access_key: null
region: "us-east-1"
interval: 20
st2_user_data: "/opt/stackstorm/packs/aws/actions/scripts/bootstrap_user.sh"

service_notifications_sensor:
  host: "localhost"
  port: 12345
  path: "/my-path"

sqs_sensor:
  input_queues:

sqs_other:
  max_number_of_messages: 1

 config.yaml は全ての pack で共通して pack の管理ディレクトリ (/opt/stackstorm/packs/{pack 名}/) の直下に置かれています。aws pack の場合には config.yaml は /opt/stackstorm/pack/aws/ 配下に置かれます。

config.schema.yaml による設定

 config.yaml はとても単純な仕組みで使い勝手が良いのですが、pack の管理ディレクトリ配下のファイルが状態を持ってしまうと、運用上で不都合が生じてしまいます。

 pack は StackStorm の ‘st2 pack install’ コマンドによって StackStorm-Exchange から最新のコードを取得し、対象 pack の管理ディレクトリ (/opt/stackstorm/packs/{pack名}/) にデプロイされた後、StackStorm に登録されます。
 また pack の削除は st2 pack uninstall コマンドで行いますが、その際 pack の管理ディレクトリがそっくり削除されるため、pack のデプロイ後に編集した config.yaml も削除されます。
 pack の削除以外でも、StackStorm-Exchange の pack が更新され config.yaml のスキーマが変わった場合には、pack のアップデート時にユーザが編集した config.yaml は上書きされてしまいます。

 こうした挙動は Infrastructure as Code のプラクティスに合ったもので問題ありません。
 むしろ config.yaml の存在によってポリシーと設定が結合する格好になり、上記のような運用上の問題が発生します。また、設定項目や設定値の型はユーザが自由に記述することができるため、設定周りのテストがしづらいという問題もあります。

 これに対して config.schema.yaml で記述される 設定スキーマ (Configuration schema) の仕組みが作られました。
 設定スキーマは、予め pack の作成者が必要な pack の設定項目のスキーマをスキーマファイル (config.schema.yaml) に定義しておき、ユーザは ‘st2 pack config ‘ コマンドによって、予め規定された設定項目・型の値を設定します。

 これによりテストし易くなるという利点のほか、設定結果を pack の管理ディレクトリとは別の設定項目を管理するディレクトリ ‘/opt/stackstorm/configs’ 配下に置き、ポリシーと分離させることで、pack のバージョンや状態に関わらず設定内容を独立させることができます。

 以下に aws pack の config.schema.yaml の全文を示します。

 ’st2 pack config aws’ を実行することで、上記のスキーマファイルに沿った入力が求められ、最終的に aws 用のオリジナルの設定ファイルが生成されます。

 なお、ここでの設定は cnofig.yaml の設定よりも優先されます。なので、同じ設定項目を config.yaml と config.schema.yaml から生成された設定ファイルの両方で設定されている場合には、後者の値が読み込まれます。

データストアによる設定

 最後に、データストアによる pack の設定について解説します。
 ’st2 key set’ コマンドによって、以下のように任意の key/value をデータストアに登録させることができます。

 センサーの実装においては、上記の config.yaml/config.schema.yaml による設定は起動時に読み込まれ、ここでの設定値は動的に読み込まれるような 使い分け をしており、aws pack のセンサーではこれに沿って、データストアによる設定が優先されるように実装されています。

 また Jinja template の拡張により、以下のように config.schema.yaml から生成した設定ファイルから、データストアで設定した値を参照させることができます。

 以下で、設定ファイルで指定したキー ‘dev_api_key’ の値 ‘SecretValue’ を暗号化して設定します。

 ただ、これには 文字列型しか設定できない制限 が存在するため、使用する際には注意が必要になります。

StackStorm

 インフラ本部の大山裕泰です。今回は Brocade が買収したことで話題になった、イベントドリブンな Workflow エンジン StackStorm が一体どういう仕組みで動作しているか、ちょっと深堀した内容をお届けします。

 StackStorm 自体については、先日行われた StackStorm 勉強会で 発表された方の資料 で詳しく解説されており、使い方についても こちらこちら のページで詳しく解説されています。
 「StackStorm を使ってみたけど、裏側で何がどうなっているかイマイチよくわからん」といったユーザ向けに、StackStorm の論理構造を解説し、StackStorm の理解の手助けが出来ればと思います。

StackStorm について

 StackStorm のドキュメント を冒頭から読んでいくと、いきなり次のような図が情報します。

 一見とても複雑そうです。ターゲットに紐付く依存関係を解決してコマンドを実行する “古典的なワークフローエンジン” とは違います。
 初めて触るユーザや、とりあえず QuickStart だけやってみたユーザにとっては、なんでこんな複雑な機構なんだろうかと戸惑われたかもしれません。
 本エントリではこうしたユーザ向けに、StackStorm の概要についての理解と、上記の内部アーキテクチャの詳細についての理解の中間的な論理構成の理解を手助けする内容を提供していきます。

StackStorm の論理構造

 以下は、StackStorm がイベントを処理する仕組みについて表した図になります。

 Sensor は外部で発生したイベントを監視するモノで、外部のイベントを検知した際には予め登録された Trigger にイベントの発生を通知 (以降では “Trigger を引く” と表現) します。
 Trigger は Sensor から通知された外部イベントを Action が扱い易い形に正規化します。何も設定されていない状態では、Sensor が Trigger を引いても何も発生しません。Trigger が引かれた際にどういった処理 (Action/Workflow) を実行するかを規定したものが Rule になります。
 Rule には二つの役割があります。一つは上述した Trigger と Action/Workflow を紐づける役割で、もう一つが Trigger パラメータと Action パラメータを変換する役割です。
 StackStorm では Trigger と Action はそれぞれ独立しており、Rule によって任意の Trigger と Action を組み合わせることができます。その際、Trigger から渡されるどのパラメータを抽出し、どういったパラメータを Action に渡すかの設定を Rule に記述します。

 以下の具体的なケースに置き換えて考えてみます。

 ここでは Sensor, Trigger, Action にそれぞれ以下を使用し、GitHub の特定のリポジトリで Issue を作成(更新)した際に RabbitMQ にメッセージを送る仕組みになります。
 尚、StackStorm では Sensor, Trigger, Action を機能毎にまとめたソフトウェアコンポーネントのことを pack と呼びます。そして、上記の github は rabbitmq のようなサードパーティの pack は (本エントリ執筆時点では) StackStorm/st2contrib リポジトリで管理する運用になっています。

 Sensor ‘github.GithubRepositorySensor’ は 30 秒毎に GitHub のリポジトリの更新状況を確認し、前回の確認から更新が発生した場合には Trigger ‘github.repository_event’ を引きます。
 ここでは、この Trigger が引かれた際に Action ‘rabbitmq.publish_message’ を実行する Rule ‘example.github_event_invocation’ を記述しました。この Rule の内部で Trigger パラメータ (Issue に対する操作(opened/closed)、及びタイトルと本文) を Action パラメータ (exchange, routing-key, message) を次のように変換しています。

動かしてみる

 確認する GitHub のリポジトリは、GitHub pack の設定ファイル ‘/opt/stackstorm/packs/github/config.yaml’ で指定します。以下に設定ファイルの全文を示します。

 ここでは、この Trigger が引かれた際に Action ‘rabbitmq.publish_message’ を実行する Rule ‘example.github_event_invocation’ を記述しました。以下が設定したルールの全文になります。

 ’trigger’ / ‘action’ のパラメータで、それぞれ図の Trigger / Action を指定します。’action’ の ‘parameters’ で当該アクションに渡すパラメータを指定します。ここで Trigger から渡されるパラメータと Action パラメータの変換処理を行っています。
 ’criteria’ パラメータは、Trigger に通知されたイベントのうち Action で処理するモノを選択する際に利用します。今回の例では Trigger はリポジトリに対する Issue の更新/コメント、Fork、Watch、Push などのイベント発生時に Sensor によって引かれますが、Issue の更新イベントだけ Action を呼ぶようにしたい場合にこのように設定します。

 それでは実際に GitHub リポジトリ に Issue を作成し動作を確認します。RabbitMQ に通知された メッセージを受け取るスクリプト を実行し、リポジトリに以下のような Issue を作成します。

 こうすることで Sensor が検知したイベントを Trigger に通知し、Rule に沿って RabbitMQ にメッセージが送られます。そして、先ほど実行したスクリプトで送られたメッセージを取得できることが確認できます。

MQ, OpenStack, Python

 こんにちは、インフラ本部の大山裕泰です。
 今日は OpenStack の共通ライブラリ群 oslo に含まれる olso.messaging を使って RPC over MQ 処理を実装してみます。なぜ RPC を MQ を介して行うと良いかについては 過去のエントリ で書きましたので、興味があれば読んでみてください。

oslo って何?

 oslo は OpenStack の各コンポーネント (OpenStack の最も大きなソフトウェアモジュールの単位) で共通で利用するライブラリを集めたプロジェクトになります。
 その昔の OpenStack では設定ファイルやコマンドライン引数の解析、メッセージパッシング、ロギングなどといった処理が各コンポーネント毎に個別に存在しており、開発・メンテナンスコストが掛かる問題がありました。
 そして Grizzly サイクルで共通ライブラリ群を提供する olso プロジェクトが発足し、Havana で設定ファイルとコマンドライン引数の解析を行なう oslo.config と、通知や RPC の仕組みを提供する oslo.messaging がリリースされました。現在では、国際化や並行処理など様々な機能が加わり、本稿執筆時点では 34 のライブラリ が存在します。

 oslo 自体は OpenStack のプロジェクトですが、用途は OpenStack に限定されません。例えば oslo.config は、設定ファイルとコマンドライン引数の解析を行なうライブラリとして広く知られており、NTT Communication などが中心となって開発している SDN Framework Ryu などで採用されています。
 今回紹介する oslo.messaging では、RabbitMQZeroMQ などの MQ システムを介した通知や RPC の仕組みを提供します。

なぜ oslo.messaging なのか?

 oslo.messaging の使い方について見て行く前に、oslo.messaging についての理解を深めるために、なぜこの仕組みを使うのかについて考えてみたいと思います。
 MQ を利用した通知や RPC の仕組みは、もちろん oslo.messaging が登場する以前から存在していました。RabbitMQ を使う場合には Pika という AMQP ライブラリを用いてそれらの処理を行なうことができます。また ZeroMQ を使う場合には、PyZMQ ライブラリを用い、Kafka にも 同様の Python ライブラリ が存在します。

 しかしこれらを用いることによって、ミドルウェアないはプロトコル依存の実装になってしまいます。
 oslo.messaging では、ミドルウェア・プロトコル非依存の通知、RPC の仕組みを提供しており、これによってユーザは様々な MQ システムを選択することができるようになりました。

oslo.messaging の使い方

 以降では oslo.messaging を使って、汎用的な RPC over MQ 処理を行なう方法を紹介します。サンプルとして以下のコードを利用します。

https://github.com/userlocalhost2000/oslo-messaging-examples

 コードの中身を見る前に、システムのアウトラインについて簡単に把握したいと思います。以下は oslo.messaging と他のシステムとの関連、及び内部アーキテクチャについて表した図になります。

 処理のおおまかな流れとしては、左上の “User Application” が “RPCClient” で定義されたインターフェイスを叩き、”Transport” オブジェクトで抽象化された MQ システム (RabbitMQ など) を経由して “Server” に登録された “Endpoint(s)” の処理を実行し結果を取得します。ここでは、oslo.messaging レイヤにおけるクライアント、サーバ側の内部の仕組みについて簡単に解説します。

 クライアント側では RPCClient オブジェクトがユーザアプリケーションに対して、RPC 処理の仕組みを提供します。また Transport オブジェクトが、RabbitMQ や Kafka , ZeroMQ などの各種 MOM を抽象化し、ユーザは RPCClient が提供するインターフェイスを通して MOM 非依存な RPC リクエストをサーバに送ることができます。
 サーバ側では、ユーザからの RPC 要求を処理する Server オブジェクトを生成します。その際、ユーザ側で各 PRC 要求に対応する処理 (Endpoint) を一つ以上実装してやり、それらを Server オブジェクト生成時に指定します。
 また Server 及び Endpoint(s) に対して RPC API の名前空間と互換 version について設定することができます。これらの設定を保持したものが Target オブジェクトになり Server 及び Endpoint にひも付きます。
 Endpoint オブジェクトにおいて Target を省略した場合には、グローバルな名前空間と version1.0 が暗黙的に設定されます。ただし Server オブジェクトにおいては Target オブジェクトを topic 及び server パラメータ付きで指定しなければなりません。
 Target オブジェクトの topic パラメータは、サーバが提供する API を識別するための項目で amqp ドライバにおいては名前付きキューの名前に対応します。クライアントはこの値だけを知っていれば、サーバの場所を意識せずにサーバに対して RPC 要求を送ることができます。
 また Target オブジェクトの server パラメータは、文字通り API を提供するサーバを指定する項目ですが、実際に存在するサーバのホスト名ないしは IP アドレスを指定しなければならないわけではく、サーバを指定する任意の文字列になります。これはクライアントが topic パラメータで指定した API を持つサーバ群のうち、特定のサーバに対してメッセージを送りたい場合に利用されるパラメータになり amqp ドライバにおいては、topic 名で指定した名前付きキューに加えて、”${topic}.${server}” の名前付きキューを生成します (${topic} と ${server} にはそれぞれパラメータの設定値が入ります)。

動かしてみる

 各データ構造の役割と関連がわかったところで、サンプルコードを見ながら実際に動かしてみます。
 まずサンプルコードを動かすために oslo.messaging をインストールします。またここでは Python 2.7.6 を使用しています、尚本稿執筆時点で OpenStack がサポートしている Python のバージョンは 2.7.x と 3.4.x になります [*1](https://wiki.openstack.org/wiki/Python3)。

 続いて次のサンプルコードを GitHub から取得します。

 サーバ側のコード src/server.py について簡単に解説します。以下がその抜粋になります。

 22 行目の get_rpc_server メソッドの呼び出しで Server オブジェクトを生成します。RPCClient からの RPC 要求に対して endpoints パラメータで指定したオブジェクトにマッチするメソッドを呼び出します。その際、エンドポイントオブジェクトに target メンバが設定されている場合には、namespace と version ネゴシエーションの確認を行います。target メンバが指定されていない場合には、デフォルトの namespace (=None) と version (=1.0) が内部的に設定されます。
 コールバックメソッド hoge() のパラメータはそれぞれ、ctx がユーザ定義のオブジェクト (ディレクトリ型) を表し、arg がユーザ定義の引数を表します。

 次にクライアント側のコード src/client.py についても簡単に見て行きます。以下がその抜粋になります。

 20 行目でユーザ定義の RPCClient ラッパーの hoge を呼び出し、内部的に 15 行目でサーバに対して hoge メソッドの呼び出しを行っています。その際 14 行目の prepare() メソッドの呼び出しで RPCClient オブジェクトが内部で持つ Target オブジェクトの namespace と version パラメータを上書きします。
 version パラメータがサーバ側のエンドポイント hoge() で指定した値と一致していないことに気づいたかもしれません。Server 側のバージョンネゴシエーション処理では、クライアントからの要求 version のメジャーバージョン (上の位) が一致しており、かつマイナーバージョン (下の位) がサーバで設定した値以下であれば、互換性がある要求として当該メソッドを実行し結果を返します。

 それでは、実際にこれらを実行してみます。次のようにターミナルからサーバスクリプト src/server.py を実行し、次の別のターミナルを開いてクライアントスクリプト src/client.py を実行してみてください。


(左:サーバ、右:クライアント)

 クライアント側からの呼び出しでサーバ側で定義したエンドポイント hoge() が呼び出され、クライアント側で実行結果が受け取られていることが確認できました。

gRPC, MQ, NewtMQ, RabbitMQ, Ruby, STOMP, ZeroMQ, ベンチマーク

 こんにちは、インフラ統括本部の大山 裕泰です。
 NewtMQ v0.2 をリリースしました。

https://github.com/newtmq/newtmq-server/releases/tag/v0.2.0

 v0.2 では temp-queue を実装し Brokered MQ を利用した高速な RPC 処理が実現できるようになりました。
 今回は RabbitMQ(AMQP) 及び RabbitMQ(STOMP) に加えて Broker-less MQ の ZeroMQ、Google 謹製の RPC フレームワーク gRPC、そして NewtMQ で RPC のベンチマークを取ってみました。
 尚、御馴染みの Apache Kafka には 同様の機能がない模様 なため、検証しておりません。

temp-queue とは?

 NewtMQ (v0.2) に実装した temp-queue の仕組みは、以前のツチノコブログエントリ で紹介した rabbitmq-stomp プラグインのものと同じ仕組みになります。RabbitMQ 本体にも 同様の機能 が実装されています。
 簡単に説明すると pub-sub モデルにおいては Publisher と Subscriber はそれぞれお互いの存在を認知しませんが、Subscriber が、受け取ったメッセージを送ったある Publisher に対してメッセージを返信したいケースにおいて、当該 Publisher 専用の一時キューに対してメッセージを送ることで Subscriber と特定 Publisher 間でメッセージパッシングを行えるようにする仕組みになります。

 MQ を介して RPC を行う際、この仕組みが有効に作用します。

RPC over MQ

 RPC はネットワークを介した計算機資源を活用する分散システムの世界で 30 年以上の歴史がある枯れた技術です。
 分散システムにおいてはネットワークや計算機に障害が発生した際においてもサービスが安定して稼働できること、そして計算機資源の動的な増減に対応できることが古くからの課題として知られています。これらの問題を解決する手段の一つとして MessageQueue が広く利用されてきました。
 大規模化・複雑化した今日のシステムの多くはこうした仕組みに支えられています。数千 ~ 数万のサーバから構成される OpenStack では、RabbitMQ を用いた RPC を行うアーキテクチャ を採っています。

Broker-less MQ

 ここで、今回の比較する ZeroMQ に代表される Broker-less MQ と呼ばれる MQ について紹介します。
 ’MQ’ という名前を冠している点で RabbitMQ や NewtMQ と同じく、Queuing や Pub/Sub といったシステムモデルの実装をサポートする機能を提供している点で共通ですが、メッセージを仲介する ‘Broker’ を持たない点で異なります。
 RabbitMQ や NewtMQ の場合、以下のように Pub/Sub モデルにおいてクライアント (Publisher 及び Subscriber) は Broker に対してメッセージの送信/取得を行います。Apache Kafka や MQTT の実装として知られる ActiveMQ Apollo もこちらに分類されます。
   

 対して ZeroMQ の場合、以下のように Publisher が直接 Subscriber プロセスに対してメッセージを送信します。

 Broker を持た無い MQ システムでは、Broker を介した MQ と比較していくつかのメリットがあります。まずクライアントと Broker 間の通信が無くなるため、通信に伴うトラフィック量を減らせる事ができます。また ‘Broker’ で行う処理を省くため高速なメッセージパッシングを行うことができます。

ZeroMQ

 ZeroMQ の用途は Queuing や Pub/Sub モデルの実装に止まりません。ここで ZeroMQ 自体について補足で紹介します。
 ZeroMQ では ‘ZeroMQ Socket’ と呼ばれる TCP Scoket を抽象化したオブジェクトに対して、1対1, 1対N, N対1, N対N の通信を実現する機能 を加えています。更に、これらの通信のベースに Queuing の仕組みを設ける事で通信の可用性を高め、従来の TCP Socket を用いた通信を行うアプリケーションに対して、高い機能性と可用性を備えた仕組みを提供しています。
 更に ZeroMQ の Router Socket を利用することで、メッセージの配送を仲介する Proxy をつくることができ Broker MQ のような振る舞いをさせることも出来ます。

測ってみた

 それではここで MQ を利用した RPC 処理のベンチマークを取ってみます。ベンチーマークは Broker MQ の雄 RabbitMQ(AMQP, STOMP) Broker-less MQ の代表格 ZeroMQ、そして RPC フレームワークとして現在非常に活発に開発が行われている Google 謹製の RPC フレームワーク gRPC、そして NewtMQ に対して行います。ベンチマークでは以下の RPC ベンチマークツールを利用します。

* https://github.com/userlocalhost2000/rpc-bench

 各クライアントライブラリはそれぞれ以下を使用しています。

ターゲット ライブラリ (バージョン)
RabbitMQ(AMQP) bunny (2.3.1)
RabbitMQ(STOMP) stomp (1.4.0)
NewtMQ stomp (1.4.0)
gRPC grpc (0.14.1)
grpc-tools (0.14.1)
ZeroMQ libzmq (4.2.0)
ffi-rzmq (2.0.4)
ffi-rzmq-core (1.0.5)

 今回は、各手法の実行時間の比較が目的のため、クライアント、サーバ、及び MQ を全て同一ホスト上で実行しています。実行ホスト環境は以下のとおりです。

CPU Intel Core i7-6700 CPU @ 3.40GHz
RAM 32GB
OS Ubuntu14.04
Ruby 2.2.2
RabbitMQ 3.6.1-1

 RPCBench では、クライアントがサーバに対して数値を送り、サーバが送られた数値に 1 を足して結果を返し、最終的にすべての結果が送った数値 + 1 になっているかを検査するまでに要した時間を、リクエスト数、同時実行数のパラメータを変えて取得できます。
 
 まず同時実行数を 1 に固定し、リクエスト数を 32k ~ 512k にそれぞれ設定した結果を以下に示します。

 予想通り ZeroMQ が最も早いですが NewtMQ もほぼ同じ程度の速度で RPC をさばけています。予想以上のスピードに我ながら驚いています(結果の取り違いを疑いました)。
 それぞれのベンチマークにおいて RPC サーバ処理 (数値を受け取って +1 する処理) は共通ですが、クライアントライブラリがそれぞれ異なるため、純粋に MQ の違いについての比較にはなりません。
 ただし RabbitMQ(STOMP) との比較に関しては、サーバ処理もクライアントライブラリもそれぞれ同じものを用いており、MQ だけが違うので純粋な NewtMQ と RabbitMQ(STOMP) の性能の違いがこの結果かわかります。

 続いてリクエスト数を 10k に固定し、同時実行数を 4 ~ 64 にそれぞれ設定した結果を以下に示します。

 

 やや ZeroMQ との差が開きましたが Broker MQ においては NewtMQ が最も速度が出ていることがわかります。

おわりに

 NewtMQ に temp-queue を実装したことで、RabbitMQ よりも高速に RPC を処理できることを確認しました。
 しかし NewtMQ はその他の機能面や信頼性、実績において RabbitMQ に遠く及ばないので、メジャーバージョンがリリースできるくらいまでは暖かい目で見守っていただければと思います。

 性能改善の余地はまだ残されているので、NewtMQ はまだまだ速くなると思っています。

PAGE TOP