Kibanaのダッシュボードの埋め込み方法

私のようなフロントエンドエンジニアによく寄せられる要望の一つは、Kibana®などの既存のダッシュボードをJavaScriptウェブアプリケーションに埋め込むことです。ユーザー生成ビューを迅速に展開したり、特定のビューをユーザーにコントロールさせたりしたいため、何度かこの作業を行わなければなりませんでした。素晴らしい開発者コミュニティから定期的に寄せられる質問を見る限り、これは私だけではないようです。

Kibanaのダッシュボードなどのデータ可視化ツールを使用すると、デザインや技術にあまり詳しくないユーザーでも、Elasticsearchのデータやプロトタイプのビューをすばやく簡単に作成できます。つまり、既存のWebアプリケーションにダッシュボードを組み込むことが最も難しい部分であるということです。特に、ユーザーに一貫したスタイルとエクスペリエンスを提供するために、データの表示を制御するカスタムWebコントロールを統合したい場合はなおさらです。

ここでは、コード例を使って、HTML iframeでKibanaのダッシュボードをWebアプリに埋め込む方法を説明します。また、これらのビューでのKibana認証や、JavaScriptを使ってカスタムコントロールを埋め込みビューに連携させる方法についても解説します。

iframeとは何ですか?

この記事で取り上げた例は両方とも、iframeを使用してダッシュボードを埋め込んでいます。<iframe>HTMLタグで示されるiframeを使用すると、現在のドキュメントに別のウェブページを埋め込むことができます。具体的には、ロードされたグローバルフライトダッシュボードをサンプルフライトデータのデータセットからページ内の独自のElastic®導入に含める予定です。

アプリケーションに他のソースを埋め込む際には、ユーザーがアクセスすべき信頼できるデータソースであることを確認することが重要です。適切なコンテンツセキュリティポリシーの利用、sandboxプロパティによる制限の適用、埋め込みコンテンツの操作を制限する権限の設定が必要です。iframeでsandbox属性を指定しない場合は、デフォルトで全ての制限が適用されます。

アプリケーションにサードパーティのコンテンツを組み込む際には、パフォーマンスについても考慮する必要があります。iframeは他のリソースよりも多くの帯域幅を消費する可能性があるため、1つのアプリケーションで多くのiframeを使用すると、アプリケーション全体の速度が低下する可能性があります。アプリケーションに複数のKibanaのダッシュボードを埋め込むことを検討している場合は、含める数をできるだけ制限して、アプリケーションのパフォーマンステストを実施してください。コンポーネントやダッシュボードを追加するのは簡単ですが、開発者としては、ユーザーが求めるすべての派手なコントロールではなく、必要なデータを提供することを確実にする必要があります。ダッシュボードと可視化のどちらを選ぶ際は、消費者と協力して本当に必要なものを特定しましょう。

HTML iframeを使用した基本的な埋め込み

Webアプリケーションにグローバルフライトダッシュボードを追加するためのコードは、こちらの基本的な例で説明されるとおり、Shareオプションを使ってKibanaから簡単に生成されます。

Kibanaの埋め込みコードの図

選択した関連オプションと現在のフィルターを追加したiframeスニペットが生成されますので、HTMLに貼り付けてください。

<iframe src="https://my-deployment:9243/app/dashboards#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?embed=true&_g=(refreshInterval%3A(pause%3A!t%2Cvalue%3A0)%2Ctime%3A(from%3Anow-1y%2Fd%2Cto%3Anow))&show-top-menu=true&show-query-input=true&show-time-filter=true" height="600" width="800"></iframe>

生成されたコードスニペットでは、iframe の幅と高さにピクセル単位の測定値を使用しています。iframeのサイズが内部のコンテンツを適切に反映するようにすることは、これまで課題となっていました。最新のレスポンシブデザインではビューポートのサイズ属性vwとvhを使用して視点に合わせてiframeのサイズを指定するか、複数の異なるデバイスサイズに対応するためにメディアクエリを使用することが推奨されます。

