Elastic Stack 6.7.0 リリース
Elasticsearchでのイベントベースのデータ重複を効果的に防止
Elastic Stackは、多くのさまざまなユースケースに使われています。最も一般的なものの一つに、セキュリティイベント、ログ、メトリックといった、色々な種類のイベントや時系列データの格納と分析があります。これらのイベントは、イベント発生時または収集時を表す特定のタイムスタンプにリンクしたデータからなることが多く、往々にしてそのイベントを他と区別できる適切なキーがありません。
いくつかのユースケース、またおそらくユースケース内のデータのタイプにとっては、Elasticsearch内のデータが重複しないことは重要です。それは、ドキュメントの重複が分析の誤りや検索結果のエラーを導くからです。昨年、Logstashを用いた重複対策についてというブロク記事で本件を見ていきましたが、今回はもう少し詳しくよくある質問にお答えしていきたいと思います。
Elasticsearchへのインデックス
Elasticsearch内にデータをインデックスするときは、データが正常にインデックスできたと確認できるレスポンスを受け取ることが必要です。接続エラーやノードクラッシュなどのエラーがあるとそうしたレスポンスを受け取ることができず、データがインデックスできていないものがあるかどうかも分かりません。クライアントがこうした事態に遭遇したとき、確実な結果を得るためには再試行するのが標準的ですが、これでは同じドキュメントを2回以上インデックスすることになります。
重複対策のブログ記事で説明したように、これは、クライアント内の各ドキュメントにユニークなIDを定義しておくことで回避することが可能で、インデックス時にElasticsearchのID自動割り当てを利用するよりも有効です。重複するドキュメントを同じインデックスに書き込むと、2回ドキュメントを書き込む代わりに更新することになるので、重複を防ぐことになります。
UUIDとハッシュベースのドキュメントID
IDにどのタイプを使うか決めるにあたっては、主に2つのタイプから選ぶことができます。
Universally Unique Identifiers (UUID)は128ビット数に基づく識別子で、実用的な目的にかなってユニークでありながら、分散システム全体で横断的に生成可能です。このタイプの識別子は通常、イベントが関連付けられているコンテンツに依存しません。
UUIDを使用して重複を回避するには、イベントが正確に1回だけ発生していることを保証する境界をイベントが通過する前に、UUIDが生成され、イベントに割り当てられる必要があります。これは実際面では、UUIDをイベントの発生時に割り当てることを意味します。イベントが発生するシステムがUUIDを生成できないと、異なるタイプの識別子を使用する必要が生じる場合があります。
もう一つの主な識別子のタイプは、ハッシュ機能を用いてイベントのコンテンツをベースにした数値ハッシュを生成するものです。このハッシュ機能は特定のコンテンツに対して必ず同じ値を生成しますが、生成された値はユニークであるとは限りません。この2つの異なるイベントが同じハッシュ値になるというハッシュの衝突は、使用されるハッシュ関数のタイプと作成する値の長さだけでなく、インデックス内のイベント数に依存します。MD5やSHA1などの少なくとも128ビット長のハッシュは、多くのシナリオにおいて、一般的に長さと低い衝突率の間でバランスがとれたものです。さらに厳密にユニークであることを保証するためには、SHA256のような長いハッシュを使うことができます。
ハッシュベースの識別子はイベントのコンテンツに依存するので、どこで生成されても同じ値が算出されるため、後の処理段階で割り当てることが可能です。このことから、このタイプのIDはデータがElasticsearchにインデックスされる前ならいつでも割り当てることができ、投入パイプラインをデザインするときに柔軟性を与えます。
Logstashは、フィンガープリントフィルター プラグインにより、UUID算出とよく使われる一般的な幅広いハッシュ関数に対応しています。
効果的なドキュメントIDの選択
Elasticsearchがインデックス時に識別子の割り当てを許されている場合は、生成された識別子がインデックスに既存のものであってはならないことが分かっているので、最適化を行うことができます。これによりインデックス化のパフォーマンスが上がります。外部で生成されドキュメントとともに渡された識別子に対しては、Elasticsearchはこれが更新の可能性があるものとみなし、ドキュメントの識別子が既存のインデックスセグメントにあるかどうかチェックするので、余分な作業が必要となり処理が遅くなります。
外部ドキュメントの識別子は、すべて等しく作成されているわけではありません。並べ替え順序に基づいて時とともに徐々に増加する識別子は、完全にランダムな識別子よりもインデックス化のパフォーマンスが上です。その理由は、Elasticsearchが、最小および最大の識別値のみに基づく古いインデックスセグメントに識別子があるかどうかを、全体を検索しなければならないよりもすばやく判断できるからです。本件はこちらのブログ記事にて紹介しており、少し前のものですが今でも有効です。
ハッシュベースの識別子と多くの種類のUUIDは、一般的にランダムであるという性質があります。一つひとつタイムスタンプを定義するイベントのフローを扱うときは、このタイムスタンプを識別子のプレフィクスとして使い並べ替え可能にして、インデックス化のパフォーマンスを上げることが可能です。
タイムスタンプをプレフィクスにして識別子を作成することは、ハッシュ値がタイムスタンプごとにユニークでありさえすれば良いため、ハッシュの衝突可能性を減らすメリットもあります。これにより、投入量の多いシナリオにおいてもより短いハッシュ値を使うことが可能になります。
Logstashでは、UUIDあるいはハッシュを生成するためにフィンガープリントフィルター プラグインを使ったり、タイムスタンプを16進の文字列表現をRubyフィルターを使うことで、この種の識別子を作成することができます。ハッシュ化できるメッセージフィールド
があり、イベントのタイムスタンプがすでに@timestamp
フィールドへパースされていることが推測される場合は、その識別子のコンポーネントを作成し、メタデータに次のように格納することが可能です。
fingerprint { source => "message" target => "[@metadata][fingerprint]" method => "MD5" key => "test" } ruby { code => "event.set('@metadata[tsprefix]', event.get('@timestamp').to_i.to_s(16))" }
すると、ドキュメントIDをElasticsearch出力プラグイン内に作成する際に、次の2つのフィールドが使用されます。
elasticsearch { document_id => "%{[@metadata][tsprefix]}%{[@metadata][fingerprint]}" }
これは、16進法で長さ40文字のドキュメントIDとなり、例えば4dad050215ca59aa1e3a26a222a9bbcaced23039
のようになります。完全な設定例は、こちらのGistでご覧いただけます。
インデックスのパフォーマンスへの影響
さまざまなタイプの識別子を使うことの影響は、データ、ハードウェア、ユースケースによって異なります。一般的なガイドラインは提供できますが、ベンチマークを実行して自分のユースケースには正確にどんな影響があるのか判断することが重要です。
最適なインデックスのスループットのためには、Elasticsearchで自動生成された識別子を使用することがいつでも一番効果的な選択肢でしょう。更新チェックが不要なため、インデックスのパフォーマンスは、インデックスやシャードのサイズが拡大するほどには変わりません。ゆえに、可能であればいつでも使用することをおすすめします。
外部ID使用が理由で生じる更新チェックは、余分なディスクアクセスを必要とします。これによる影響は、オペレーティングシステムが必要なデータをどの程度効果的にキャッシュできるか、またストレージの速度や、ランダムな読み取りがどの程度うまく処理されるかによって異なります。インデックスのスピードはまた、インデックスとシャードが拡大しチェックするセグメントが増えるほど低下することもよくあります。
ロールオーバーAPIの使用
従来の時間ベースのインデックスは、特定の設定期間をカバーするインデックス一つひとつに依存しています。つまり、データ量が時間とともに変動する場合は、このインデックスとシャードのサイズがかなり大きく変化するということです。不規則なサイズのシャードは好ましくなく、パフォーマンス上の問題を引き起こします。
ロールオーバーインデックスAPIは、時間だけでなく複数の基準に基づく時間ベースのインデックスを管理する、柔軟な方法を提供するために導入されました。これは、既存のインデックスが一定のサイズ、ドキュメント数/古さに達したときに新しいインデックスにロールオーバーすることを可能にし、シャードとインデックスのサイズがより予測可能になっています。
しかしながらこれによって、イベントのタイムスタンプとそれが属するインデックスとのつながりは絶たれてしまいます。インデックスが厳密に時間を元にしたものであれば、到着がどれだけ遅くなっても、イベントはかならず同じインデックスになります。この考え方が、外部識別子を使った重複防止を可能にしています。ゆえに、ロールオーバーAPIを用いると、重複の確率が低くなったとしても、完全に重複を防ぐことはもう不可能です。 2つの重複したイベントがロールオーバーしたどちらかに到着して、そのため同じタイムスタンプを持っていても別々のインデックスになってしまう可能性があり、これが更新されることにはなりません。
ですから、重複防止が厳密な要件の場合には、ロールオーバーAPIの使用は推奨しません。
予測できないトラフィック量への対策
ロールオーバーAPIを使うことはできなくとも、トラフィック量が上下し時間ベースのインデックスが過剰に小さく、あるいは大きくなってしまった場合に、シャードサイズを適応させ変更する方法がまだあります。
トラフィックの急増などによりシャードが過剰に長くなってしまった場合は、分割インデックスAPIを使用してインデックスを複数のシャードに分けることが可能です。このAPIは、インデックス作成時に適用するよう設定する必要があるので、インデックステンプレートによって追加する必要があります。
反対にトラフィック量が少なすぎて、異常に小さいシャードができてしまった場合は、圧縮インデックスAPIを使ってインデックス内のシャード数を減らすことができます。
まとめ
このブログ記事でご覧になったように、Elasticsearch内での重複を防ぐことは、Elasticsearchにデータをインデックスする前に外部でドキュメントの識別子を特定することで可能になります。識別子のタイプと構造は、インデックス化のパフォーマンスに著しく影響することがあります。ただし、これはユースケースごとに異なるため、自分の特定のシナリオにはどれが最適か、ベンチマークによって見極めることをおすすめします。
ディスク容量の節約:Elasticsearchのインデックスソートに関するあまり知られていないメリット
Elasticsearch 6.0では、インデックスソートという新機能をリリースしました。詳細についてはリンク先のブログをお読みいただければと思いますが、要約すると、ドキュメントをインデックスする際に、キーまたはキーのセットを使用して、指定した順序に並び変えることができます。この機能によって、今までに述べてきたような新しいメリットが実現します。
- インデックスソートに使用したのと同じキーで並べ替えて結果を返すようにElasticsearchに指示すると、クエリ時の結果の並べ替えが不要になります。すでにソートされているからです。
- 総ヒット件数が不要でソートキーで並べ替える場合、Elasticsearchでは、リクエストを満たすヒット件数に達した場合にそのクエリは終了します。これにより、クエリパフォーマンスが驚くほど向上します。
- 異なるフィールドに関してANDを使用してクエリを実行する場合、それらのフィールドはインデックスソートによってグループ化されるため、Elasticsearchは一致しない多数のドキュメントブロックをスキップすることが可能になり、検索も速くなります。
つまり、特に複数のユーザーがドキュメントの検索や並べ替えにいくつかの共通の方法を使用している場合には、インデックスソートによってより迅速に検索できるようになります。 そして、実はこのインデックスソートに関してあまり語られていないメリットがあります。それは、インデックスに使用されているディスク容量の削減も可能になることです。その理由と仕組みについて説明しましょう。
注意:インデックスソートは誰にでも役立つものではない
この機能の仕組みについて話す前に再度確認しておきたいのは、インデックスソートは誰にでも役立つものではないということです。ソートはインデックスを作る際に実行されます。ソートは負荷の高いオペレーションであるため、インデックスする速度が主な懸念事項の場合は、実行前に注意が必要です。書き込みのパフォーマンスが40~50%も低下します。そのためインデックス作成のスループットが主な懸念事項の場合、たとえば大量のロギング、メトリック、セキュリティ分析などのユースケースでは往々にしてそれが問題となりますが、インデックスソートは適していません。 インデックス数が少ない場合やクエリ速度が最も重要なユースケースの場合、またはインデックス付けのオフピーク時に定期的に再インデックスプロセスが実行される場合には便利です。
ソートオーダーの検討:一例
Elasticsearchインスタンス使用して製品検索を行うとしましょう。インデックスを作成する際に、以下のようなドキュメントのセットを持っていると仮定します(見やすくするために表の形式にしています)。
製品ID | 製品カテゴリー | 製品カラー | 価格 |
206f467b-8cfe | シューズ | レッド | $97.00 |
4f89fbec-acc3 | ジャケット | ブラック | $120.50 |
47771396-dfe3 | ジャケット | グレー | $170.10 |
c6c8fbdf-651b | 帽子 | イエロー | $15.00 |
dc18c426-0eb3 | シューズ | レッド | $107.20 |
ee304259-df57 | ジャケット | ブラック | $88.00 |
9332c0ac-e55e | シューズ | ブラック | $49.00 |
30e96765-52a1 | 帽子 | ブルー | $11.00 |
811cc8ca-d6bb | ジャケット | ブルー | $92.99 |
ここで、インデックスソートを有効にしたいとします。何でソートしますか?製品カテゴリー、製品カラー、および/または価格といったいくつかのオプションがあります。ユーザー検索がほぼ常に価格のみでソートされており、カテゴリーやカラーのフィルターがない場合には、ソートキーとして価格で並び変えることが最も有用です。しかし、おそらくユーザーは最安値の商品を見つける前に、少なくともカテゴリーは選択しているでしょう。また、カラーの好みもあるはずです。 カテゴリーの昇順、カラーの昇順、そして価格の昇順でソートしてみましょう。
"sort.field" : ["product_category", "product_color", "price"], "sort.order" : ["asc", "asc", "asc"]
ソートされたインデックスは次のようになります。
製品ID | 製品カテゴリー | 製品カラー | 価格 |
30e96765-52a1 | 帽子 | ブルー | $11.00 |
c6c8fbdf-651b | 帽子 | イエロー | $15.00 |
ee304259-df57 | ジャケット | ブラック | $88.00 |
4f89fbec-acc3 | ジャケット | ブラック | $120.50 |
811cc8ca-d6bb | ジャケット | ブルー | $92.99 |
47771396-dfe3 | ジャケット | グレー | $170.10 |
9332c0ac-e55e | シューズ | ブラック | $49.00 |
206f467b-8cfe | シューズ | レッド | $97.00 |
dc18c426-0eb3 | シューズ | レッド | $107.20 |
興味深いことが見えてきました。例を示しながら説明します。
- 価格でソートして最安値上位2つのシューズを示すように指示し、すべてのシューズの総ヒット数が不要な場合、ElasticsearchはShoesのブロックを見つける必要があり、その他のすべてのカテゴリーをスキップするので効率的です。そして、結果となる2つが見つかると、残りのインデックスに対する処理を停止し、結果を返します。このように機能させるためには、一致するフィルターがあったとしても、ソートオーダーのすべての要素をインデックスに含める必要があります。
- Elasticsearchで
product_category:Jackets AND product_color:Black
と指定すると、HatsのすべてとShoesのすべてがスキップされ、Jacketsの中で「Black」のものが検索され、それが見つかると、他のカラーのすべてがスキップされるので効率的です。 - Elasticsearchは、見えないところでかなりの圧縮を行っているわけです。この圧縮は繰り返し値がある場合に機能します。最も効率的に機能するのは、繰り返し値がインデックス内でお互いに近い位置にある場合です。「Jackets」のすべてまたは「Color」のすべてがお互いに側にある場合、ディスクに効率的に圧縮されます。これによってディスクスペースが少なくて済むとともに、オペレーティングシステムのファイルシステムキャッシュがより効率化され、動作がさらに迅速になります。
一般的に、ソートオーダーはカーディナリティの高い順番にすることがベストプラクティスです。1行内のできるだけ多くの繰り返し値を利用できるという利点があるからです。
節約できるディスク容量
インデックスソートを有効化することで、どれくらいのディスク容量を節約できるでしょうか。 それは他のさまざまなことと同様、「場合による」ことになります。 その主な要素の1つは、ソートするフィールドのカーディナリティです。これによるディスク容量の節約はかなりの量になります。 先週末、私は個人的なプロジェクトに使用しているIoT/ホームオートメーションのデータの一部を、古いマシンから新しいマシンに移動させることにしました。そのデータの移行にはスナップショット/復元のような迅速な方法がありますが、再インデックスする時間の余裕があったため、インデックスソートによってどれくらいディスク容量が節約できるかを見てみたいと思いました。そこでまず、ソートされていないインデックスとしてリモートに再インデックスを実行しました。
status index pri docs.count docs.deleted pri.store.size open devices-2017 1 33310674 0 4.2gb
デバイスは30台より少し多いぐらいで、各デバイスは30秒ごとにステータスを送信、全体のインデックス登録の速度は1秒あたり1ドキュメントです。私はインデックス登録に関して制約を受けることはありません。インデックス登録の速度またはデバイス数を大きく増加させる必要があれば、影響を受けるでしょう。つまり、インデックスソートの利点が得られるケースに該当するようです。 データはハードウェア ID、ハードウェア名、時間、さまざまなセンサー値(温度、特定の時間におけるデバイスのオン/オフ、その他のセンサーレベルなど)で構成されています。インデックスをデバイスIDでソートし、そして時間でソートしたところ、あるデバイスではかなり高い確率で、ほぼ同じ時間に同様または同じ値が見られるらしいことが分かりました。つまり、さらに効率的に圧縮できる可能性があることになります。例えば、スイッチが7:00:00に「オン」の状態になる場合、そのオンになる時間が7:00:30、7:01:00、および少なくとも数分後になる可能性は非常に高く、この場合だと効率的に圧縮できることになります。 ソートされたインデックスの統計によると…
status index pri docs.count docs.deleted pri.store.size open devices-2017 1 3310674 0 2.5gb
ディスク容量を約40%節約できました!
再び注意
同じデータを40%も少ないディスク容量で格納できることは、誰にとってもありがたいことでしょう。ここで、皆さんにもう一度注意を喚起しなければなりません。要約すると次の2つの内容になります。
- 節約量は異なります。私は別のデータセットにインデックスソートを使用しましたが、そちらは20%の節約でした。どのフィールドでソートするか、慎重に考えてください。
- インデックスする速度が遅くなります。インデックスする速度が非常に重要な場合、例えば大量のロギングを実行している、またはメトリックのユースケースなどの場合、短時間でインデックスするにはドキュメント数に影響を受けるため、インデックスソートを実行するのに適していない可能性があります。
インデックスする速度よりもディスク容量のほうがはるかに重要な場合、またはインデックスの容量が少なく、インデックスする速度に制限を受けない場合は、インデックスソートから利点を得られるかどうか確認してみる価値はあるでしょう。
Elasticsearchに切り替えて世界最速スーパーコンピューターのサイバーセキュリティを向上
本記事は最近のElastic{ON} Tourでの事例紹介セッションの要約です。Elastic{ON}のトークはアーカイブで多数公開中です。また、お近くの都市で開催されるElastic{ON} Tourのスケジュールもご覧ください。
世界最速のスーパーコンピューター「サミット」は、米国エネルギー省(DOE) のオークリッジ国立研究所(ORNL)にあります。ORNLの研究者の仕事は演算速度200ペタフロップスの能力に支えられており、このことが物質科学、中性子科学、エネルギー、国家安全保障、高機能演算といった幅広い分野の科学的発見を促進しています。
これらすべての研究で生み出されたデジタル情報を保護するために、ORNLのサイバーセキュリティチームはElasticsearchとElastic Stackをセキュリティ情報およびイベント管理(SIEM)に用いています。しかし、最初からそうだったわけではありません。ワシントンでのElastic{ON} Tourで、ORNLのサイバーセキュリティ部門のエンジニアにしてSIEM管理者のLarry Nichols氏は、ORNLが大規模なログ監視と異常検知にわたる約20,000のエンドポイントのセキュリティ管理能力を改善するために、SplunkからElasticsearchへと移行した理由について解説しました。
6年間にわたり、ORNLのサイバーセキュリティチームはSplunkをSIEMに使用していました。しかしORNLのニーズが高まり、ますます大量のデータを投入することや300億件を超えるドキュメントの莫大なインデックスに対してクエリーを実行することが必要になってくると、いくつかの課題に直面して、別のソリューションを追求せざるを得なくなりました。Splunkのライセンスは1日あたりの投入データ量をベースにしており、このために研究所はデータを追加することが制限されていました。スピードも障害となっていました。ORNLの主要目標は研究の促進ですが、Splunkクラスターでは検索に15分もかかることがあり、データ分析から貴重な時間を奪っていたのです。「Splunkによる処理は私たちが望むよりも長く時間がかかり、アナリストはデータ収集を待つ間に自分で分析できるほどでした」(Nichols氏)。
Elasticsearchへの切り替えという決断はORNLにとって有意義なものでした。投入できるデータ量の制限がなくなり、何分もかかっていた検索が数秒に短縮されたのです。「私達には拡張できるハードウェアもリソースもありましたが、データの投入に余分なお金をかけたくなかったので、Elasticは明らかに魅力的でした」とNichols氏は語り、「スピードは明らかに、Elasticsearchの方がはるかに上でした」とも付け加えました。
Elasticsearchで、ORNLはスピードとセキュリティを強化したSIEMを展開することが可能になりました。現在、研究所の実働アーキテクチャでは、25のElsaticsearchのノードが、すべてDocker内で25の仮想マシンにわたって実行されています。このシステムには1日あたり20億以上のドキュメント(データ量約1.5TB)が投入され、180日分のデータ(3千億以上のドキュメント)を10のホットノードと7つのウォームノードにわたり保持しています。3つの機械学習ノード、3つのマスターノード、2つのコーディネーションノードも、合計120TBのディスク容量で実行しています。開発アーキテクチャは保持するログがより少ない(30日分)だけで、ほとんど同じです。ORNLには、研究とテスト専用の3番目のクラスターがあります。日々約1.5TBのデータが投入されるこのクラスターでは、物理サーバーに6つのElasticsearchノードを備えています。最後のクラスターはシングルノードで、他の3つのクラスターの監視に使われています。
Elasticsearchに加えElastic Stackの他の部分によって、セキュリティ上の問題の特定と対処を強化しています。チームはKibanaを使用しており、ユーザーの一般的な使用状況をひと目で確認したり、必要に応じて特定のユーザーに注目したりすることができます。またグラフによって、さまざまなインデックスに基づく関連データを可視化することも可能です。この情報により、1台のマシンの感染が判明した場合には、その問題を食い止め解決するために、感染したデバイスと通信した他のマシンをすばやく特定することができます。さらに最近、チームはCanvasの使用を始めており、ダイナミックなインフォグラフィックスタイルのダッシュボードを作って研究所でのアクティビティに関するハイレベルな画面を管理者層に提供しています。
ORNLがElasticsearchをどのように使用し世界最速のスーパーコンピューター上で検索とセキュリティ管理を強化しているか、さらに詳しくはElastic{ON} TourでのNichols氏のセッション全体をご覧ください。
ElasticsearchとElastic APMでアプリを監視する
APM(アプリケーションパフォーマンス監視)とは?
APMについて説明する場合、ログやインフラメトリックの"監視性"など、別の側面も考えるとよりわかりやすくなります。ログとAPM、インフラメトリックは監視の3大要素です。
3つの領域には重なり合う部分もあり、相互に関連付ける際に役立ちます。ログは、エラーが生じた痕跡を示すかもしれませんが、エラーの理由までは示しません。メトリックはサーバー上でCPU使用量にスパイクがあったことを示すかもしれませんが、何が原因だったかは示しません。しかし、うまく組み合わせて活用すれば、はるかに広い範囲の問題を解決できる可能性があります。
ログ
はじめにいくつかの定義について考えておきましょう。ログとメトリックには、わずかな違いがあります。通常、ログは何かが生じたときに発信されるイベントです。あるリクエストが受信されたとか、反応があったとか、ファイルを開いた、コードでprintf
が生じた... といった出来事です。
たとえば、Apache HTTPサーバーのプロジェクトからくるよくあるログ形式はこんな感じです(フェイクデータ、短縮のため一部省略)。
264.242.88.10 - - [22/Jan/2018:07:08:53 -0800] "GET /ESProductDetailView HTTP/1.1" 200 6291 264.242.88.10 - - [22/Jan/2018:07:08:53 -0800] "POST /intro.m4v HTTP/1.1" 404 7352 264.242.88.10 - - [22/Jan/2018:16:38:53 -0800] "POST /checkout/addresses/ HTTP/1.1" 500 5253
ログの情報は、アプリケーションの全体を把握するというより、コンポーネントレベルにとどまる傾向にあります。しかし人間が読む上では便利です。上の例では、IPアドレスと、明らかに設定のないフィールド、日付、ユーザーがアクセスしていたページ(とその方法)、いくつかの数字を確認することができます。私の経験から、一連の数字がレスポンスコード(200はよい、404は良くないが、500よりはマシ)であることと、データが返された量であることがわかります。
ログは通常、対応するアプリケーションやサービスが実行されているホスト/マシン/コンテナーインスタンス上で使用でき、この例のように人間が読める情報であるため、"便利"です。一方でログには、「コードを書いておかなければ、プリントされない」という本質的なデメリットもあります。Rubyでputs
を使うか、Javaでsystem.out.println
するか、あるいは類似の作業が必須です。またプリントする場合も、フォーマットが重要です。前出のApacheログに、見慣れない日付フォーマットが含まれています。たとえば"01/02/2019"という日付を考えてみましょう。米国に住む私にとっては2019年1月2日という意味ですが、世界の多くの仲間はこの日付を2019年2月1日と読みます。ロギングでstatementの形式を決定する際は、こうした点も考慮されることをお勧めします。
メトリック
一方、メトリックは周期的なサマリーやカウントの情報が主です。たとえばこの10秒間に、平均CPUは12%で、アプリケーションが使用したメモリ量は27MB、であるとか、プライマリディスクの容量は71%、といったことです(いま筆者のマシンのメトリックを確認してみました)。
上のスクリーンショットは、あるMacで実行されているiostat
です。多くのメトリックがあることがわかります。メトリックは傾向や履歴を示したいときに便利であり、シンプルで予測可能、また信頼できるルールを作成してインシデントや異常を捉える場合に特に役立ちます。メトリックのデメリットとして、インフラレイヤーの監視や、コンポーネントインスタンスレベル(ホスト、コンテナー、ネットワークなど)のデータ捕捉が中心で、カスタムアプリケーションレベルのデータをあまり扱わない点があります。というのも、メトリックは一定の経過時間に対してサンプルされるため、わずかな外れ値が"平均化"されるリスクを伴うためです。
APM
メトリックとログのギャップに橋を架ける存在がアプリケーションパフォーマンス監視です。ログやメトリックは、インフラや複数のコンポーネントを扱う横断的なデータです。APMは特にアプリケーションに注目し、エンドユーザーエクスペリエンスを含むスタック中のアプリ層を、IT部門や開発者が監視できるようサポートします。
監視にAPMを追加するメリットとして、次のような点があります。
- サービス提供にかかる時間と、クラッシュの原因を把握できる
- サービスと他の要素の通信状況や、ボトルネックを可視化できる
- パフォーマンスのボトルネックやエラーの予防的な発見・修正に役立つ
- 最良のシナリオは、多数のエンドユーザーが影響を受ける前の発見・修正
- 開発チームの生産性向上をサポート
- ブラウザー上でエンドユーザーエクスペリエンスを追跡可能
特に注目したいのが、"APMにはコードが通じる"という点です(この後詳しく取り上げます)。
ログとAPMで、得られる情報を比較してみましょう。先ほどこのようなログエントリがありました:
264.242.88.10 - - [22/Jan/2018:07:08:53 -0800] "GET /ESProductDetailView HTTP/1.1" 200 6291
先ほど見た通り、よい内容になっています。レスポンスに成功しており(200)、6,291バイトを返しました。しかし、レスポンスに16.6秒かかったことは示されていません。これは次のAPMスクリーンショットに表示されています。
その他にも豊富なコンテクストが見て取れます。最初のログには、このようなエラーもありました:
264.242.88.10 - - [22/Jan/2018:07:08:53 -0800] "POST /checkout/addresses/ HTTP/1.1" 500 5253
APMが捉えた内容はこうなります:
最終発生日時、発生頻度、アプリケーションで処理されたか否か、という情報が表示されています。たとえばNumberParseException
を使って例外処理の詳細を見ると、エラーが発生した回数の分布がウインドウで視覚的に表示されます。
パッと見て、一定の時間に数回起きているということ、一日中発生していることがわかります。ログで見ても、ログファイルの1つに対応するスタックの痕跡が見つかるはずです。しかしAPMのように、そのコンテクストやメタデータまで見つかる可能性は高くありません。
赤い長方形の部分はこの例外処理を実施したコード行を示し、APMが提供するメタデータが問題の正確な内容を伝えています。pythonプログラマーでない筆者のような人間が見てもどういう問題であるか正確に理解でき、チケットをオープンするために必要十分な情報があります。
スクリーンショットで見るAPMオプションツアー
Elastic APMについて一日中語ることもできる筆者ですが(よかったらTwitterで検索してください)、ここではスクリーンショットを使って、もう少し視覚的にご紹介したいと思います。早速ツアーをはじめましょう。
APMを開く
KibanaでAPMアプリケーションを開くと、Elastic APMに搭載されているすべてのサービスが表示されます:
サービスの詳細を見る
個々のサービスについて詳細を見ることができます。今回は"petclinic-spring"サービスを見てみましょう。各サービスとも、レイアウトは似ています:
左上にレスポンスタイムがあります。平均、95パーセンタイル、99パーセンタイルがあり、外れ値の部分を表示しています。さまざまな線グラフのエレメントを表示/非表示にして、外れ値がチャート全体に与える影響をわかりやすくすることもできます。右上はレスポンスコードです。時間に対してRPM(requests per minute/1分あたりリクエスト数)を詳しく表示しています。実際の画面では、各チャートの上でマウスを動かすとポップアップが現れ、特定の時間のサマリーを表示します。最初に得られるインサイトは、データを深堀りせずともすぐにわかります。すなわち、レイテンシの大きなスパイクに、"500"のレスポンス(サーバーエラー)がまったくありません。
トランザクションレスポンスタイムを詳しく見る
引き続きトランザクションサマリーを見ると、下の方にリクエストの詳細があります。各リクエストは、基本的には(さまざまなエージェントAPIを使用して初期設定を拡張可能できる)アプリケーション中の異なるエンドポイントです。リクエストは列見出しで並べ替えできますが、筆者が個人的に好んで使うのは"impact"列です。これは、そのリクエストのレイテンシと出現度を考慮します。今回の事例では、"getOwners"が最も問題を生じさせているように見えますが、それでも平均レイテンシは96ミリ秒に留まっています。このトランザクションを詳細にドロップダウンしていくと、先ほどと同じレイアウトが表示されます。
"ウォーターフォール作戦"
さて、最も遅いリクエストでも、1秒未満にレスポンスしていることがわかりました。下に向かってスクロールしてゆくと、トランザクション中のオペレーションが滝のように表示されています。
クエリ詳細表示
見ると確かに、大量のSELECT文があります。APMでは実行された実際のクエリを表示させることができます。
分散トレーシング
このアプリケーションスタックは、多層化されたマイクロサービスアーキテクチャーを扱っています。すべての層にElastic APMを組み込んであるため、[View full trace]ボタンを押してこのコールに関与する全要素が入るまでズームアウトすることが可能です。こうして、トランザクションに参加したすべてのコンポーネントの分散トレーシングを表示します:
多層的にトレーシングする
今回の事例で最初に見たのは、他の層がコールする"Spring"という層でした。いまこの画面で、"petclinic-node"が"petclinic-spring"層をコールしたことがわかります。これは2つの層でしたが、ブラウザー(React)層からはじまるより多層的な例もあります。
リアルユーザー監視
分散トレーシングの力を最大に引き出すためには、リアルユーザー監視(RUM)も含めて、できる限り多くのコンポーネントやサービスを組み込むことが重要です。サービスレスポンスタイムの早さが、必ずしもブラウザーでのすばやい動作を意味するわけではありません。つまり、ブラウザー上でのエンドユーザーエクスペリエンスを測定することこそが重要です。この事例で、分散トレーシングは4つの異なるサービスが同時に実行されていることを示しています。複数あるサービスの1つが、Webブラウザー(クライアント)です。53ミリ秒の時点でdomはインタラクティブでした。67ミリ秒では、domが完了していることがわかります。
UIにとどまらない魅力
Elastic APMは使いやすいAPM UIに加え、アプリ開発者に必要な機能を豊富にそろえています。そして、追跡データをUIで表示できるだけでなく、一歩深く活用できます。実は、Elastic APMデータは1つのインデックスとなっています。ログやメトリックから、事業データまでの情報が揃うことにより、サーバー遅延が収益に及ぼした影響を把握することも、影響の大きいリクエストを調べるなどして、次のコード拡張の計画策定にAPMデータを役立てることも可能です。
APMはデフォルトの可視化とダッシュボード機能を搭載しており、ログとメトリック、さらに事業データを組み合わせて可視化することができます。
Elastic APMを使いはじめる
Elastic APMは、LogstashやBeatsと似たデプロイトポロジーを使い、組み合わせて実行することができます:
APMサーバーはデータプロセッサーとして振る舞い、APMデータをAPMエージェントからElasticsearchに転送します。インストール手順もシンプルです。ドキュメントの"install and run"ページを参照いただくか、Kibanaで"K"ロゴをクリックしてホーム画面を表示し、"Add APM"オプションをクリックしてはじめることができます:
画面の案内にしたがって進むだけで、APMサーバーを稼働させることができます:
設定が完了したら、搭載させる各エージェントタイプについてKibanaのチュートリアルを参照することができます:
わずか数行のコードで、すべての設定が完了します。
Elastic APMの入口
新しいことを学ぶには、実際に手を動かしてみるのが一番です。また、その方法も1つではありません。実際のインターフェースをライブで、リアルに見てみたいという方はこちらのAPMデモ環境をクリックしてご覧ください。ローカルで実行してみたいという方は、APMサーバーダウンロードページの手順に沿って進めていただくことができます。
最も手軽な方法は、Elastic CloudのElasticsearch Serviceです。APMサーバー6.6、Kibanaインスタンス、機械学習ノードを含むすべてのElasticsearchデプロイが、SaaSで提供されており、1分ほどで使いはじめることができます(全サービスを2週間の無料トライアルでお試しいただけます)。クラウドサービスのため、デプロイインフラの保守/管理を行わなくて済むという大きなメリットがあります。
Elasticsearch ServiceでAPMを有効化する
APMクラスターを作成する(または既存のクラスターにAPMを追加する)には、クラスター設定の画面でAPM設定のセクションが表示されるまで下にスクロールします。既存のデプロイをアップデートする場合は[Enable]、[Save changes]の順にクリック、新規デプロイの場合は[Create deployment]をクリックします。
ライセンス
ElasticのAPMサーバーと、全APMエージェントはオープンソースとして提供されています。洗練されたAPM UIはElastic Stackでデフォルトの配布、また無料のベーシックライセンスより使用可能です。アラートや機械学習と統合する場合はベースとなるオプションのライセンスが必要になり、アラートにはゴールド、機械学習にはプラチナをご購入いただく必要があります。
まとめ
APMは、アプリケーションのあらゆる層で起きていることを表示します。機械学習やアラートと統合できるElastic APMなら検索機能を最大に活用でき、アプリケーションインフラの可視性が大きく向上します。トランザクションから痕跡、エラー、例外処理まで可視化でき、洗練されたAPMユーザーインターフェースがコンテクストも表示します。問題が発生していないときも、Elastic APMのデータを参考に修正内容の優先順位付けを行って、アプリケーションパフォーマンスの最大化と、ボトルネックの排除に取り組むことができます。
Elastic APMと監視に関するウェビナーも複数あります。併せてぜひご覧ください。
- Instrument and Monitor Java Applications using Elastic APM
- Using the Elastic Stack for Application Performance Monitoring
- Using Elasticsearch, Beats and Elastic APM to monitor your OpenShift Data
- Unifying APM, Logs, and Metrics for Deeper Operational Visibility
- Tracking your Infrastructure Logs & Metrics in the Elastic Stack (ELK Stack)
はじめてみませんか? APMに関するトピックはディスカッションフォーラムでもご覧いただけます。チケットの送信や機能のリクエストは、APM GitHub reposにお寄せください。
複数のデータ・センターにおける Elasticsearch クラスター間のレプリケーション
データセンター間のレプリケーションは、Elasticsearch のミッション・クリティカルな用途では、しばらくの間は要件となってきましたが、以前は他のテクノロジーによって部分的に解決されていました。Elasticsearch 6.7 でのクロス・クラスター・レプリケーションの導入により、データ・センター、地理的にも、または Elasticsearch クラスター全体にわたって、情報をレプリケートするための追加のテクノロジーは不要になります。
クラスター横断複製 (CCR) は、1つの Elasticsearch クラスターから1つ以上の Elasticsearch クラスターへの特定のインデックスのレプリケーションを可能にします。データの局所性を含む複数のレプリケーション(ユーザー/アプリケーション・サーバへの近い場所にデータをレプリケートする、製品カタログのレプリケーションを、20個の異なるデータセンターに複製するなど) に加えて、CCR にはさまざまな使用例(本社のクラスターへの1,000の銀行の支店から報告を目的としてレプリケーションするなど)があります。
このチュートリアルでは、CCR を使用したデータセンター間のレプリケーションについて、私たちは CCR の基本を簡単に紹介し、アーキテクチャオプションとトレードオフを強調し、データセンターを使用しない展開のサンプルを構成して、管理コマンドを強調します。CCR の技術概要については、「リーダーをフォローする: Elasticsearch でのクラスター間レプリケーションの概要」を参照してください。
CCR は、プラチナレベルの機能であり、試用版の API を通じて有効にするか、または Kibana から直接有効化できる30日間の試用ライセンスを使用できます。
クロスクラスターレプリケーション (CCR) の基本事項
レプリケーションはインデックス・レベル (またはインデックスパターンに基づいて)で構成される
CCR は Elasticsearch のインデックスレベルで構成されます。レプリケーションをインデックスレベルで構成することにより、一部のインデックスを一方向にレプリケートする、他のインデックスを別の方向に複製する、複数のデータセンターアーキテクチャなど、レプリケーションに関する多数の戦略を利用できます。
レプリケートされたインデックスは読み取り専用である
インデックスは、1つ以上の Elasticsearch クラスターによって複製できます。インデックスをレプリケートしている各クラスターは、インデックスの読み取り専用コピーを保持しています。アクティブなインデックス書き込みを受信できるのはリーダーといいます。そのインデックスのパッシブな読み取り専用コピーは、フォロワーと呼ばれます。新しいリーダーには選挙の概念はなく、リーダーインデックスが使用できない場合 (クラスターやデータセンターの停止など)、別のインデックスは、アプリケーションまたはクラスターアドミニストレーター (多くの場合別のクラスターでも可) によって書き込みが行われるよう明示的に選択する必要があります。
CCR の既定値は、多種多様な高スループットの用途で選択可能
値を調整することがシステムに与える影響を完全に理解していない限り、既定値を変更することはお勧めしません。 ほとんどのオプションは、"max_read_request_operation_count" や "max_retry_delay" などのフォロワーの作成 API にあります。すぐに、独自の作業負荷に合わせて、これらのパラメーターを調整するための情報を今後公開します。
セキュリティ要件
CCR 入門ガイドで説明しているように、ソース・クラスターのユーザーは、"read_ccr" クラスター特権、"監視"、および "読み取り" インデックスの特権を持っている必要があります。ターゲット・クラスター内では、「manage_ccr」クラスタ権限、「monitor」、「read」、「write」、「manage_follow_index」インデックスの権限が必要です。LDAP などの集中認証システムも使用できます。
複数データセンター CCR アーキテクチャの例
本番および DR データセンター
2つ以上のデータセンター
データは、datacenter A から複数のデータセンター (ダイアグラムの B と C) にレプリケートされます。データセンター B および C には、データセンター a 内のインデックスの読み取り専用コピーが現在保存されています。
チェーンレプリケーション
双方向のレプリケーション
データセンター間の導入に関するチュートリアル
1. セットアップ
このチュートリアルでは2つのクラスターを使用し、両方のクラスターはローカルコンピューター上にあります。どのような場所にいても、クラスターは自由に検索できます。
- 「us-cluster」: これは当社の「米国のクラスター」であり、ポート9200でローカルに実行します。ドキュメントは米国のクラスターから日本のクラスターに複製されます。
- ‘「japan-cluster」: 当社の「日本のクラスター」である、ポート8200でローカルに実行します。日本のクラスターでは、米国のクラスターからレプリケートされたインデックスを維持します。
2. リモートクラスターの定義
CCR を設定する場合、Elasticsearch クラスターは他の Elasticsearch クラスターを認識している必要があります。これは単一方向の要件であり、ターゲットクラスターはソースクラスターに対する単一方向の接続を維持します。他の Elasticsearch クラスターをリモートクラスターとして定義し、それらを説明するエイリアスを指定します。
日本のクラスターにおける「us-cluster」の認識を確保したいと思います。CCR でのレプリケーションはプルベースであるため、「us-cluster」から「japan-cluster」への接続を指定する必要はありません。
「japan-cluster」の API 呼び出しで 「us-cluster」 を定義します。
# From the japan-cluster, we’ll define how the us-cluster can be accessed PUT /_cluster/settings { "persistent" : { "cluster" : { "remote" : { "us-cluster" : { "seeds" : [ "127.0.0.1:9300" ] } } } } }
(APIベースのコマンドについては Kibana 内で Dev tools コンソールを使用することをおすすめしますが、これは Kibana-> Dev tools-> Console) から入手できます。
API の呼び出しでは、"127.0.0.1: 9300" でエイリアス "us-cluster" を持つリモートクラスターにアクセスできます。1つ以上のシードを指定できますが、ハンドシェイクフェーズ中にシードが使用できない場合には、一般に複数を指定することをお勧めします。
リモートクラスターの構成の詳細については、リファレンスドキュメントを参照してください。
また、「us-cluster」と接続するためにポート9300に注意しても重要なのは、elasticsearch がポート9200で HTTP プロトコルを耳にしていることです (これはデフォルトであり、「us-cluster」の yml ファイルで指定されています)。ただし、レプリケーションは、Elasticsearch のトランスポートプロトコル (ノード間通信のため) を使用して行われます。デフォルトはポート9300です。
Kibana 内では、リモートクラスターの管理 UI があり、このチュートリアルでは、ui と CCR の API を順を追って説明します。Kibana のリモートクラスター UI にアクセスするには、左側のナビゲーション・パネルで [管理] (歯車アイコン) をクリックし、Elasticsearch のセクションで「リモート・クラスター」に移動します。
Kibanaの Elasticsearch Remote Cluster Management UI
3. レプリケーションに使用するインデックスを作成する
「products」と呼ばれるインデックスを「us-cluster」に作成し、このインデックスをソース「 us-cluster」から「japan-cluster」に複製します:
"us-cluster" で、次のようになります。
# Create product index PUT /products { "settings" : { "index" : { "number_of_shards" : 1, "number_of_replicas" : 0, "soft_deletes" : { "enabled" : true } } }, "mappings" : { "_doc" : { "properties" : { "name" : { "type" : "keyword" } } } } }
"soft_deletes" の設定にお気づきかもしれません。CCR のリーダーインデックスとして機能するインデックスには、論理削除が必要です (ご存知ない方はこちらを参照してください)。
soft_deletes: 論理削除は、既存のドキュメントが削除または更新されたときに発生します。これらの論理削除を構成可能な制限まで保持することで、工程の履歴をリーダーシャードに保持し、工程の履歴を再現することでフォロワーシャードの作業に使用できるようになります。
フォロワーシャードがリーダーから操作を複製すると、リーダーシャードにマーカーが残され、リーダーのフォロワーの位置がわかるようになります。これらのマーカーの下にあるソフト削除された操作は、破棄対象となります。これらのマーカーの上にあるリーダーシャードは、シャード履歴保持リースの期間 (デフォルトは12時間) これらの操作を保持します。この期間により、致命的が遅れてリーダーから再ブートストラップされるリスクが生じる前にフォロワーをオフラインにできる期間が決定されます。
4. レプリケーションの開始
リモートクラスターのエイリアスを作成し、レプリケートするインデックスを作成したので、次にレプリケーションを開始しましょう。
「japan-cluster」において:
PUT /products-copy/_ccr/follow { "remote_cluster" : "us-cluster", "leader_index" : "products" }
このエンドポイントには ' product-copy ' が含まれていますが、これは "japan-cluster ' クラスター内の複製さインデックスの名前です。前に定義した ' us-cluster ' クラスターからレプリケートしていますが、レプリケートしようとしているインデックスの名前は、' us-cluster ' クラスター上の ' product ' と呼ばれています。
重要なのは、レプリケートされたインデックスが読み取り専用であり、書き込み操作を受け付けることができないという点です。
これでElasticsearch クラスター間で複製を行うインデックスを構成しました!
インデックス・パターンのレプリケーションの開始
お気づきかもしれませんが、上記の例は時間ベースの使用法には十分なものではなく、日次インデックス、または大量データに関しては正しく動作しません。また、CCR API には、複製するパターンを自動で指定するパターン、つまり、レプリケートする必要があるインデックスものを定義するメソッドも含まれています。
CCR API を使用して、自動フォローパターンを定義できます
PUT /_ccr/auto_follow/beats { "remote_cluster" : "us-cluster", "leader_index_patterns" : [ "metricbeat-*", "packetbeat-*" ], "follow_index_pattern" : "{{leader_index}}-copy" }
上記のサンプル API 呼び出しは、'metricbeat' または 'packetbeat' で始まるインデックスを複製します。
また、Kibana で CCR UI を使用して、自動フォローパターンを定義することもできます。
5. レプリケーションのセットアップのテスト
「us-cluster」から「japan-cluster」に製品を複製したので、今度はテスト用の文書を挿入し、それが複製されたことを確認してみましょう。
"us-cluster" クラスター上:
POST /products/_doc { "name" : "My cool new product" }
次に、「日本のクラスター」クエリして、ドキュメントが確実に複製されているか確認しましょう:
GET /products-copy/_search
1つのドキュメントが存在し、「us-cluster」に書き込まれて、「japan-cluster」に複製されるはずです。
{ "took" : 1, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 1, "max_score" : 1.0, "hits" : [ { "_index" : "products-copy", "_type" : "_doc", "_id" : "qfkl6WkBbYfxqoLJq-ss", "_score" : 1.0, "_source" : { "name" : "My cool new product" } } ] } }
データセンター間管理に関する注意事項
CCR の管理上の API、チューニング可能な設定、およびレプリケートされたインデックスを通常のインデックスに変換する方法の概要について見てみましょう。
レプリケーションの管理 API群
CCR では、Elasticsearch に、便利な管理用 API がいくつかあります。これらは、レプリケーションのデバッグ、レプリケーション設定の変更、詳細診断の収集などに役立つ場合があります。
# Return all statistics related to CCR GET /_ccr/stats # Pause replication for a given index POST /<follower_index>/_ccr/pause_follow # Resume replication, in most cases after it has been paused POST /<follower_index>/_ccr/resume_follow { } # Unfollow an index (stopping replication for the destination index), which first requires replication to be paused POST /<follower_index>/_ccr/unfollow # Statistics for a following index GET /<index>/_ccr/stats # Remove an auto-follow pattern DELETE /_ccr/auto_follow/<auto_follow_pattern_name> # View all auto-follow patterns, or get an auto-follow pattern by name GET /_ccr/auto_follow/ GET /_ccr/auto_follow/<auto_follow_pattern_name>
CCR 管理 APIs の詳細については、Elasticsearch のリファレンスドキュメントを参照してください。
フォロワーインデックスを通常のインデックスに変換する
上記の管理 API群 の一部を使用して、フォロワーインデックスを Elasticsearch の通常のインデックスに変換し、書き込みを受け入れることができます。
# Pause replication POST /<follower_index>/_ccr/pause_follow # Close the index POST /my_index/_close # Unfollow POST /<follower_index>/_ccr/unfollow # Open the index POST /my_index/_open
Elasticsearch の CCR (クロスクラスターレプリケーション) を引き続き探索する
このガイドは Elasticsearch での CCR の使用を開始するお手伝いをするために作成されたものですが、CCR を十分に理解していただき、CCR のさまざまな APIs (Kibana で使用できる UI を含む) について学習し、この機能を試していただければうれしいです。その他のリソースとしては、「クロスクラスターレプリケーションの概要」および「クロスクラスターレプリケーション API ガイド」を参照してください。
ディスカッションフォーラムにご意見やご感想をお寄せください。すべての質問については、できるだけ早く回答するように心がけています。
Elastic Stack 7.0.0 リリース
Elastic APMからLogstashまたはKafkaを通してデータを送信するには
Canvas:メトリック要素とマークダウン要素
KibanaでCanvasの要素を使用してみる
Canvasは現在、workpad(ブログ「KibanaでCanvasを使い始める」を参照)に追加できる約20の組み込みの要素を提供しています。このブログでは、そのうちの2つ、メトリックとマークダウンのみをご紹介します。
![]() |
メトリック:1つのデータ値とラベルを表示する簡素化されたテキストボックス |
![]() |
マークダウン:handlebars.js |
注:すでにCanvasを所有しており、サンプルデータがインストールされている場合は、レビューセクションをスキップしてチュートリアル本文(メトリックの使用)に進んでください
クイックレビュー
まだ「KibanaでCanvasを使い始める」ブログを読んでいない場合は、これにアクセスして記載の手順を実行することを強く推奨します。今回のこのブログでは前回説明したコンセプトを基に構築すること、および読者がすでに以下の状態であることを想定しているからです。
- ElasticsearchおよびKibanaを使用可能(バージョン6.4以降)
- Canvasをインストール済み
Canvas Workpadの作成
- サイドバーの[Canvas]タブをクリックします
- [Create workpad]をクリックします
- 新しいworkpadに一意の名前をつけます
サンプルデータのインストール
このチュートリアルでは、Elastic提供のサンプルデータセット、具体的にはsample flight dataを使用します。
注:このデータセットはKibanaバージョン6.4以降でのみ利用可能です。
Kibanaインスタンスにアクセスします。
- サイドバーでメインの[Kibana]ホームページをクリックします
- 「Add Data to Kibana」セクションの下部にあるリンク[Load a data set and a Kibana dashboard]をクリックします
- [Sample flight data]タイルで[Add]をクリックします
クイックリファレンス
下の表は、先ほどインストールしたサンプルデータセットの情報を示しています。太字の下線が引かれている項目はこのアクティビティの後のほうで使用しますが、その他の項目については自由に使ってみてください。
kibana_sample_data_flights | ||
AvgTicketPrice Carrier DestCityName DestCountry FlightDelayType FlightTimeMin OriginCityName OriginCountry Dest DestAirportID DestLocation |
DestRegion DestWeather DistanceKilometers DistanceMiles FlightDelay FlightDelayMin FlightNum FlightTimeHour Origin OriginAirportID |
OriginLocation OriginRegion OriginWeather _id _index _score _type dayOfWeek hour_of_day timestamp |
メトリックの使用
基本から開始
フライトの遅延総時間(分)を導き出し、メトリック要素で表示しましょう。そのためにはSQLクエリを使用して、サンプルデータセットのFlightDelayMin
フィールドから総時間を求めます。
- [Add Element]をクリックします
- [Metric]を選択します
- ヒント:初めて要素を作成すると、デモ用データが入った状態で表示され、すぐに各種の操作ができるようになっています。
- 右の編集パネルで[Data]タブを選択します
- [Change your data source]をクリックします
- [Elasticsearch SQL]を選択します
- SQLクエリエディターに、次のように入力します
SELECT SUM(FlightDelayMin) AS delay FROM kibana_sample_data_flights
詳細説明:SQLクエリは、インデックスkibana_sample_data_flights
からキー FlightDelayMin
のすべてのJSON値を選択します。このデータは「delay」という名前の「column」に返されます。
- [Save]をクリックします
メトリック要素には警告マークが表示されています。これは、要素が誤ったデータを指しているからです。
- 右の編集パネルの上部で[Display]タブを選択します
- 「Numbers」セクションで、次のように設定します
- Measurement:
Value
- Field:
delay
項目のカスタマイズ
任意の期間におけるフライトの遅延総時間を表示できるように、時間フィルターを設定します。
- [Add element]をクリックします
- [Time Filter]を選択します
メトリック要素には警告マークが表示されています。時間フィルターはworkpadのすべての要素にすぐに適用されますが、この警告マークはその時間フィルターがまだ正しく設定されていないことを示しています。
- 時間フィルターをworkpadの空白の場所に移動します
- 時間フィルター要素のデフォルトの時間フィールドは
@timestamp
ですが、ここでは適切ではありません。時間フィルターの編集パネルで、列の値をtimestamp
にします(つまり@
マークを削除します) - [Set]をクリックします
- 時間フィルターをクリックし、以下の期間から選択します
- 最近24時間
- 最近7日間
- 最近2週間
注:サンプルデータは4週間分あります。サンプルデータがインストールされた日を中心として、そこから過去のデータが2週間分、未来のデータが2週間分あります。
高度な内容
次に、フライト遅延時間の平均を求めてみましょう。ここで表示を見やすくするためには、背後で動作するコードを少しカスタマイズすることが必要になります。
- 作成した最初のメトリックが選択されていることを確認します
- 画面の右上隅にある[Duplicate]ボタンをクリックします
- 新しい時間フィルターをworkpadの空白の場所に移動します
- メトリックのラベルを次のように変更します。「Average Delay in Minutes」
- [Data]タブを選択します
- SQLクエリエディターに、次のように入力します
SELECT AVG(FlightDelayMin) AS delay FROM kibana_sample_data_flights
- [Save]をクリックします
- 数値のすべてが見えるようにメトリック要素を拡大します
- 次のような数値が表示されます。
47.33517114633586
- 四捨五入したほうがいいのは明らかです
- ElasticはCanvasドキュメントで、使用可能な数学関数のリストを提供しています
round(a,b)
関数を使います
- aは四捨五入する値
- bは小数点以下の桁数
- 3行目:
| math "round(delay,0)"
マークダウンの使用
基本から開始
フライト総数を導き出してマークダウン要素に表示します。そのためには、サンプルデータセットのFlightNum
フィールドに対してSQLコマンドを実行します。
- [Add Element]をクリックします
- [Markdown]を選択します
- ヒント:初めて要素を作成すると、デモ用データが入った状態で表示され、すぐに各種の操作ができるようになっています。
- 右の編集パネルで[Data]タブを選択します
- [Change your data source]をクリックします
- [Elasticsearch SQL]を選択します
- SQLクエリエディターに、次のように入力します
SELECT FlightNum FROM kibana_sample_data_flights
- [Save]をクリックします
- [Display]タブをクリックします
- 以下のものを除き、「Markdown content」エディターからすべてを削除します
**{{rows.length}} rows**
注:マークダウン要素は、その名前が示すとおり、マークダウン構文を完全にサポートします。例:**
、##
、```
、など。マークダウン構文に慣れていない方のために、上記の例では太字にするテキストを**
で囲んでいます。
- [Apply]をクリックします
- これでマークダウン要素にはデータセットの列の総数、つまりフライトの総数が表示されます。テキストを次のように更新します
Total number of flights: **{{rows.length}}**
- [Apply]をクリックします
- マークダウンエディターの画面右上隅にある[+]をクリックします
- ドロップダウンメニューで[Text Settings]を選択します
- テキスト設定で、次のように調節します
- フォントサイズ:
36
- アラインメント:
中央揃え
- テキストが見やすくなるようにマークダウン要素のサイズを調整します
項目のカスタマイズ
次に、いくつのフライトがどのような理由で遅延したかについて調べてみましょう。そのためには、FlightDelayTypeフィールドを使用します。
- [Add Element]をクリックします
- [Markdown]を選択します
- 右の編集パネルで[Data]タブを選択します
- [Change your data source]をクリックします
- [Elasticsearch SQL]を選択します
- SQLクエリエディターに、次のように入力します
SELECT FlightDelayType, COUNT(FlightDelayType) AS count FROM kibana_sample_data_flights GROUP BY FlightDelayType
- [Save]をクリックします
- [Display]タブをクリックします
- 以下のものを除き、「Markdown content」エディターからすべてを削除します
{{#each columns}} **{{name}}** {{/each}}
- 詳細説明:
- これはhandlebar.js構文です。SQLクエリで返された各列には、列の名前が出力されていますが、
FlightDelayType
列の各行の値を出力する必要があります。次の手順でこれを修正します。
- 「Markdown content」エディターで、次のように置き換えます
columns
をrows
に置き換えるname
をFlightDelayType
に置き換える
{{#each rows}} - {{FlightDelayType}} -- {{count}} {{/each}}
- 上記のコードの説明
- 1行目:各行に対し...
- 2行目:リスト項目であることを示すために「-」を入力し、2つの変数を「--」で区切って入力します
- 3行目:改行
- 4行目:ループを終了させます
- [Apply]をクリックします
高度な内容
先ほど作成したマークダウン要素では、行の1つが次のようになっています。
|
![]() |
- 先ほど作成したマークダウン要素(すべての遅延タイプを表示)が選択されていることを確認します。
- 画面の右下隅にある「Expression editor」をクリックします。するとコードエディターが開きます。ここで、選択した要素の表示形式を指定するコードを修正できます。
- 表現エディターでは以下に類似した記述になっているはずです。
filters | essql query="SELECT FlightDelayType, COUNT(FlightDelayType) AS count FROM kibana_sample_data_flights GROUP BY FlightDelayType" | markdown "{{#each rows}} - {{FlightDelayType}} -- {{count}} {{/each}}"
- まずfilterrows関数でデータをパイプし、その後、マークダウン要素にパイプします。10行目に改行を挿入し、
| markdown
関数のすぐ上にfilterrows
関数を追加します。行頭にパイプ(縦線)|
を入れるのを忘れないようにしてください。
... GROUP BY FlightDelayType" <b style="background-color:#ffae5b"><i>| filterrows {}</i></b> | markdown "{{#each rows}} - {{FlightDelayType}} -- {{count}} ...
FlightDelayType
列に含まれている値に基づいて行をフィルターします。そのためには、getCell
と呼ばれる別の関数を使用する必要があります。これによって、所定の列の各行の値が出力されます(「getCell」関数についてのドキュメントを参照)。
| filterrows { <b style="background-color:#ffae5b"><i>getCell “FlightDelayType”</i></b> }
- 次に、
any
と呼ばれる関数に、各行から値をパイプします。これは、 チェック条件に基づいてtrueまたはfalseを返します(「any」関数についてのドキュメントを参照)。
| filterrows {getCell “FlightDelayType” <b style="background-color:#ffae5b"><i>| any {}</i></b> }
- 最後に、チェック条件について「No Delay」 に等しくないという値を返すために、
neq
と呼ばれる関数を使用します(「neq」関数についてのドキュメントを参照)。
| filterrows {getCell “FlightDelayType” | any { <b style="background-color:#ffae5b"><i>neq “No Delay”</i></b> }}
- 表現エディターの右下隅にある[Run]をクリックします。「No Delay -- 2856」となっていた行が表示されなくなります。
その他の便利なリソースリンク
お疲れ様でした。Canvasのメトリック要素およびマークダウン要素を使用する例をいくつか見てきました。ぜひ他の要素もworkpadに追加して、Canvasのフル機能をお試しください。
Canvasのチュートリアルはこの他にもあります。ぜひご活用ください。
Canvas: データ表要素とデバッグ要素
Canvasは現在、workpadに追加できる約20の組み込みの要素(一覧についてはブログ「KibanaでCanvasを使い始める」を参照)を提供しています。このブログでは、そのうちの2つ、データ表およびデバッグ要素のみをご紹介します。
![]() |
データ表高度に柔軟かつ動的な表です。初期状態のままで、スクロール、ページネーション、カスタムCSSがサポートされます。 |
![]() |
デバッグ背後で動作するJSONデータへのアクセスを提供し、発生する問題をより正確に分析できます。 |
ここでは具体的に、非常に馴染みのあるデータ表である空港のフライト発着表を作成するためにCanvasを使用します。
下記がCanvasで作成する表の完成例です。
要件とレビュー
読者の環境が以下の条件を満たしていることを前提に、ブログ「KibanaでCanvasを使い始める」で説明した概念に基づき作成します。
- ElasticsearchおよびKibanaが稼働中(バージョン6.4以降)
- Canvasをインストール済み(CanvasはKibanaバージョン6.5以降に組み込み)
サンプルデータのインストール
このチュートリアルでは、Elastic提供のサンプルデータセット、具体的にはsample flight dataを使用します。
注:このデータセットはKibanaバージョン6.4以降でのみ使用できます。
Kibanaインスタンスにアクセスします。
- サイドバーでメインの[Kibana]ホームページをクリックします
- 「Add Data to Kibana」セクションの下部にあるリンク「Load a data set and a Kibana dashboard」をクリックします
- [Sample flight data]タイルで[Add]をクリックします
クイックリファレンス
下記の表は、先ほどインストールした、フライトのサンプルデータセットの情報を示しています。太字の下線が引かれている項目は、このアクティビティの後のほうで使用しますが、その他の項目については自由に使ってみてください。
kibana_sample_data_flights | ||
AvgTicketPrice Cancelled Carrier DestCityName DestCountry FlightDelayType FlightTimeMin OriginCityName OriginCountry Dest DestAirportID |
DestLocation DestRegion DestWeather DistanceKilometers DistanceMiles FlightDelay FlightDelayMin FlightNum FlightTimeHour Origin OriginAirportID |
OriginLocation OriginRegion OriginWeather _id _index _score _type dayOfWeek hour_of_day timestamp |
空港の発着表の作成
作成
最初にworkpadを作成し、次にデータ表にデータを追加します。
Canvas Workpadの作成
- Kibanaインスタンスにアクセスします
- サイドバーの[Canvas]タブをクリックします
- [Create workpad]をクリックします
- 新しいworkpadに一意の名前をつけます
データ表要素の作成
- [Add element]ボタンをクリックします
- [Data Table]要素を選択します
- ヒント:初めて要素を作成すると、デモ用データが入った状態で表示されるため、すぐに各種の操作ができます。
- 右の編集パネルで[Data]タブを選択します
- [Change your data source]をクリックします
- [Elasticsearch SQL]を選択します
- SQLクエリエディターに、次のように入力します
SELECT DestCityName AS Destination, timestamp AS Time, Carrier AS Airline, FlightNum AS Flight, FlightDelayType AS Status, Cancelled FROM kibana_sample_data_flights
注:このサンプルデータセットには空港のゲート番号のフィールドが含まれていません。このブログの後のほうで、ランダムに番号を生成し、その列を作成します。
- [Save]をクリックします
- 以下のようなデータ表ができているはずです
コード
これでデータが入力されたデータ表ができましたが、望んでいる形式にはなっていません。背後で動作するコードを表示し、調整する必要があります。
Time列の形式の調整
- データ表が選択されていることを確認します
- 画面の右下隅にある[Expression editor]をクリックします
- 式エディター内に次のコードが見つかるはずです
filters
| essql query="SELECT DestCityName AS Destination, timestamp AS Time, Carrier AS Airline, FlightNum AS Flight, FlightDelayType AS Status, Cancelled FROM kibana_sample_data_flights"
| table
| render
分類 -- このコードには次の4つのメインセクションがあります。
- フィルター: Time Filter要素をこのworkpadに追加すると、このデータ表要素に入力されたデータには最初に時間フィルターが適用されて、フィルターで除外されなかったデータのみが表示されることになります。この行を消去すると、データ表要素はworkpadに追加されるどのフィルター要素にも影響を受けることがなくなり、状況によっては便利です。
- データソース: Elastic SQLデータソースを使用しているため、ここでもSQLクエリの表示と編集ができます。
- 要素: この行は、workpadに表示する要素のタイプを定義します。試してみる場合は、「table」を「shape」に変更し、次に右下隅の[Run]をクリックして、どうなるか見てみましょう。元に戻すことを忘れないようにしてください。
- レンダリング: 要素の表示の見た目をカスタマイズできます。このブログの後のほうで、データ表をよりスタイリッシュにするために、カスタムのCSSをレンダリング関数に追加します。
- 「table」要素の関数で表示する前に、データを修正する必要があります。そのために、「essql」データソース関数と「table」要素の関数の間に、「mapColumn」という新しい関数を追加します。 この関数「mapColumn」は単に任意の列の値を修正するためのものです。ここで修正しようとしている列は「Time」列です。そのため、12列目に次のコードを追加します。
... FROM kibana_sample_data_flights" <b style="background-color:#ffae5b"><i>| mapColumn Time fn={}</i></b> | table ...
- Canvasには、「formatdate」関数など、利用可能な多くの組み込みの関数が用意されています。ここでは時間を「hh:mm A」の形式で表示します。そのために、コードの12行目に以下を追加します。
... FROM kibana_sample_data_flights" | mapColumn Time fn={ <b style="background-color:#ffae5b"><i>formatdate “hh:mm A”</i></b> } | table ...
- 式エディターの右下隅にある[Run]をクリックします
- すると、エラーになりました。デバッグが必要です
デバッグの時間(この手順をスキップしないでください)
後ですぐにコードに戻りますが、まずはCanvasでのデバッグ方法を見てみましょう。
エラーの特定
- 表要素の警告シンボルをクリックします。
- 下図のように、エラーの原因が表示されるはずです。
- 「mapColumn」関数がタイムスタンプデータを「number」にキャストしようとしているようです。これは、使用している「formatdate」関数が、数値形式のタイムスタンプ(UTCミリ秒など)を要求しているからです。
- 実際には、どのようなタイムスタンプ形式になっているのでしょうか。それを解明するために、「debug」要素を追加しましょう。
デバッグ要素の追加
- [Add element]をクリックします
- [Debug]要素を選択します
- 右の編集パネルで[Data]タブを選択します
- [Change your data source]をクリックします
- [Elasticsearch SQL]を選択します
- SQLクエリエディターに、次のように入力します
SELECT DestCityName AS Destination, timestamp AS Time, Carrier AS Airline, FlightNum AS Flight, FlightDelayType AS Status, Cancelled FROM kibana_sample_data_flights
- [Save]をクリックします
- デバッグ要素では、[Time]フィールドのタイプが「date」になっており、最初のエントリーは次のようになっています:2018-11-05T00:00:00.000Z
- ありがたいことに、Canvasには 「date」タイプをUTCミリ秒の数値に変換できる機能が組み込まれています。
- データ表を再度選択し、下記のとおり式エディターでコードに「date」関数を追加します。
| mapColumn Time fn={<b style="background-color:#ffae5b"><i> date | </i></b>formatdate “hh:mm A” }
- [Run]をクリックします
- データ表に適切な形式でタイムスタンプが表示されます。
ヒント:毎回、デバッグ要素を追加する必要はありません。どの要素の式エディターでも、コード
| render as="debug"
を追加して、その要素のJSONを見ることができます。ただし、作業中の参照用として、専用のデバッグ要素を準備しておくと便利です。
では、通常のプログラムに戻りましょう。
コードの続き
次に、「Gate」列を追加しましょう。このデータセットにはゲートのデータが含まれていないため、Canvasに組み込まれている強力な機能を使用してランダムに生成します。
ゲートのデータの追加
- 列を追加する最も簡単な方法は、SQLクエリにエントリーを追加することです。単純に「FlightNum」データを追加し、新しい列の名称を「Gate」にします。
- 右側のエディターパネルで[Data]タブをクリックし、SQLエディターで次の行を追加します。
SELECT DestCityName AS Destination, timestamp AS Time, Carrier AS Airline, FlightNum AS Flight, <b style="background-color:#ffae5b"><i>FlightNum AS Gate,</i></b> FlightDelayType AS Status, Cancelled FROM kibana_sample_data_flights
- [Save]をクリックします
- 式エディターで、1行目の下にもう1つ「mapColumn」関数を追加します。今回は「Gate」列を修正します。
... | mapColumn Time fn={ date | formatedate “hh:mm A” } <b style="background-color:#ffae5b"><i>| mapColumn Gate fn={}</i></b> | table ...
- 実際のゲート番号を取得するには、1~100の間の数字をランダムに生成する必要があります。ありがたいことに、Canvasには活用できる数学関数がいくつか組み込まれています。 ここで「random」関数を使用しますが、この関数では小数点以下の桁数の多い数字をランダムに生成するため、求めているものにはなりません。そこで、2つ目の関数を追加します。ランダムに生成した数字を四捨五入する「round」関数です。コードは次のようになります。
| mapColumn Gate fn={ <b style="background-color:#ffae5b"><i>math "round(random(0,100),0)"</i></b> }
- [Run]をクリックします
- これで、新しい「Gate」列にはすべて、ランダムに生成されたゲート番号が表示されるはずです。
列の結合
値が「true」のときは常に「Cancelled」ステータスが表示されるように、「Cancelled」列を「Status」列と組み合わせる必要があります。
- データ表要素が選択されていること、および式エディターが開いていることを確認します
- 「mapColumn」関数をもう1つ追加する必要がありますが、今回は「Status」列に追加します
... | mapColumn Gate fn={math "round(random(1,100),0)"} <b style="background-color:#ffae5b"><i>| mapColumn Status fn={}</i></b> | table ...
- 次に、「Cancelled」列のフィールド(または「cell」)が「true」になっているかどうかを確認する必要があります
| mapColumn Status fn={<b style="background-color:#ffae5b"><i>if {getCell "Cancelled" | eq true}</i></b>}
- そして、条件がtrueの場合に「Status」列の値が文字列「Cancelled」になるように設定します
| mapColumn Status fn={if {getCell “Cancelled” | eq true}<b style="background-color:#ffae5b"><i> then=”Cancelled” </i></b>}
- ここで[Run]をクリックすると、「Cancelled」を「Status」列にマッピングできます。ただし、true以外の場合については「Status」の値が「null」値になります。
- 最後に必要な作業は、「Status」列の値が「Cancelled」ではない場合に、元の値を維持するようCanvasに指示することです。
| mapColumn Status fn={if {getCell “Cancelled” | eq true} then=”Cancelled”<b style="background-color:#ffae5b"><i> else={getCell "Status"}</i></b>}
- [Run]をクリックします
- これで、「Status」列を「Cancelled」列と組み合わせることができました。
列の削除
これで「Status」列に、必要な情報をすべて含めることができたので、「Cancelled」列を表示する必要がなくなりました(背後では機能している必要があります)。そのため、「Cancelled」列を削除します。
- データ表要素が選択されていること、および式エディターが開いていることを確認します
- Canvasには、列を含めるまたは除外するのに使用する「columns」という関数があります。ここでは「Cancelled」列を除外します。次のとおり、コードを1行追加します。
... | mapColumn Status fn={if {getCell “Cancelled” | eq true} then=”Cancelled” else={getCell “Status”}} <b style="background-color:#ffae5b"><i>| column exclude=”Cancelled”</i></b> | table ...
- [Run]をクリックします
- 背後ではその列のデータを使用していますが、データ表要素に「Cancelled」列が表示されなくなっているはずです。
カスタマイズ
これで望むとおりのデータがすべて揃いました。次は、workpadの表示の見た目をカスタマイズしてみましょう。
背景色の設定
- workpadで色が何も選択されていないことを確認します
- ページ右側の編集パネルで[Page Background]カラーピッカーをクリックし、値を「#0276fd」に設定します
ページネーションの削除
- データ表要素を選択します
- ページの右側の編集パネルで、[Display]タブを選択します
- [Table Style]パネルの[+]ボタンをクリックします
- ドロップダウンから[Pagination]を選択します
- 切り替えボタンをクリックしてオフにします
表の行数の設定
- 再度、[Table Style]パネルの[+]ボタンをクリックします
- ドロップダウンから[Rows per page]を選択します
- ページごとの行数を25に増やします
- 実際には18行にするので、データ表要素の式エディターを開き、「perPage」の値を18に変更します
| table paginate=false perPage=<b style="background-color:#ffae5b"><i>18</i></b>
- [Run]をクリックします
- 18行すべてが表示されるようにデータ表要素を広げます
表テキストのスタイル設定
- 再度、[Table Style]パネルの[+]ボタンをクリックします
- 今回は[Text Settings]を選択します
- テキストを太字に、色をホワイトに設定します
表ヘッダーのスタイル設定
- 表の他の行よりも目立つように、表のヘッダーのスタイルを設定します。すでに[Table Style]パネルでできることは見てきたので、ここではカスタムCSSを使います
- そのためには、[Element Style]パネルの[+]ボタンをクリックします
- ドロップダウンメニューから[CSS]を選択します
- CSSエディターの内容を消去し、次のコードをCSSエディターにペーストします
canvasDataTable__th { text-decoration: underline; background-color: #d0e9ff; color: black;}
- [Apply stylesheet]をクリックします
- これで表のヘッダーには、薄いブルーの背景色とブラックの下線が引かれたテキストが表示されるはずです
表の行のスタイル設定
- 表の行の色が交互になるよう設定するために、ここでもカスタムCSSを使用します
- 次のコードをCSSエディターにペーストします
.canvasDataTable__tbody>:nth-child(even) { background-color: #2a2a50; } .canvasDataTable__tbody>:nth-child(odd) { background-color: #5f67af; }
- [Apply stylesheet]をクリックします
- workpadの幅に合うようにデータ表要素の幅を調整します
- 下図のように、行の色が交互になるように表示されるはずです
タイトルの追加
- [Add element]をクリックします
- [Markdown]要素を選択します
- 画面右側の[Markdown content]エディターにあるすべてのテキストを削除します
- [Markdown content]エディターに「Departures」と入力します
- [Apply]をクリックします
- [Markdown]要素のサイズを調整し、画面の中央に揃えます
- 右側の編集エリアにある [Markdown]パネルで、[+]ボタンをクリックします
- ドロップダウンメニューから[Text Settings]を選択します
- テキストを次のとおりに設定します
- サイズ:48
- フォント:太字
- アラインメント:中央揃え
- 色:ホワイト
- これで、実際に空港で見るものによく似たworkpadが完成したはずです。
完成したコード
下記は、式エディターでのデータ表の完全なコードです。
filters | essql query="SELECT DestCityName AS Destination, timestamp AS Time, Carrier AS Airline, FlightNum AS Flight, FlightNum AS Gate, FlightDelayType AS Status, Cancelled FROM kibana_sample_data_flights " | mapColumn "Time" fn={date | formatdate "hh:mm A"} | mapColumn "Gate" fn={math "round(random(1,100),0)"} | mapColumn "Status" fn={if {getCell "Cancelled" | eq true} then="Cancelled" else={getCell "Status"}} | columns exclude="Cancelled" | table paginate=false perPage=18 font={font family="'Open Sans', Helvetica, Arial, sans-serif" size=14 align="left" color="#FFFFFF" weight="bold" underline=false italic=false} | render css=".canvasDataTable__th { text-decoration: underline; background-color: #d0e9ff; color: black;} .canvasDataTable__tbody>:nth-child(even) { background-color: #2a2a50; } .canvasDataTable__tbody>:nth-child(odd) { background-color: #5f67af; }"
その他の便利なリソースリンク
お疲れ様でした。Canvasのデータ表要素およデバッグ要素を使用する例をいくつか見てきました。ぜひ他の要素もworkpadに追加して、Canvasのフル機能をお試しください。
Canvasのブログ記事はこの他にもあります。ぜひご活用ください。
バナー画像:MPD01605による「Miami Airport Screen」はCC BY 2.0 によって許可を得ています。
Elastic Stackで一元管理データベースを監視する
本記事はElastic{ON} 2018での事例紹介セッションの要約です。Elastic{ON} 2018のトークはアーカイブで多数公開中です。また、お近くの都市で開催されるElastic{ON} Tourのスケジュールもご覧ください。
シティグループは世界100か国以上で金融サービスを展開し、世界160か国以上に顧客を抱えています。同社の大規模でグローバルな運用インフラを支える部門が、ビジネスアプリケーションチームです。事業規模が大きく、ITインフラが複雑であることから、ビジネスアプリケーションチームはこれまで数多くの難題に直面してきました。スケール化や事業回復力、ITアジリティ、IT資産活用、ITツーリング(ITツールの最適化)、新たなテクノロジーの導入など、シティグループのITチームは近年さまざまな側面で急速な進化を迫られています。同時に、コスト最適化も重要です。
こうした状況下で、特に大きな課題となっていたのが部門間でのITツーリング(ITツールの最適化)です。自動化ソフトウェアのデプロイや、デプロイ環境の増設、マルチユースな一元化ツールへの移行など、新たなトレンドに対応できるITツールはあらゆる部門で求められていました。基幹業務に関する技術的課題を多く抱える一方、ITチームはシティグループの経営陣からも多くの問い合わせを受けていました。経営陣もITインフラの活用や、ツールが事業にもたらすメリットにますます関心を寄せるようになったためです。
シティグループが業務を監視するためには、大量のデータが必要です。しかし、データが異なる部門や国のあちこちに、さまざまな形式で存在している限り、ツールやインフラの状態、運用の改善に役立つようなエンドツーエンドのインフラ監視システムを構築することは不可能です。そこでITチームはあらゆる場所で既存、および新規のツーリングパフォーマンスを追跡・監視でき、そのパフォーマンスを経営陣にわかりやすいレポートとして出力する統合監視システムを構築することにしました。
新たな統合監視システムには、次のすべての機能と仕様が必要です。
- 一元化されたデータストアとして振る舞うことが可能
- エージェントを設定可能
- ツールライセンスのサポートコストを軽減
- データガバナンスを統一し、一元化する
Elastic Stackを使ったデプロイ
類似のアーキテクチャーで検索解析テクノロジーが確かな実績を持つことから、シティグループのITチームはElastic Stackに注目しました。新たなインフラでツールのパフォーマンスを監視するには、メトリック、イベント、ログ(全社で1日に9千万ドキュメント)を投入する必要がありますが、Elasticsearchを使えば、あらゆるデータを1か所にまとめて格納、検索できるようになります。シティグループはElasticsearchを使用し、アクティブな、プライマリデータストレージクラスターを構築しました。このクラスターは各種データを30日間保持し、検索と可視化に対応します。30日を経過すると、自動のスナップショットでElasticsearchから安価なオンプレミスのオブジェクトストレージへデータを移行させる設計です。
シティグループでは、セキュリティオペレーションセンターと監査用のインデックス、さまざまなユースケースとレベルに応じたアクセス権限も設計する必要がありました。Elasticsearchを導入したことで、ITチームはデータのグループ化、ネーミングスキームの標準化、ロールベースのアクセス制御と、データガバナンス用認証の定義も実現させました。さらにシティグループではITチームと経営陣向けに、通常のダッシュボードと社内用APIを組み合わせ、手軽にデータを取得・分析できる環境を構築しています。基幹業務部門でもこのAPIを呼び出し、取得したデータをローカルシステムに投入して部署ごとに詳しく分析することができます。
シティグループではアラート(旧Watcher)を活用したコンテナーインフラ監視を構築し、1つの部署に限定せず、全部門にわたりコンテナーのメトリック、イベント、ログを収集するシステムも開発しました。収集された情報は監査、アラート管理、システム管理などの幅広いユースケースに使用されています。
監視ニーズの他に、シティグループのITチームでは経営陣を含む多様なユーザーグループにデータから得たインサイトを共有するスケーラブルで安全な手段を必要としていました。そこで作成されたのがKibanaをベースとするダシュボードです。このダッシュボードはリアルタイムなアプリケーション状態の集約ビュー、さらにメトリックやログ、コンテナーリソースの使用状況、トポロジー情報に関する個別のビューを表示します。ユーザーはこのダッシュボードでアプリケーションやコンテナーの詳細情報を取得し、ソースの問題を把握することができます。データ表示の切り替えも簡単です。運用チームとアプリケーションチームはツーリングのパフォーマンス監視やレポートの実行に際してトップダウン、あるいはボトムアップビューでデータを取得し、レポートから各種ツールの実際の効率を把握しています。
シティグループでインフラの監視を支える高度な検索解析テクノロジーについては、Elastic{ON}の事例紹介セッション「シティグループが実践するアプリインフラ監視」でさらに詳しくご紹介しています。
Elastic APMからLogstashまたはKafkaを通してデータを送信するには
Canvas:メトリック要素とマークダウン要素
KibanaでCanvasの要素を使用してみる
Canvasは現在、workpad(ブログ「KibanaでCanvasを使い始める」を参照)に追加できる約20の組み込みの要素を提供しています。このブログでは、そのうちの2つ、メトリックとマークダウンのみをご紹介します。
![]() |
メトリック:1つのデータ値とラベルを表示する簡素化されたテキストボックス |
![]() |
マークダウン:handlebars.js |
注:すでにCanvasを所有しており、サンプルデータがインストールされている場合は、レビューセクションをスキップしてチュートリアル本文(メトリックの使用)に進んでください
クイックレビュー
まだ「KibanaでCanvasを使い始める」ブログを読んでいない場合は、これにアクセスして記載の手順を実行することを強く推奨します。今回のこのブログでは前回説明したコンセプトを基に構築すること、および読者がすでに以下の状態であることを想定しているからです。
- ElasticsearchおよびKibanaを使用可能(バージョン6.4以降)
- Canvasをインストール済み
Canvas Workpadの作成
- サイドバーの[Canvas]タブをクリックします
- [Create workpad]をクリックします
- 新しいworkpadに一意の名前をつけます
サンプルデータのインストール
このチュートリアルでは、Elastic提供のサンプルデータセット、具体的にはsample flight dataを使用します。
注:このデータセットはKibanaバージョン6.4以降でのみ利用可能です。
Kibanaインスタンスにアクセスします。
- サイドバーでメインの[Kibana]ホームページをクリックします
- 「Add Data to Kibana」セクションの下部にあるリンク[Load a data set and a Kibana dashboard]をクリックします
- [Sample flight data]タイルで[Add]をクリックします
クイックリファレンス
下の表は、先ほどインストールしたサンプルデータセットの情報を示しています。太字の下線が引かれている項目はこのアクティビティの後のほうで使用しますが、その他の項目については自由に使ってみてください。
kibana_sample_data_flights | ||
AvgTicketPrice Carrier DestCityName DestCountry FlightDelayType FlightTimeMin OriginCityName OriginCountry Dest DestAirportID DestLocation |
DestRegion DestWeather DistanceKilometers DistanceMiles FlightDelay FlightDelayMin FlightNum FlightTimeHour Origin OriginAirportID |
OriginLocation OriginRegion OriginWeather _id _index _score _type dayOfWeek hour_of_day timestamp |
メトリックの使用
基本から開始
フライトの遅延総時間(分)を導き出し、メトリック要素で表示しましょう。そのためにはSQLクエリを使用して、サンプルデータセットのFlightDelayMin
フィールドから総時間を求めます。
- [Add Element]をクリックします
- [Metric]を選択します
- ヒント:初めて要素を作成すると、デモ用データが入った状態で表示され、すぐに各種の操作ができるようになっています。
- 右の編集パネルで[Data]タブを選択します
- [Change your data source]をクリックします
- [Elasticsearch SQL]を選択します
- SQLクエリエディターに、次のように入力します
SELECT SUM(FlightDelayMin) AS delay FROM kibana_sample_data_flights
詳細説明:SQLクエリは、インデックスkibana_sample_data_flights
からキー FlightDelayMin
のすべてのJSON値を選択します。このデータは「delay」という名前の「column」に返されます。
- [Save]をクリックします
メトリック要素には警告マークが表示されています。これは、要素が誤ったデータを指しているからです。
- 右の編集パネルの上部で[Display]タブを選択します
- 「Numbers」セクションで、次のように設定します
- Measurement:
Value
- Field:
delay
項目のカスタマイズ
任意の期間におけるフライトの遅延総時間を表示できるように、時間フィルターを設定します。
- [Add element]をクリックします
- [Time Filter]を選択します
メトリック要素には警告マークが表示されています。時間フィルターはworkpadのすべての要素にすぐに適用されますが、この警告マークはその時間フィルターがまだ正しく設定されていないことを示しています。
- 時間フィルターをworkpadの空白の場所に移動します
- 時間フィルター要素のデフォルトの時間フィールドは
@timestamp
ですが、ここでは適切ではありません。時間フィルターの編集パネルで、列の値をtimestamp
にします(つまり@
マークを削除します) - [Set]をクリックします
- 時間フィルターをクリックし、以下の期間から選択します
- 最近24時間
- 最近7日間
- 最近2週間
注:サンプルデータは4週間分あります。サンプルデータがインストールされた日を中心として、そこから過去のデータが2週間分、未来のデータが2週間分あります。
高度な内容
次に、フライト遅延時間の平均を求めてみましょう。ここで表示を見やすくするためには、背後で動作するコードを少しカスタマイズすることが必要になります。
- 作成した最初のメトリックが選択されていることを確認します
- 画面の右上隅にある[Duplicate]ボタンをクリックします
- 新しい時間フィルターをworkpadの空白の場所に移動します
- メトリックのラベルを次のように変更します。「Average Delay in Minutes」
- [Data]タブを選択します
- SQLクエリエディターに、次のように入力します
SELECT AVG(FlightDelayMin) AS delay FROM kibana_sample_data_flights
- [Save]をクリックします
- 数値のすべてが見えるようにメトリック要素を拡大します
- 次のような数値が表示されます。
47.33517114633586
- 四捨五入したほうがいいのは明らかです
- ElasticはCanvasドキュメントで、使用可能な数学関数のリストを提供しています
round(a,b)
関数を使います
- aは四捨五入する値
- bは小数点以下の桁数
- 3行目:
| math "round(delay,0)"
マークダウンの使用
基本から開始
フライト総数を導き出してマークダウン要素に表示します。そのためには、サンプルデータセットのFlightNum
フィールドに対してSQLコマンドを実行します。
- [Add Element]をクリックします
- [Markdown]を選択します
- ヒント:初めて要素を作成すると、デモ用データが入った状態で表示され、すぐに各種の操作ができるようになっています。
- 右の編集パネルで[Data]タブを選択します
- [Change your data source]をクリックします
- [Elasticsearch SQL]を選択します
- SQLクエリエディターに、次のように入力します
SELECT FlightNum FROM kibana_sample_data_flights
- [Save]をクリックします
- [Display]タブをクリックします
- 以下のものを除き、「Markdown content」エディターからすべてを削除します
**{{rows.length}} rows**
注:マークダウン要素は、その名前が示すとおり、マークダウン構文を完全にサポートします。例:**
、##
、```
、など。マークダウン構文に慣れていない方のために、上記の例では太字にするテキストを**
で囲んでいます。
- [Apply]をクリックします
- これでマークダウン要素にはデータセットの列の総数、つまりフライトの総数が表示されます。テキストを次のように更新します
Total number of flights: **{{rows.length}}**
- [Apply]をクリックします
- マークダウンエディターの画面右上隅にある[+]をクリックします
- ドロップダウンメニューで[Text Settings]を選択します
- テキスト設定で、次のように調節します
- フォントサイズ:
36
- アラインメント:
中央揃え
- テキストが見やすくなるようにマークダウン要素のサイズを調整します
項目のカスタマイズ
次に、いくつのフライトがどのような理由で遅延したかについて調べてみましょう。そのためには、FlightDelayTypeフィールドを使用します。
- [Add Element]をクリックします
- [Markdown]を選択します
- 右の編集パネルで[Data]タブを選択します
- [Change your data source]をクリックします
- [Elasticsearch SQL]を選択します
- SQLクエリエディターに、次のように入力します
SELECT FlightDelayType, COUNT(FlightDelayType) AS count FROM kibana_sample_data_flights GROUP BY FlightDelayType
- [Save]をクリックします
- [Display]タブをクリックします
- 以下のものを除き、「Markdown content」エディターからすべてを削除します
{{#each columns}} **{{name}}** {{/each}}
- 詳細説明:
- これはhandlebar.js構文です。SQLクエリで返された各列には、列の名前が出力されていますが、
FlightDelayType
列の各行の値を出力する必要があります。次の手順でこれを修正します。
- 「Markdown content」エディターで、次のように置き換えます
columns
をrows
に置き換えるname
をFlightDelayType
に置き換える
{{#each rows}} - {{FlightDelayType}} -- {{count}} {{/each}}
- 上記のコードの説明
- 1行目:各行に対し...
- 2行目:リスト項目であることを示すために「-」を入力し、2つの変数を「--」で区切って入力します
- 3行目:改行
- 4行目:ループを終了させます
- [Apply]をクリックします
高度な内容
先ほど作成したマークダウン要素では、行の1つが次のようになっています。
|
![]() |
- 先ほど作成したマークダウン要素(すべての遅延タイプを表示)が選択されていることを確認します。
- 画面の右下隅にある「Expression editor」をクリックします。するとコードエディターが開きます。ここで、選択した要素の表示形式を指定するコードを修正できます。
- 表現エディターでは以下に類似した記述になっているはずです。
filters | essql query="SELECT FlightDelayType, COUNT(FlightDelayType) AS count FROM kibana_sample_data_flights GROUP BY FlightDelayType" | markdown "{{#each rows}} - {{FlightDelayType}} -- {{count}} {{/each}}"
- まずfilterrows関数でデータをパイプし、その後、マークダウン要素にパイプします。10行目に改行を挿入し、
| markdown
関数のすぐ上にfilterrows
関数を追加します。行頭にパイプ(縦線)|
を入れるのを忘れないようにしてください。
... GROUP BY FlightDelayType" <b style="background-color:#ffae5b"><i>| filterrows {}</i></b> | markdown "{{#each rows}} - {{FlightDelayType}} -- {{count}} ...
FlightDelayType
列に含まれている値に基づいて行をフィルターします。そのためには、getCell
と呼ばれる別の関数を使用する必要があります。これによって、所定の列の各行の値が出力されます(「getCell」関数についてのドキュメントを参照)。
| filterrows { <b style="background-color:#ffae5b"><i>getCell “FlightDelayType”</i></b> }
- 次に、
any
と呼ばれる関数に、各行から値をパイプします。これは、 チェック条件に基づいてtrueまたはfalseを返します(「any」関数についてのドキュメントを参照)。
| filterrows {getCell “FlightDelayType” <b style="background-color:#ffae5b"><i>| any {}</i></b> }
- 最後に、チェック条件について「No Delay」 に等しくないという値を返すために、
neq
と呼ばれる関数を使用します(「neq」関数についてのドキュメントを参照)。
| filterrows {getCell “FlightDelayType” | any { <b style="background-color:#ffae5b"><i>neq “No Delay”</i></b> }}
- 表現エディターの右下隅にある[Run]をクリックします。「No Delay -- 2856」となっていた行が表示されなくなります。
その他の便利なリソースリンク
お疲れ様でした。Canvasのメトリック要素およびマークダウン要素を使用する例をいくつか見てきました。ぜひ他の要素もworkpadに追加して、Canvasのフル機能をお試しください。
Canvasのチュートリアルはこの他にもあります。ぜひご活用ください。
Elastic Stack 6.5.0 リリース
Kibana 6.5.0 リリース
Kibana リリースの時間です! 6.5 のリリースには、スペース、ロールアップのサポート、およびCanvasなど、数多くの新しい影響の大きな機能が含まれています。また、新しいインスペクター機能やConsoleの改善など、中程度か小さな機能も備えています。
Kibana 6.5.0をダウンロード
Kibana 6.5.0 release notes
Plugin API changes
詳細については上記リリースノートに記載がありますが、中でも注目の機能は以下の通りです:
- スペースで作業を整理する
- データをCanvasに表示する
- Kibanaからロールアップジョブを作成する
- 2つの新しいサンプルデータセット
- Consoleの改善
- Spy パネルに代わる新しいインスペクター機能
スペースで作業を整理する
スペースは、最も要求の多かった機能の1つです。スペースを使用すると、保存されたオブジェクトを意味のあるカテゴリに整理できます。たとえば、sales の領域にはすべての sales のビジュアルを、ログ領域にはすべてのログ記録オブジェクトを入れます。スペースは必要に応じていくつでも作成でき、いつでもスペースを変更したり、保存したオブジェクトをスペース間で移動したりすることが可能です。セキュリティを有効にすると、どの役割およびユーザーがどのスペースにアクセスできるかを、RBAC を使用して制御できます。
スペースを作成、編集、および削除するには、Management > Spaces を使用します。または、プログラムで作成する場合は Kibana の Spaces API のように使用できます。
Canvasでプレゼンテーション(ベータ版)
Canvasは、データを1ピクセルの完璧なデザインで表示するための新しいスペースで、 Kibana、6.5 でテストすることができます。新しいワークパッドを使用すると、静的なコンテンツ (テキスト、イメージ、および図形) とデータ駆動型の要素 (グラフ、テーブル、画像など) を組み合わせることによって、データに関するストーリーを作成することができます。既定では、各動的要素はサンプルデータソースに接続されるので、独自のデータに接続する前に、実際にテストを試すことができます。Canvas はクエリの為に、Elasticsearch: es docs、SQL、およびTimelineの複数の言語をサポートしています。
Canvasツールには、リッチスタイル機能があり、ユーザーは静的要素と動的エレメントの両方の色とスタイルを変更できます。Canvas は、UI を使用して要素のコンテンツやスタイルを変更したり、要素の階層を深くしたり、css スタイルを作成する式を編集したりできるように設計されています。つまり、Canvasには、ライブデータの表示に必要なすべてのものが用意されています。
ロールアップジョブを作成および管理する
Kibana には、ロールアップジョブの作成、開始、停止、および削除を行うための新しい管理 UI があります。ロールアップは、Elasticsearch および ロールアップ を作成および管理するための API が6.4 以降で使用可能になっています。ロールアップインデックスは、履歴データを集計し、それを将来の分析のためにコンパクトにに保存するため、ストレージの一部を使用してこのデータをクエリ、集計、および視覚化できます。ロールアップジョブは、ロールアップインデックスのデータを集計する定期的なタスクです。UI に移動するには、Management に移動し、Elasticsearch のRollup Jobsをクリックします。
可視化におけるロールアップデータの表示 (ベータ)
Kibanaには、ロールアップされたデータを視覚化するベータ機能があります。ロールアップインデックスまたは混合ロールアップと生のインデックスを使用して、すべてのデータをまとめて視覚化するインデックスパターンを作成できます。ほとんどのビジュアル表示は、Timeline、Visual Builder、および Vega の視覚エフェクトを除いて、データのロールアップをサポートしています。また、ロールアップされたデータと未処理の情報に基づいて、視覚エフェクトを使用するダッシュボードを作成することもできます。最後に、ロールアップされた生データの両方が Discover で利用可能です。
2 つの新しいサンプルデータセット
新しいユーザーエクスペリエンスに合わせて調整された2つの新しいワンクリックのサンプルデータセットがあります。ビジネス分析に興味がある場合は、電子商取引のデータセットに、コスト、収益、価格など、製品に関連する情報を視覚化するものが含まれている場合は、それらをインストールします。web サイトのトラフィックを分析する場合は、web ログのサンプルデータセットを確認してください。
サンプルデータにアクセスするには、Kibana ホームページに移動し、[Load a data set and a Kibana dashboard] をクリックします。また、従来のダッシュボードには、各サンプルがCanvasワークパッドでパッケージ化されています。電子商取引のサンプルのワークパッドには、同じデータに対して2つの異なるスタイルがあります。ワークパッドは、新しいユーザーがCanvasを学習するための手段として便利です。
Consoleの強化
このリリースでは、Consoleのオートコンプリート機能が、テンプレートエンドポイントで利用可能なクエリの追加の DSL タイプ(#19178を参照)およびテンプレート(#20141を参照)に拡張されています。また、Tools ドロップダウンメニューの特定のエンドポイントのドキュメントへのリンクも追加しています。ドキュメントは、ショートカットキーの CTRL/CMD +/からも使用できます。#19715も参照ください。
最後に、構文を強調表示してGrokデバッガーを拡張しました(#18572を参照).
Spy パネルに代わる新しいインスペクター機能
このリリースでは、以前の Spy パネルが新しいインスペクターツールに置き換えられています。新しいインスペクターでは常に、特定の要素の検査対象となる正しいデータが表示されます。古い Spy パネルとは対照的に、インスペクターの状態は一時ビューとして表示され、URL には格納されません。Spy パネルを使用して、基になるデータを表示する場合は、ビジュアル化と同じ集計を使用してテーブルを作成することをお勧めします。インスペクターは次のように開くことができます。
- ダッシュボード - パネルのコンテキストメニューを使用してインスペクターを表示します。
- ビジュアルエディター - 画面の最上部にある [Inspector] ボタンを使用します。
みなさん、楽しい毎日を過ごせますように。
Kibanaより
業務コンテンツを整理して安全に保つKibanaの"space"
Kibanaの"space"で業務コンテンツ整理する
バージョン6.5より、新たに"space"機能が加わりました。"space"を使うと、ダッシュボードや可視化、その他の保存済みオブジェクトをカテゴリ別に整理することができます。各spaceは独立しています。1つのspaceに入れたオブジェクトが、他のspaceに出てくることはありません。
はじめて使う
Kibanaには自動で作成される"Default"というspaceがあります。以前のバージョンからアップグレードした場合、保存済みのオブジェクトはここに入っています。
Kibanaで開いているspaceは、いつでも画面左のナビゲーションバー下部に表示されます。ここから直接spaceの管理UIを操作して、新しいspaceを作成することもできます。 [Create space]ボタンをクリックします。
次に作成したspaceに名前をつけ、アバターを好みに応じて調整します。
URL識別子の注意点
このステップで作成する識別子は、Kibanaで使用するURLの一部になります。spaceを作成する際にURLをカスタマイズすることができますが、作成後は変更できません。
開いているspaceを把握する
Kibanaのナビゲーションバーを確認することで、現在開いているspaceをいつでも確認できます。spaceのアバターをクリックするとメニューが表示され、別のspaceに移動することもできます。
spaceを削除する
spaceを削除するには、メニューから[Management] > [Spaces]画面に進みます。次にこの画面で、削除するspaceの横のゴミ箱アイコンをクリックします。
spaceを削除すると、そのspaceに含まれるすべてのオブジェクトが削除され、復元することはできません。この点に注意してください。
space間でオブジェクトを移動する
import/export機能を使用して、spaceに保存したオブジェクトを別のspaceへ移動することができます。詳しい手順は、ブログ記事「spaceを移動する」をご参照ください。
spaceのアクセスを安全に保つ
ゴールドまたはプラチナライセンスでご利用の場合、各spaceのアクセス権を付与するロールを制御することができます。この機能を使うには、画面で[Management] > [Roles]に進みます。
spaceへのアクセス権限を定義する概念は"Minimum Privilege"と呼ばれ、3つのオプションがあります。
Minimum Privilege |
説明 |
all |
ユーザーは、Kibanaのすべてのspaceに読み/書き(read/write)のアクセス権を持ちます。さらにユーザーは、Kibanaのすべてのspaceを作成、編集、削除することができます。将来追加されるspaceに対しても同じ権限を持ちます。 |
read |
ユーザーは、Kibanaのすべてのspaceに読み取りのみ(read-only)のアクセス権を持ちます。将来追加されるspaceに対しても同じ権限を持ちます。 |
none |
ユーザーは、Kibanaのすべてのspaceにアクセス権を持ちません。 |
Minimum Privilegeを設定した後に、特定のspaceに対するアクセス権をカスタマイズすることができます。
spaceを安全に保つ活用事例
例:すべてのspaceに完全なアクセス権を付与する
Minimum Privilegeを"all"に設定することで、すべてのspaceに対するアクセス権が与えられます。この設定では、特定のspaceへのアクセス権をカスタマイズすることはできません。
例:すべてのspaceに読み取りのみのアクセスを設定し、"Marketing"spaceに完全なアクセスを付与する
Minimum Privilegeを"read"に設定すると、すべてのspaceに対して読み取りのみのアクセス権が与えられます。この設定では、必要に応じて特定のspaceに"all"アクセスを設定することができます。一方、特定のspaceに対してアクセスを禁止することはできません。
例:"Executive"spaceだけに読み取りのみのアクセスを付与する
Minimum Privilegeを"none"にすると、すべてのspaceにアクセスできません。この設定では、"Executive"spaceに読み取りのみのアクセスを追加で付与することができます。
すべてのspaceの権限を表示する
あるロールがKibanaのspaceで持つすべてのアクセス権を表示するには、[View summary of spaces privileges]リンクをクリックします。
まとめ
Kibanaに加わったspaceは、パワフルな機能です。ダッシュボードや可視化を、これまでにない方法で整理できるようになりました。設計から新しくなったこのロール管理インターフェースをKibanaで活用することで、アクセスを安全に保つことができます。ブログ記事「Kibanaのspaceを移行する方法」もぜひ合わせてご覧ください。
Kibanaのspaceに移行する
Canvas: データ表要素とデバッグ要素
Canvasは現在、workpadに追加できる約20の組み込みの要素(一覧についてはブログ「KibanaでCanvasを使い始める」を参照)を提供しています。このブログでは、そのうちの2つ、データ表およびデバッグ要素のみをご紹介します。
![]() |
データ表高度に柔軟かつ動的な表です。初期状態のままで、スクロール、ページネーション、カスタムCSSがサポートされます。 |
![]() |
デバッグ背後で動作するJSONデータへのアクセスを提供し、発生する問題をより正確に分析できます。 |
ここでは具体的に、非常に馴染みのあるデータ表である空港のフライト発着表を作成するためにCanvasを使用します。
下記がCanvasで作成する表の完成例です。
要件とレビュー
読者の環境が以下の条件を満たしていることを前提に、ブログ「KibanaでCanvasを使い始める」で説明した概念に基づき作成します。
- ElasticsearchおよびKibanaが稼働中(バージョン6.4以降)
- Canvasをインストール済み(CanvasはKibanaバージョン6.5以降に組み込み)
サンプルデータのインストール
このチュートリアルでは、Elastic提供のサンプルデータセット、具体的にはsample flight dataを使用します。
注:このデータセットはKibanaバージョン6.4以降でのみ使用できます。
Kibanaインスタンスにアクセスします。
- サイドバーでメインの[Kibana]ホームページをクリックします
- 「Add Data to Kibana」セクションの下部にあるリンク「Load a data set and a Kibana dashboard」をクリックします
- [Sample flight data]タイルで[Add]をクリックします
クイックリファレンス
下記の表は、先ほどインストールした、フライトのサンプルデータセットの情報を示しています。太字の下線が引かれている項目は、このアクティビティの後のほうで使用しますが、その他の項目については自由に使ってみてください。
kibana_sample_data_flights | ||
AvgTicketPrice Cancelled Carrier DestCityName DestCountry FlightDelayType FlightTimeMin OriginCityName OriginCountry Dest DestAirportID |
DestLocation DestRegion DestWeather DistanceKilometers DistanceMiles FlightDelay FlightDelayMin FlightNum FlightTimeHour Origin OriginAirportID |
OriginLocation OriginRegion OriginWeather _id _index _score _type dayOfWeek hour_of_day timestamp |
空港の発着表の作成
作成
最初にworkpadを作成し、次にデータ表にデータを追加します。
Canvas Workpadの作成
- Kibanaインスタンスにアクセスします
- サイドバーの[Canvas]タブをクリックします
- [Create workpad]をクリックします
- 新しいworkpadに一意の名前をつけます
データ表要素の作成
- [Add element]ボタンをクリックします
- [Data Table]要素を選択します
- ヒント:初めて要素を作成すると、デモ用データが入った状態で表示されるため、すぐに各種の操作ができます。
- 右の編集パネルで[Data]タブを選択します
- [Change your data source]をクリックします
- [Elasticsearch SQL]を選択します
- SQLクエリエディターに、次のように入力します
SELECT DestCityName AS Destination, timestamp AS Time, Carrier AS Airline, FlightNum AS Flight, FlightDelayType AS Status, Cancelled FROM kibana_sample_data_flights
注:このサンプルデータセットには空港のゲート番号のフィールドが含まれていません。このブログの後のほうで、ランダムに番号を生成し、その列を作成します。
- [Save]をクリックします
- 以下のようなデータ表ができているはずです
コード
これでデータが入力されたデータ表ができましたが、望んでいる形式にはなっていません。背後で動作するコードを表示し、調整する必要があります。
Time列の形式の調整
- データ表が選択されていることを確認します
- 画面の右下隅にある[Expression editor]をクリックします
- 式エディター内に次のコードが見つかるはずです
filters
| essql query="SELECT DestCityName AS Destination, timestamp AS Time, Carrier AS Airline, FlightNum AS Flight, FlightDelayType AS Status, Cancelled FROM kibana_sample_data_flights"
| table
| render
分類 -- このコードには次の4つのメインセクションがあります。
- フィルター: Time Filter要素をこのworkpadに追加すると、このデータ表要素に入力されたデータには最初に時間フィルターが適用されて、フィルターで除外されなかったデータのみが表示されることになります。この行を消去すると、データ表要素はworkpadに追加されるどのフィルター要素にも影響を受けることがなくなり、状況によっては便利です。
- データソース: Elastic SQLデータソースを使用しているため、ここでもSQLクエリの表示と編集ができます。
- 要素: この行は、workpadに表示する要素のタイプを定義します。試してみる場合は、「table」を「shape」に変更し、次に右下隅の[Run]をクリックして、どうなるか見てみましょう。元に戻すことを忘れないようにしてください。
- レンダリング: 要素の表示の見た目をカスタマイズできます。このブログの後のほうで、データ表をよりスタイリッシュにするために、カスタムのCSSをレンダリング関数に追加します。
- 「table」要素の関数で表示する前に、データを修正する必要があります。そのために、「essql」データソース関数と「table」要素の関数の間に、「mapColumn」という新しい関数を追加します。 この関数「mapColumn」は単に任意の列の値を修正するためのものです。ここで修正しようとしている列は「Time」列です。そのため、12列目に次のコードを追加します。
... FROM kibana_sample_data_flights" <b style="background-color:#ffae5b"><i>| mapColumn Time fn={}</i></b> | table ...
- Canvasには、「formatdate」関数など、利用可能な多くの組み込みの関数が用意されています。ここでは時間を「hh:mm A」の形式で表示します。そのために、コードの12行目に以下を追加します。
... FROM kibana_sample_data_flights" | mapColumn Time fn={ <b style="background-color:#ffae5b"><i>formatdate “hh:mm A”</i></b> } | table ...
- 式エディターの右下隅にある[Run]をクリックします
- すると、エラーになりました。デバッグが必要です
デバッグの時間(この手順をスキップしないでください)
後ですぐにコードに戻りますが、まずはCanvasでのデバッグ方法を見てみましょう。
エラーの特定
- 表要素の警告シンボルをクリックします。
- 下図のように、エラーの原因が表示されるはずです。
- 「mapColumn」関数がタイムスタンプデータを「number」にキャストしようとしているようです。これは、使用している「formatdate」関数が、数値形式のタイムスタンプ(UTCミリ秒など)を要求しているからです。
- 実際には、どのようなタイムスタンプ形式になっているのでしょうか。それを解明するために、「debug」要素を追加しましょう。
デバッグ要素の追加
- [Add element]をクリックします
- [Debug]要素を選択します
- 右の編集パネルで[Data]タブを選択します
- [Change your data source]をクリックします
- [Elasticsearch SQL]を選択します
- SQLクエリエディターに、次のように入力します
SELECT DestCityName AS Destination, timestamp AS Time, Carrier AS Airline, FlightNum AS Flight, FlightDelayType AS Status, Cancelled FROM kibana_sample_data_flights
- [Save]をクリックします
- デバッグ要素では、[Time]フィールドのタイプが「date」になっており、最初のエントリーは次のようになっています:2018-11-05T00:00:00.000Z
- ありがたいことに、Canvasには 「date」タイプをUTCミリ秒の数値に変換できる機能が組み込まれています。
- データ表を再度選択し、下記のとおり式エディターでコードに「date」関数を追加します。
| mapColumn Time fn={<b style="background-color:#ffae5b"><i> date | </i></b>formatdate “hh:mm A” }
- [Run]をクリックします
- データ表に適切な形式でタイムスタンプが表示されます。
ヒント:毎回、デバッグ要素を追加する必要はありません。どの要素の式エディターでも、コード
| render as="debug"
を追加して、その要素のJSONを見ることができます。ただし、作業中の参照用として、専用のデバッグ要素を準備しておくと便利です。
では、通常のプログラムに戻りましょう。
コードの続き
次に、「Gate」列を追加しましょう。このデータセットにはゲートのデータが含まれていないため、Canvasに組み込まれている強力な機能を使用してランダムに生成します。
ゲートのデータの追加
- 列を追加する最も簡単な方法は、SQLクエリにエントリーを追加することです。単純に「FlightNum」データを追加し、新しい列の名称を「Gate」にします。
- 右側のエディターパネルで[Data]タブをクリックし、SQLエディターで次の行を追加します。
SELECT DestCityName AS Destination, timestamp AS Time, Carrier AS Airline, FlightNum AS Flight, <b style="background-color:#ffae5b"><i>FlightNum AS Gate,</i></b> FlightDelayType AS Status, Cancelled FROM kibana_sample_data_flights
- [Save]をクリックします
- 式エディターで、1行目の下にもう1つ「mapColumn」関数を追加します。今回は「Gate」列を修正します。
... | mapColumn Time fn={ date | formatedate “hh:mm A” } <b style="background-color:#ffae5b"><i>| mapColumn Gate fn={}</i></b> | table ...
- 実際のゲート番号を取得するには、1~100の間の数字をランダムに生成する必要があります。ありがたいことに、Canvasには活用できる数学関数がいくつか組み込まれています。 ここで「random」関数を使用しますが、この関数では小数点以下の桁数の多い数字をランダムに生成するため、求めているものにはなりません。そこで、2つ目の関数を追加します。ランダムに生成した数字を四捨五入する「round」関数です。コードは次のようになります。
| mapColumn Gate fn={ <b style="background-color:#ffae5b"><i>math "round(random(0,100),0)"</i></b> }
- [Run]をクリックします
- これで、新しい「Gate」列にはすべて、ランダムに生成されたゲート番号が表示されるはずです。
列の結合
値が「true」のときは常に「Cancelled」ステータスが表示されるように、「Cancelled」列を「Status」列と組み合わせる必要があります。
- データ表要素が選択されていること、および式エディターが開いていることを確認します
- 「mapColumn」関数をもう1つ追加する必要がありますが、今回は「Status」列に追加します
... | mapColumn Gate fn={math "round(random(1,100),0)"} <b style="background-color:#ffae5b"><i>| mapColumn Status fn={}</i></b> | table ...
- 次に、「Cancelled」列のフィールド(または「cell」)が「true」になっているかどうかを確認する必要があります
| mapColumn Status fn={<b style="background-color:#ffae5b"><i>if {getCell "Cancelled" | eq true}</i></b>}
- そして、条件がtrueの場合に「Status」列の値が文字列「Cancelled」になるように設定します
| mapColumn Status fn={if {getCell “Cancelled” | eq true}<b style="background-color:#ffae5b"><i> then=”Cancelled” </i></b>}
- ここで[Run]をクリックすると、「Cancelled」を「Status」列にマッピングできます。ただし、true以外の場合については「Status」の値が「null」値になります。
- 最後に必要な作業は、「Status」列の値が「Cancelled」ではない場合に、元の値を維持するようCanvasに指示することです。
| mapColumn Status fn={if {getCell “Cancelled” | eq true} then=”Cancelled”<b style="background-color:#ffae5b"><i> else={getCell "Status"}</i></b>}
- [Run]をクリックします
- これで、「Status」列を「Cancelled」列と組み合わせることができました。
列の削除
これで「Status」列に、必要な情報をすべて含めることができたので、「Cancelled」列を表示する必要がなくなりました(背後では機能している必要があります)。そのため、「Cancelled」列を削除します。
- データ表要素が選択されていること、および式エディターが開いていることを確認します
- Canvasには、列を含めるまたは除外するのに使用する「columns」という関数があります。ここでは「Cancelled」列を除外します。次のとおり、コードを1行追加します。
... | mapColumn Status fn={if {getCell “Cancelled” | eq true} then=”Cancelled” else={getCell “Status”}} <b style="background-color:#ffae5b"><i>| column exclude=”Cancelled”</i></b> | table ...
- [Run]をクリックします
- 背後ではその列のデータを使用していますが、データ表要素に「Cancelled」列が表示されなくなっているはずです。
カスタマイズ
これで望むとおりのデータがすべて揃いました。次は、workpadの表示の見た目をカスタマイズしてみましょう。
背景色の設定
- workpadで色が何も選択されていないことを確認します
- ページ右側の編集パネルで[Page Background]カラーピッカーをクリックし、値を「#0276fd」に設定します
ページネーションの削除
- データ表要素を選択します
- ページの右側の編集パネルで、[Display]タブを選択します
- [Table Style]パネルの[+]ボタンをクリックします
- ドロップダウンから[Pagination]を選択します
- 切り替えボタンをクリックしてオフにします
表の行数の設定
- 再度、[Table Style]パネルの[+]ボタンをクリックします
- ドロップダウンから[Rows per page]を選択します
- ページごとの行数を25に増やします
- 実際には18行にするので、データ表要素の式エディターを開き、「perPage」の値を18に変更します
| table paginate=false perPage=<b style="background-color:#ffae5b"><i>18</i></b>
- [Run]をクリックします
- 18行すべてが表示されるようにデータ表要素を広げます
表テキストのスタイル設定
- 再度、[Table Style]パネルの[+]ボタンをクリックします
- 今回は[Text Settings]を選択します
- テキストを太字に、色をホワイトに設定します
表ヘッダーのスタイル設定
- 表の他の行よりも目立つように、表のヘッダーのスタイルを設定します。すでに[Table Style]パネルでできることは見てきたので、ここではカスタムCSSを使います
- そのためには、[Element Style]パネルの[+]ボタンをクリックします
- ドロップダウンメニューから[CSS]を選択します
- CSSエディターの内容を消去し、次のコードをCSSエディターにペーストします
canvasDataTable__th { text-decoration: underline; background-color: #d0e9ff; color: black;}
- [Apply stylesheet]をクリックします
- これで表のヘッダーには、薄いブルーの背景色とブラックの下線が引かれたテキストが表示されるはずです
表の行のスタイル設定
- 表の行の色が交互になるよう設定するために、ここでもカスタムCSSを使用します
- 次のコードをCSSエディターにペーストします
.canvasDataTable__tbody>:nth-child(even) { background-color: #2a2a50; } .canvasDataTable__tbody>:nth-child(odd) { background-color: #5f67af; }
- [Apply stylesheet]をクリックします
- workpadの幅に合うようにデータ表要素の幅を調整します
- 下図のように、行の色が交互になるように表示されるはずです
タイトルの追加
- [Add element]をクリックします
- [Markdown]要素を選択します
- 画面右側の[Markdown content]エディターにあるすべてのテキストを削除します
- [Markdown content]エディターに「Departures」と入力します
- [Apply]をクリックします
- [Markdown]要素のサイズを調整し、画面の中央に揃えます
- 右側の編集エリアにある [Markdown]パネルで、[+]ボタンをクリックします
- ドロップダウンメニューから[Text Settings]を選択します
- テキストを次のとおりに設定します
- サイズ:48
- フォント:太字
- アラインメント:中央揃え
- 色:ホワイト
- これで、実際に空港で見るものによく似たworkpadが完成したはずです。
完成したコード
下記は、式エディターでのデータ表の完全なコードです。
filters | essql query="SELECT DestCityName AS Destination, timestamp AS Time, Carrier AS Airline, FlightNum AS Flight, FlightNum AS Gate, FlightDelayType AS Status, Cancelled FROM kibana_sample_data_flights " | mapColumn "Time" fn={date | formatdate "hh:mm A"} | mapColumn "Gate" fn={math "round(random(1,100),0)"} | mapColumn "Status" fn={if {getCell "Cancelled" | eq true} then="Cancelled" else={getCell "Status"}} | columns exclude="Cancelled" | table paginate=false perPage=18 font={font family="'Open Sans', Helvetica, Arial, sans-serif" size=14 align="left" color="#FFFFFF" weight="bold" underline=false italic=false} | render css=".canvasDataTable__th { text-decoration: underline; background-color: #d0e9ff; color: black;} .canvasDataTable__tbody>:nth-child(even) { background-color: #2a2a50; } .canvasDataTable__tbody>:nth-child(odd) { background-color: #5f67af; }"
その他の便利なリソースリンク
お疲れ様でした。Canvasのデータ表要素およデバッグ要素を使用する例をいくつか見てきました。ぜひ他の要素もworkpadに追加して、Canvasのフル機能をお試しください。
Canvasのブログ記事はこの他にもあります。ぜひご活用ください。
バナー画像:MPD01605による「Miami Airport Screen」はCC BY 2.0 によって許可を得ています。
検索順位を自在に操る
検索順位を決める要素
Elasticsearchは、スケーラブルで高速なリアルタイムの検索・分析エンジンです。文字列、数値、日時、地理情報など、指定された条件にしたがってインデックスされたドキュメントを検索し、一致するものを利用者に返します。数値、日時、地理情報であれば、「価格が1000円」以上、「今週入荷した商品」、「東京都庁から100km以内」などといった条件をもとに検索ができ、条件に一致するドキュメントのスコアは全て同一です。文字列を条件に指定した場合には、検索対象の文字列の長さや、条件に一致した句の数や頻度などに応じて、それぞれのドキュメントのスコアは異なり、順位付けがなされます。
ただ、検索機能をアプリケーションに実装する場合、検索順位を制御したい場合が多々あります。「1000円以上の商品を安い順」「今週入荷した商品を新しい順」「東京都庁から近い順」に並べるといった場合です。それらのうち、複数の条件を組み合わせたい場合もあるでしょう。Elasticsearchを使用して、どのように実装したら良いでしょうか。
フルーツのオンラインショッピングサイトを想定し、以下のような商品テーブルを用意します。
arrival_date | name | origin.prefecture | origin.location | price | promotion |
---|---|---|---|---|---|
2018-12-02 | Tsugaru Apple | Aomori | 40.82,140.73 | 310 | 2 |
2018-11-29 | Shinano Apple | Nagano | 36.65,138.17 | 280 | 10 |
2018-12-04 | Fuji Apple | Akita | 39.69,139.78 | 150 | 1 |
2018-12-04 | Mikkabi Mandarine Orange | Shizuoka | 34.97,138.38 | 80 | 1 |
POST items/doc/_bulk {"index":{}} {"arrival_date":"2018-12-02","name":"Tsugaru Apple","origin":{"prefecture":"Aomori","location":"40.82,140.73"},"price":310,"promotion":2} {"index":{}} {"arrival_date":"2018-11-29","name":"Shinano Apple","origin":{"prefecture":"Nagano","location":"36.65,138.17"},"price":280,"promotion":10} {"index":{}} {"arrival_date":"2018-12-04","name":"Fuji Apple","origin":{"prefecture":"Akita","location":"39.69,139.78"},"price":150,"promotion":1} {"index":{}} {"arrival_date":"2018-12-04","name":"Mikkabi Mandarine Orange","origin":{"prefecture":"Shizuoka","location":"34.97,138.38"},"price":80,"promotion":1}
商品の「入荷日(arrival_date )」、「商品名(name)」、「生産地(origin.location)」、「価格(price)」、「販促度(promotion)」フィールドを用意しました。利用者は「商品名」のみで検索しますが、「入荷日」が新しいもの、フラッシュセール用の「販促度」が高いものが、より上位に表示されるように試みてみます。
注意:本項に使用しているインデックスやクエリはこちらで入手できます。予期した通り動作させるためには、適切に「入荷日(arrival_date )」などを調整する必要があります。クエリのnow
を2018-12-06
とすることもできます。
好ましくない方法 - スクリプトスコア(Script Score)
まずはじめに思いつくのは、ユーザが検索したキーワードに該当するドキュメントから、「入荷日(arrival_date)」と「販促度(promotion)」を要素としてスクリプトにより点数付けし、各ドキュメントのスコアを上書きするものです。これは、Function Scoreクエリの、Script Scoreを用いて実現できます。
アプリケーションは、Elasticsearchに以下のようなクエリをリクエストすることができます。
GET items/_search { "query": { "function_score": { "score_mode": "sum", "query": { "match": { "name": "apple" } }, "script_score": { "script": "doc['promotion'].value - (new Date().getTime() - doc['arrival_date'].value.toInstant().toEpochMilli()) / 1000000 / 60" } } } }
script_score
で、「入荷日(arrival_date)」から現在の経過日数を求め、「販促度(promotion)」から引いています。「販促度(promotion)」が高く、より新鮮な商品が上位に表示されることになります。
実際に多くElasticsearchユーザーが、このような方法を用いています。では、なぜ好ましくないのでしょうか。それは、Elasticsearchはスクリプトを実行するために、マッチクエリーで一致したドキュメント全ての、「入荷日(arrival_date)」フィールドと、「販促度(promotion)」にアクセスし、それぞれのドキュメントでスクリプトを用いて計算を行い、求められた値にしたがって検索順位を並べ替える必要があるからです。プロファイルAPIを用いて観察してみると、score
に多くの時間(本例では267,863ナノ秒)が割かれていることがわかります。
{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 3, "max_score" : 2.0, "hits" : [ { "_index" : "items", "_type" : "doc", "_id" : "2", "_score" : 2.0, "_source" : { "arrival_date" : "2018-11-29", "name" : "Shinano Apple", "origin" : { "prefecture" : "Nagano", "location" : "36.65,138.17" }, "price" : 280, "promotion" : 10 } }, { "_index" : "items", "_type" : "doc", "_id" : "3", "_score" : 0.0, "_source" : { "arrival_date" : "2018-12-04", "name" : "Fuji Apple", "origin" : { "prefecture" : "Akita", "location" : "39.69,139.78" }, "price" : 150, "promotion" : 1 } }, { "_index" : "items", "_type" : "doc", "_id" : "1", "_score" : -2.0, "_source" : { "arrival_date" : "2018-12-02", "name" : "Tsugaru Apple", "origin" : { "prefecture" : "Aomori", "location" : "40.82,140.73" }, "price" : 310, "promotion" : 2 } } ] }, "profile" : { "shards" : [ { "id" : "[3AvCgesDSpqiSKQR2y_qPA][items][0]", "searches" : [ { "query" : [ { "type" : "FunctionScoreQuery", "description" : "function score (name:apple, functions: [{scriptScript{type=inline, lang='painless', idOrCode='doc['promotion'].value - (new Date().getTime() - doc['arrival_date'].value.toInstant().toEpochMilli()) / 1000000 / 60', options={}, params={}}}])", "time_in_nanos" : 366579, "breakdown" : { "score" : 267863, "build_scorer_count" : 7, "match_count" : 0, "create_weight" : 4120, "next_doc" : 16184, "match" : 0, "create_weight_count" : 1, "next_doc_count" : 6, "score_count" : 3, "build_scorer" : 78395, "advance" : 0, "advance_count" : 0 } } ], "rewrite_time" : 3536, "collector" : [ { "name" : "CancellableCollector", "reason" : "search_cancelled", "time_in_nanos" : 286452, "children" : [ { "name" : "SimpleTopScoreDocCollector", "reason" : "search_top_hits", "time_in_nanos" : 275455 } ] } ] } ], "aggregations" : [ ] } ] } }
減衰(Decay)関数を検討する
Function Scoreクエリでは、指定した値から遠ざかるほどスコアが下がる、Decay Functionを利用できます。指定した原点から遠ざかるほど、検索スコアが下がり、複数の条件を指定したり、減衰度を調整することができます。以下のようなクエリで実現することができます。
GET items/_search { "query": { "function_score": { "score_mode": "sum", "query": { "match": { "name": "apple" } }, "functions": [ { "linear": { "arrival_date": { "origin": "now", "scale": "7d", "offset": "0d" } } }, { "linear": { "promotion": { "origin": "10", "scale": "10", "offset": "0" } } } ] } } }
まず、「商品名(name)」にapple
が含まれるものを検索します。さらに「入荷日(arrival_date)」が現在より遠ざかるほど、直線的(Linear)にスコアが下がります。そして同様に、「販促度(promotion)」が10から遠ざかるに連れて、スコアが下がり、これら3つのスコアを足した(sum)ものをスコアとします。スコア自身はscript_score
とは異なりますので、検索の順位は異なる可能性がありますが、「入荷日」が新しいもの、フラッシュセール用の「販促度」が高いものを上位にするという要件を満たせることがわかります。
さらに、プロファイルAPIを使用すると、より少ないコスト(計算時間)のscore
で(本例では24,824ナノ秒)、レスポンスが得られることが確認できます。
{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 3, "max_score" : 0.58585125, "hits" : [ { "_index" : "items", "_type" : "doc", "_id" : "2", "_score" : 0.58585125, "_source" : { "arrival_date" : "2018-11-29", "name" : "Shinano Apple", "origin" : { "prefecture" : "Nagano", "location" : "36.65,138.17" }, "price" : 280, "promotion" : 10 } }, { "_index" : "items", "_type" : "doc", "_id" : "3", "_score" : 0.5511543, "_source" : { "arrival_date" : "2018-12-04", "name" : "Fuji Apple", "origin" : { "prefecture" : "Akita", "location" : "39.69,139.78" }, "price" : 150, "promotion" : 1 } }, { "_index" : "items", "_type" : "doc", "_id" : "1", "_score" : 0.5164573, "_source" : { "arrival_date" : "2018-12-02", "name" : "Tsugaru Apple", "origin" : { "prefecture" : "Aomori", "location" : "40.82,140.73" }, "price" : 310, "promotion" : 2 } } ] }, "profile" : { "shards" : [ { "id" : "[3AvCgesDSpqiSKQR2y_qPA][items][0]", "searches" : [ { "query" : [ { "type" : "FunctionScoreQuery", "description" : "function score (name:apple, functions: [{org.elasticsearch.index.query.functionscore.DecayFunctionBuilder$NumericFieldDataScoreFunction@84ef9e63}{org.elasticsearch.index.query.functionscore.DecayFunctionBuilder$NumericFieldDataScoreFunction@7ed62d90}])", "time_in_nanos" : 148424, "breakdown" : { "score" : 24824, "build_scorer_count" : 7, "match_count" : 0, "create_weight" : 55157, "next_doc" : 2485, "match" : 0, "create_weight_count" : 1, "next_doc_count" : 6, "score_count" : 3, "build_scorer" : 65941, "advance" : 0, "advance_count" : 0 } } ], "rewrite_time" : 3466, "collector" : [ { "name" : "CancellableCollector", "reason" : "search_cancelled", "time_in_nanos" : 37957, "children" : [ { "name" : "SimpleTopScoreDocCollector", "reason" : "search_top_hits", "time_in_nanos" : 30003 } ] } ] } ], "aggregations" : [ ] } ] } }
減衰(Decay)関数を地理情報に応用する
減衰(Decay)ファンクションが適用可能なのは、数値や日付時刻だけに限らず、地理情報にも適用できます。ある地点から遠ざかるほどスコアが下がるという検索は、タクシーの配車やイベントのチケット販売、デーティングアプリケーションなど、様々なケースで容易に応用できます。フルーツのオンラインショッピングサイトにおいて、生産者直送の地産地消を推進するのであれば、以下のようなクエリで、購入者から地理的に近い商品を勧めることもできます。
GET items/_search { "query": { "function_score": { "score_mode": "sum", "query": { "match": { "name": "apple" } }, "linear": { "origin.location": { "origin": "35.68,139.69", "offset": "0", "scale": "300km" } } } } }
まとめ
検索順位を柔軟に、かつ自在に制御するヒントは得られましたでしょうか。パフォーマンスの観点からは、なるべくスクリプトによるスコア計算は、避けることが望ましいですし、Elasticsearchが提供している減衰(Decay)ファンクションは、より低コストで検索順位を制御できる機会を提供しますので、第一の候補として検討してください。
また、アプリケーション検索に特化した当社のサービスである、Elastic App Searchを用いると、GUIを用いて関連性をチューニングすることができます。条件に応じた検索結果をその場でリアルタイムに確認できたり、アプリケーション開発者の手を煩わせることなく検索順位を制御することができます。この他にも便利なAPIや検索UIのリファレンスなども提供していますので、アプリケーション開発の工数を著しく削減します。ぜひお試しください。
CanvasとElasticsearchを活用した空港セキュリティオペレーション監視
Crimson Macawは英国を拠点とするコンサルティング企業です。本記事は、マンチェスター・エアポート・グループ向けに実施したプロジェクト事例をご紹介します。このプロジェクトはロンドン・スタンステッド空港のセキュリティオペレーション向けにリアルタイムダッシュボードを実装するというものでした。
ダッシュボードの目的は、乗客のフローとセキュリティパフォーマンスを制御室とセキュリティスタッフによりわかりやすく表示し、リアルタイムデータに基づいてすばやく判断を下せるようにすることです。そのため、複数のオンプレミスシステムと外部データソースからデータを投入し、いくつもの巨大なスクリーンに可視化して表示する必要がありました。
データ投入の課題
データのストレージレイヤーにElasticsearchを導入すると決まった後は、どのデータを、どのように投入するか決定する必要があります。この事例では、利用できる情報のソースが多岐にわたっていました。オンプレミスのデータベースシステムとAmazon S3バケットに頻繁に投入されるファイル、さらに外部のAPIデータソースがあります。一例が、英国の鉄道運行企業ナショナル・レールのデータです。このケースでは、STOMP(Streaming Text Oriented Messaging Protocol)インターフェイスを使用してデータをElasticsearchに読み込ませています。
そこには初期の段階で解決しなければならない課題がありました。
- データベースから取得するデータのを毎分1回以上の頻度でポーリングする必要がある
- STOMPから来るデータはgzipで圧縮されている
データベースを毎分1回以上の頻度でポーリングする
1つ目の課題は、既存のlogstash-input-jdbcプラグインに簡単なパッチをあてて解決することができました。パッチを作成するまで、スケジュールはcron形式でしか表現できませんでした。パッチにはrufus-schedulerという名称で知られるJob Schedulerを使用しました。Job Schedulerは供給スケジュールをcronと秒数のどちらでも表現できます。唯一の変更はcronに代わって反復メソッドを使用するための1行のコードです。
これを活用するためのパッチはGitHubで公開されています。
gzipで圧縮されたメッセージを処理する
STOMPインターフェースから来る圧縮済みメッセージを扱うには、データを解凍し、Logstashでデータをフィルターする必要があります。gzipで圧縮されたメッセージからデータ行を読み取る既存のcodecも存在しますが、このケースではgzipを解凍したメッセージが複数行のXMLとなります。これを克服するため、私たち実装チームは独自のプラグイン — logstash-codec-gzip — を作成しました。こちらもGitHubで公開されています。
Canvasを使用してデータを可視化する
チームはElastic Cloud上にあるマネージドのElasticsearchにあるデータをKibanaで可視化してみました。ところが、思うような表示になりません。より細やかなレベルでの制御が必要だろうと考えていた2018年5月、私たちはマンチェスター・エアポート・グループのBI責任者と共にロンドンで開催されたAWSサミットを訪れ、そこでCanvasの存在を知りました。Elasticの担当者によれば、Canvasは私たちが望んでいる表示により適しているという話でした。
「データをKibanaで可視化するのに、ローレベルで必要な制御がないので困っています」
「Canvasのことはご存知ですか?」
「いいえ」
「新しい可視化ツールです。今はテクニカルプレビューの段階ですが、そのケースに最適だと思いますよ」
Canvasのインストールとファーストインプレッション
CanvasはKibanaのプラグインとして利用でき、他のKibanaプラグインと同様にインストールできました。当時テクノロジープレビューだったCanvasはElastic Cloudでは利用できませんでした。私たちはTerraformを使用し、完全にスクリプト化してマンチェスター・エアポート・グループが使用するAWSにKibanaをホストさせました。
Canvasはシンプルな表現言語をもち、各要素の可視化方法を制御することができます。それは、1つのコマンドが次にパイプされ、部分式が括弧内の式で宣言されるような形で実行されるshellプログラミングに似ています。
何日か触ってCanvasに慣れたところで、私たちはスタンステッド空港に到着する列車の時刻を可視化することができました。このダッシュボードは、テキスト部分にマークダウン要素を、フッターのアイコンと画像にImage Repeatを使用して作成しています。
見栄えもよく、個々の要素も制御することができましたが、1つだけまだ足りない点がありました。このユースケースでは、より細かな制御が必要なのです。
- タイムゾーンに基づいてタイムスタンプをフォーマットする(データはUTCで格納されているが、夏時間に合わせる必要があるため)
- 既知のしきい値に基づいてテキストや画像の色を変更する
Canvasを拡張する
Canvas向けにプラグインを記述する方法は、Kibana向けプラグインの場合にかなり似ています。プラグインはNode.jsで記述されており、使用するレジストリに機能や新しい要素を追加して、Canvas UI内で選択することもできます。このケースでは、必要な制御水準を達成するため3つのプラグインを作成しました。
タイムスタンプのフォーマットにタイムゾーンを使用する
最初に作成したプラグインは、内蔵のformatdate
を簡単に拡張したものです。
import moment from 'moment'; import 'moment-timezone/builds/moment-timezone-with-data'; export const formatdatetz = () => ({ name: 'formatdatetz', type: 'string', help:'Output a ms since epoch number as a formatted string according to a given timezone', context: { types: ['number'], }, args: { _: { types: ['string'], help:'MomentJS Format with which to bucket (See https://momentjs.com/docs/#/displaying/)', required: true }, timezone: { types: ['string'], help:'The timezone', required: true, default:'UTC' } }, fn: (context, args) => { if (!args._) return moment.utc(new Date(context)).tz(args.timezone).toISOString(); return moment.utc(new Date(context)).tz(args.timezone).format(args._); }, });
このformatdatetz pluginはCanvasにインストールして使用するもので、GitHubで公開されています。インストールも簡単です:
./bin/kibana-plugin install https://github.com/crimsonmacaw/nodejs-canvas-plugin-formatdatetz/releases/download/v1.0.2/canvas-plugin-formatdatetz-1.0.2.zip
テキストと画像の色を制御する
このケースで、テキストと画像の色を制御にマークダウンを使用することはできませんでした(Canvas内にCSSを提供することはできますが、マークダウン構文はどのスタイルを適用するかについてHTMLクラス属性設定をサポートしていません)。
最もシンプルなアプローチは、HTMLで直接コーディングできて、マークダウン要素にも同様の方法を使用でき、ハンドルバー表現のデータバインディングができるプラグインを作成することでした。SVG画像は直接HTMLに埋め込むことができます。実装チームは同じレベルの制御を適用し、Elasticsearchから取得したデータに基づいて動的に画像を変更することができるようになりました。
可視化を作成する
必要なプラグインを作成すると細かな制御が可能になり、表の中で行ごとに色を変えるといったこともできるようになりました。
複数のデータを1つの画面に可視化できており、あとはプレースホルダにデータを入れるだけですが、何かしっくりこない感じがします。そしてこんなリクエストが届きました。
「もう少しおしゃれにできますか?」
即座にインターネットにインスピレーションを求めに行きました。"かっこいい"、"ダッシュボード"というキーワードでPinterestやDribbbleといったプラットフォームを検索したところ、良さそうなダッシュボードデザインのアプローチが見つかりました。そして方向性を修正した結果…
画面に多数の要素が追加されています。シンプルなHTMLと複雑なSVG画像を組み合わせて作成したプラグインが広い範囲で使われ、要素を動的に生成しています。
フィードバックはポジティブでした。しかし設計に参加しなかったユーザーは、ダッシュボードを見ただけでは各メトリックの意味がわかりませんでした。
「いい感じですね!でもこの棒グラフはどういう意味ですか?」
"空港セキュリティの現場ですばやく判断を下せるようにする"、という原点に立ち帰ってみると、デザインを美しくすることが情報を可視化する最適な方法であるとは限りません。何も説明がなくても、何が表示されているか瞬時にわかるようなダッシュボードにする必要があるのです。
インタラクティブなアプローチ
Canvasには豊かな表現言語が備わっています。私たちは関係者とダッシュボードのライブ編集について複数回のワークショップを開催し、方向性を探りました。こうしてCanvasついて説明する前や、以前使われていたレポートをベースにしたワイヤフレームから大きく前進し、ダッシュボードに最適に情報を表示するにあたり、新しい発想によるアプローチが見つかりました。ダッシュボードの最新バージョンをエンドユーザーに見せ、フィードバックをもらって修正する、という作業を続けました。
最終版のダッシュボード
関係者が力を合わせ、クリエイティブシンキングを実践して完成したダッシュボードが下の画像です。
「Crimson MacawがElasticsearchとCanvasを使って制作したスタンステッド空港のダッシュボードは、以前は別々のシステムに入っていたデータをリアルタイムに、モダンに一括表示してくれます。現場のプロセスは、かつては不可能だと思われた部分で大幅に効率化されました」– マンチェスター・エアポート・グループ、BI & Analytics責任者、スチュアート・ヒューストン
ダッシュボードはプレゼンテーション用にランダムなデータを表示しています。データのランダム化にあたり、Canvas向けのrandomise plugin(ランダム化プラグイン)を作成、使用しています。
セキュリティハブ
このダッシュボードは空港セキュリティを出入りする人の数が基準を満たしているかや、個々のレーンの情報など、空港セキュリティの現在の状況を示します。セキュリティレーンを示す図形は空港セキュリティの実際のレイアウトに基づいており、オペレーションスタッフはこのダッシュボードを見て実際のレーンをすぐに理解することができます。
セキュリティポッド
このダッシュボードはトレンド(傾向)情報を表示します。空港のさまざまなエリアで、事前に予測した搭乗者数と待ち時間に対し、実際にセキュリティエリアに進む人の数をプロットしています。
出発便情報
一般的なFIDS(フライト情報表示画面)によく似ていますが、今後のフライトについて事前予測された搭乗者数に対し、空港セキュリティに入場した人の数を比較した情報を追加表示しています。
到着列車情報
スタンステッド空港を利用する搭乗客の多くが電車を利用しており、電車の遅延は空港セキュリティに流入する人の数にも大きな影響を及ぼします。1本の列車が数百人の搭乗客を乗せており、一時的な運転見合わせや遅延の解消後には複数本の列車が一気に到着することもあります。
このためダッシュボードには通常の列車運行情報に加え、タイムラインで列車の到着時刻を表示しています。Canvasでの表現を手探りしていた最初のダッシュボードに比べると、大きく進歩していることがお分かりいただけると思います。
まとめ
Canvasはデータをリアルタイムに、美しく可視化する優れたツールです。(本稿執筆時点で)Canvasはベータであり、今後もいくつかの機能が追加される予定ですが、中核となる機能性をプラグインで拡張できる点はElasticプロダクトがソフトウェアに対してとるアプローチと共通しています(注:本稿はCanvasの一般公開前に執筆されました)。
多くのBIツールはグラフや表機能に限られ、ゲージやその他の可視化はまだあまりありません。さらにCanvasのダッシュボード作成手順はシンプルです。つまりデータエンジニアやデータ可視化スペシャリストのイマジネーションが及ぶ限り、自由にダッシュボードを作成することが可能です。
ロバート・ブルース | 英国マンチェスターを拠点とするクラウド/データITコンサルティング企業Crimson Macawの創業パートナー、エンジニアリングディレクター。データエンジニアリングとWeb業界で20年以上の経験を持ち、現在はクラウドテクノロジー分野を強みとする。