利用可能な設定数が多いため、必要な要素の判断に迷う可能性があります。オプションを利用すると、ダッシュボードの状態やiframe内に表示されるコントロールを設定できます。

生成するURLのタイプは、次の2つのオプションのいずれかになります。

  1. スナップショット:ダッシュボードの現在の状態全体をエンコードしたURL。つまり、ダッシュボードへの変更は埋め込みバージョンには反映されません。
  2. 保存済みオブジェクト:ダッシュボードの保存済みオブジェクトIDを参照するURLを使用します。これにより、URLが生成された後にダッシュボードに加えられた変更は、JavaScriptアプリケーションのユーザーに表示されます。

著者の経験では、これらのダッシュボードは変更される可能性があります。したがって、「保存済みオブジェクト」オプションが、URL生成後に行われたダッシュボードの変更が見えるようにするための埋め込みに最も適しています。

このInclude設定は、組み込みダッシュボードの上部に追加のコントロールを表示します。

Kibanaのダッシュボード要素
  1. トップメニュー:編集や全画面表示などのダッシュボード機能を含む設定。Kibana URLにshow-top-menu=trueを含めることで制御できます。
  2. クエリ:KQLクエリバーでは、ダッシュボードに表示されるデータをフィルタリングできます。これはshow-query-input=trueURLパラメーターで表されます。
  3. 時間フィルター:ダッシュボード内のデータの日付範囲を選択するための日付ピッカー。URL内でshow-time-filter=trueを使用することで有効になります。
  4. フィルターバー:データのフィルタリングを追加する設定を非表示にします。そのためには、hide-filter-bar URLパラメーターをtrueに設定する必要があります。

公開URLを使わずに、ダッシュボードにアクセスするにはログインが必要です。現時点では、操作性は完璧とは言えませんが、ログイン認証情報を持つユーザーはダッシュボードにアクセスできます。

埋め込みダッシュボード(匿名認証なし)

自動ログイン

ダッシュボードが自動的に表示されるようにするには、Kibanaのダッシュボードに認証機能を統合し、ユーザーがJavaScriptアプリケーションとダッシュボードの両方に認証情報を入力する必要がないようにする必要があります。これによりシームレスな体験が得られます。これは2つの方法のいずれかで行うことができます。

  1. 有効にする匿名認証を設定し、認証トークンを抽出できない受信リクエストに対してデフォルトの認証情報と権利セットを付与します(無料プランで利用可能)。

  2. SAMLシングルサインオン(SSO)プロバイダーのサポートを追加し、認証されていないユーザーをSSOポータルにリダイレクトし、認証済みのユーザーをダッシュボードに直接アクセスできるようにします。これはライセンスが必要な機能です。

ここでは匿名オプションについて説明します。まず、匿名認証プロバイダーanonymous1kibana.ymlに追加する必要があります。

xpack.security.authc.providers:
  anonymous.anonymous1:
    order: 0
    credentials:
      username: "my_anonymous_user"
      password: "password"

auth_provider_hintパラメーターを指定して、プロバイダーanonymous1の設定済みの認証情報を埋め込みコンテンツにリンクするために、iframe URLを再生成する必要もあります。

<iframe src="https://my-deployment-9f9945.kb.eu-west-2.aws.cloud.es.io:9243/app/dashboards?auth_provider_hint=anonymous1#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?embed=true&_g=(refreshInterval%3A(pause%3A!f%2Cvalue%3A120000)%2Ctime%3A(from%3Anow-1y%2Cto%3Anow))&show-time-filter=true" height="600" width="800"></iframe>

auth_provider_hint=anonymous1パラメーターを含めないと、ゲストとしてダッシュボードに進むことができません。同様に、Kibanaに正しいユーザー名とパスワードで対応するユーザーロールが登録されていない場合も、認証エラーが発生します。

埋め込み認証の認証情報が無効

この問題を解決するには、kibana.ymlのプロバイダー設定で指定されているパスワードと一致する正しいパスワードでユーザーが登録されていることを確認してください。認証されていないユーザーにもアクセスが許可されるため、このアカウントの権限は必要最低限に制限することをお勧めします。

認証ユーザーを作成するKibana

この時点で、準備は万端だと思うかもしれません。しかし、ダッシュボードに接続しようとすると、奇妙な繰り返し更新イベントが発生するのがわかります。

組み込みダッシュボードのコンテンツポリシーブロック

この問題は、ブラウザがKibanaダッシュボードをブロックしていることが原因です。最新のWebブラウザは、埋め込みコンテンツを制限するために同一オリジンポリシーを適用しています。2つのURLは、プロトコル、ポート、ホストが同じであれば、同一オリジンを共有しているとみなされます。つまり、コンテンツポリシーで許可されていない限り、異なるオリジンから発信されたコンテンツはデフォルトでブロックされます。

Elastic v8.x以降のデフォルトであるセキュリティ機能を有効にして、ブラウザがELKスタック内のKibanaサーバーにセッションクッキーを送信できるようにするには、kibana.yml sameSiteCookiesオプションを設定する必要があります。

xpack.security.sameSiteCookies: "None"

この最後のステップで、JavaScriptアプリケーションにKibanaのダッシュボードが埋め込まれているのがわかります。

Kibanaの基本的な埋め込みダッシュボード

カスタムコントロールを使用する

このダッシュボードではコントロールを使ってデータをフィルタリングしていることにお気づきかもしれません。ユーザーがデータを調査し、興味深い洞察を見つけるために選択肢を絞り込むことが重要です。

場合によっては、ダッシュボード内のコントロールを使うことが適切な判断でない場合もあります。既存のアプリケーション内でデザインの一貫性を保つために、独自のカスタムコントロールを使用したい場合もあるでしょう。あるいは、ダッシュボードを、フィルタリングしたい追加のデータソースや可視化と並べて配置し、一貫性のある体験を構築することもできます。

この高度な例では、日付ピッカーとドロップダウン選択から日付範囲設定をダッシュボードに渡して、ダッシュボードを強制的に更新する方法を示します。

高度な組み込み型Kibanaダッシュボード

カスタムコントロールを使用するには、ダッシュボードのURLの構成を理解する必要があります。次の例を見てみましょう。

https://elastic-deployment-9f9945.kb.eu-west-2.aws.cloud.es.io:9243/app/dashboards?auth_provider_hint=anonymous1#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?embed=true&_g=(filters:!(),refreshInterval:(pause:!f,value:0),time:(from:'${selectedStartDate}',to:'${selectedEndDate}'))&_a=(query:(language:kuery,query:'${carrierQuery}'))&hide-time-filter=true

基本的な例で述べたパラメーターに加え、フィルターの操作も必要です。コミュニティで以前に議論したようにKibanaには2つのフィルターレベルがあります。

  1. グローバル状態は_gパラメーターで表され、個々のKibanaアプリケーション間で移動する状態を示します。その代表的な例が、選択した開始日と終了日を含むピン留めフィルターです。

  2. 状態は現在のダッシュボードのような個別アプリケーションに限定されています。これは_aURLパラメーターで表されます。

任意の日付ピッカーから日付範囲を渡すには、コントロールに新しい日付範囲が適用されたときに、選択した開始日と終了日でURLのiframeを更新する必要があります。最初は、これらの値を過去1年間の相対的な範囲に設定しています。easepickを例にすると、セットアップ時に登録されたselect イベントで新しい日付がキャプチャされ、必要なISO日付形式に変換された後、iframeのsrc属性が新しいURLで更新されます。

let selectedStartDate = 'now-1y';
let selectedEndDate = 'now';

const picker = new easepick.create({
    element: '#datepicker',
    css: [
        'https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css'
    ],
    zIndex: 10,
    firstDay: 0,
    autoApply: false,
    format: 'MMM DD, YYYY @ HH:MM:00',
    plugins: [
        'RangePlugin',
        'TimePlugin'
    ],
    setup(picker) {
        picker.on('select', (e) => {
            const dateFormat = 'YYYY-MM-DDTHH:MM:00.000Z';
            selectedStartDate =  picker.getStartDate().format(dateFormat);
            selectedEndDate =  picker.getEndDate().format(dateFormat);
            
            dashboardUri=getDashboardUri();
            iframe.setAttribute('src', dashboardUri);
        });
     }
});

URL自体に関しては、_ggetDashboardUri()ヘルパーメソッドで示されているように、選択された範囲で更新されます。

function getDashboardUri() {
    return `https://my-deployment-9f9945.kb.eu-west-2.aws.cloud.es.io:9243/app/dashboards?auth_provider_hint=anonymous1#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?embed=true&_g=(filters:!(),refreshInterval:(pause:!f,value:0),time:(from:'${selectedStartDate}',to:'${selectedEndDate}'))&hide-time-filter=true`;
}

ドロップダウンなどのコントロールでフィルタリングしたいデータフィールドについては、queryオプション(_aパラメーター)を使用してそれらの値を渡す必要があります。次のHTMLselectコントロールを例にとってみましょう。

<div class="carrier-select-container">
  <label for="carrier-select">Carrier</label>
  <select name="carrier-select" id="carrier-select" onchange="updateWithCarrier()">
    <option value="ES-Air">ES-Air</option>
    <option value="JetBeats">JetBeats</option>
    <option value="Kibana Airlines">Kibana Airlines</option>
    <option value="Logstash Airways">Logstash Airways</option>
  </select>
</div>

onchangeイベントに接続されているupdateWithCarrier ()メソッドから変更されたときに、選択した値を抽出することができます。イベントはイベントハンドラーのselectコントロールから引き出されます。

function updateWithCarrier() {
    const carrierSelect = document.getElementById('carrier-select');
    selectedCarrier = carrierSelect.value || '';

    dashboardUri=getDashboardUri();
    iframe.setAttribute('src', dashboardUri);
}

まだgetDashboardUri()ヘルパーを使用しています。アプリケーションフィルターのqueryオプションを介してダッシュボードのURLに渡すKQLクエリを生成するために、ヘルパーを更新する必要があります。

function getDashboardUri() {
  const carrierQuery = rison.encode_object({Carrier : encodeURIComponent(selectedCarrier)});
  return `https://my-deployment-9f9945.kb.eu-west-2.aws.cloud.es.io:9243/app/dashboards?auth_provider_hint=anonymous1#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?embed=true&_g=(filters:!(),refreshInterval:(pause:!f,value:0),time:(from:'${selectedStartDate}',to:'${selectedEndDate}'))&_a=(query:(language:kuery,query:'${carrierQuery}'))&hide-time-filter=true`;
}

KibanaはRisonとURIエンコードを利用しているため、含める前にクエリに適用する必要があります。これは上記のcarrierQueryの定義に記載されており、rison.jsを使用し、通常のencodeURIComponentメソッドで選択した値をエスケープしています。

接続が完了すると、ダッシュボードは新しい選択に応じて毎回更新されます。フォーラムで報告されたこのようなエラーにご注意ください。これらのエラーはデバッグが難しい場合があります。

URLは常に変更される可能性があるため、埋め込むサードパーティツールの新しいバージョンで機能が損なわれるリスクがあることにご注意ください。Kibanaの各リリースにおける互換性のない変更点を必ず確認し、アプリケーションの回帰テストを慎重に実施してください。

Kibanaダッシュボードをさらに活用

ここでは、埋め込みKibanaのダッシュボードの世界に飛び込みました。単一のHTML iframeを使用する簡単な例独自のJavaScriptコンポーネントを使用してダッシュボードにパラメーターを渡す複雑な例について説明しました。すべてのコードはこのGitHubリポジトリにあり、お気に入りのWebテクノロジー、JavaScriptフレームワーク、またはTypeScriptで使用するように簡単に調整できます。

ダッシュボードの埋め込みに関してご質問や問題点がありましたら、ぜひコミュニティフォーラムにご投稿ください。喜んでお手伝いさせていただきます。快適なダッシュボード作成をお楽しみください!