Kibana

Elasticsearchと連携するオープンソースデータ可視化・探索ツール。豊富な可視化オプション、機械学習による異常検知、地理空間マッピング機能を提供。

監視サーバーデータ可視化Elasticsearchダッシュボードログ分析ELKスタックオープンソース

監視サーバー

Kibana

概要

KibanaはElasticsearchと連携するオープンソースデータ可視化・探索ツールです。豊富な可視化オプション、機械学習による異常検知、地理空間マッピング機能を提供し、ELKスタック(Elasticsearch、Logstash、Kibana)の可視化コンポーネントとして高い採用率を誇ります。リアルタイムデータ分析とインタラクティブなダッシュボード作成により、企業のデータドリブン意思決定を支援します。

詳細

Kibanaは2013年にElastic社によって開発が開始され、現在ではELKスタックの可視化コンポーネントとして高い採用率を維持しています。機械学習統合、高度な分析機能、リアルタイム可視化により、エンタープライズ市場で継続的な成長を遂げており、バージョン8.x系列では更なる機能強化が図られています。

主要な技術的特徴

  • リッチな可視化: 多様なチャートタイプとカスタマイズ可能なダッシュボード
  • 機械学習統合: AI支援による異常検知とデータ分析
  • 地理空間分析: 位置情報データの高度な可視化機能
  • リアルタイム監視: ライブデータストリームの即座な可視化
  • カスタムプラグイン: 拡張可能なアーキテクチャ

用途

  • ログ分析とトラブルシューティング
  • ビジネスメトリクス監視
  • セキュリティ情報・イベント管理(SIEM)
  • IoTデータ可視化
  • E-commerce分析

メリット・デメリット

メリット

  • 豊富な可視化: 多様なチャートとグラフィック表現
  • Elasticsearch連携: シームレスなデータ統合
  • 機械学習機能: 自動化された異常検知
  • ユーザーフレンドリー: 直感的なインターフェース
  • スケーラビリティ: 大規模データセットの処理能力
  • オープンソース: 無料で高機能な可視化ツール

デメリット

  • Elastic依存: ElasticsearchとのペアリングがCRITICAL
  • リソース消費: 大量データ処理時の高いメモリ使用量
  • 学習コスト: 高度な機能の習得に時間が必要
  • ライセンス複雑性: 複数ライセンス体系の理解が必要
  • パフォーマンス: 複雑なクエリでのレスポンス遅延

参考ページ

書き方の例

基本的なKibana設定

# kibana.yml
server.port: 5601
server.host: "0.0.0.0"
server.name: "kibana-server"
server.publicBaseUrl: "http://localhost:5601"

# Elasticsearch設定
elasticsearch.hosts: ["http://elasticsearch:9200"]
elasticsearch.username: "kibana_system"
elasticsearch.password: "your_password"

# セキュリティ設定
xpack.security.enabled: true
xpack.security.encryptionKey: "your_encryption_key_here"
xpack.security.session.idleTimeout: "1h"

# 監視設定
monitoring.ui.enabled: true
monitoring.kibana.collection.enabled: true

# 機械学習設定
xpack.ml.enabled: true

# 地理空間設定
map.includeElasticMapsService: true
map.tilemap.url: "https://tiles.example.com/{z}/{x}/{y}.png"

# アラート設定
xpack.actions.enabled: true
xpack.alerts.enabled: true

# 可視化設定
vis_type_vega.enabled: true
newsfeed.enabled: false

# パフォーマンス設定
server.maxPayloadBytes: 1048576
elasticsearch.requestTimeout: 30000
elasticsearch.pingTimeout: 3000

ダッシュボード作成の例

// Kibana Saved Object (ダッシュボード設定)
{
  "version": "8.x.x",
  "objects": [
    {
      "id": "web-analytics-dashboard",
      "type": "dashboard",
      "attributes": {
        "title": "Web Analytics Dashboard",
        "description": "Website traffic and performance metrics",
        "panelsJSON": "[{\"version\":\"8.x.x\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{},\"panelRefName\":\"panel_1\"}]",
        "timeRestore": true,
        "timeTo": "now",
        "timeFrom": "now-24h",
        "refreshInterval": {
          "pause": false,
          "value": 30000
        },
        "kibanaSavedObjectMeta": {
          "searchSourceJSON": "{\"query\":{\"match_all\":{}},\"filter\":[]}"
        }
      },
      "references": [
        {
          "name": "panel_1",
          "type": "visualization",
          "id": "page-views-visualization"
        }
      ]
    }
  ]
}

可視化設定の例

{
  "id": "page-views-visualization",
  "type": "visualization",
  "attributes": {
    "title": "Page Views Over Time",
    "visState": "{\"title\":\"Page Views Over Time\",\"type\":\"line\",\"params\":{\"grid\":{\"categoryLines\":false,\"valueAxis\":\"ValueAxis-1\"},\"categoryAxe\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"line\",\"mode\":\"normal\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"showCircles\":true}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}}]}",
    "uiStateJSON": "{}",
    "description": "",
    "version": 1,
    "kibanaSavedObjectMeta": {
      "searchSourceJSON": "{\"index\":\"web-logs-*\",\"query\":{\"match_all\":{}},\"filter\":[]}"
    }
  }
}

Index Pattern設定

{
  "id": "web-logs-*",
  "type": "index-pattern",
  "attributes": {
    "title": "web-logs-*",
    "timeFieldName": "@timestamp",
    "fields": "[{\"name\":\"@timestamp\",\"type\":\"date\",\"searchable\":true,\"aggregatable\":true},{\"name\":\"ip\",\"type\":\"ip\",\"searchable\":true,\"aggregatable\":true},{\"name\":\"method\",\"type\":\"string\",\"searchable\":true,\"aggregatable\":true},{\"name\":\"url\",\"type\":\"string\",\"searchable\":true,\"aggregatable\":false},{\"name\":\"status\",\"type\":\"number\",\"searchable\":true,\"aggregatable\":true},{\"name\":\"response_time\",\"type\":\"number\",\"searchable\":true,\"aggregatable\":true},{\"name\":\"user_agent\",\"type\":\"string\",\"searchable\":true,\"aggregatable\":false}]",
    "fieldFormatMap": "{\"response_time\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"milliseconds\",\"outputFormat\":\"humanizePrecise\"}}}",
    "sourceFilters": "[{\"value\":\"user_agent\"}]"
  }
}

Watcher設定(アラート)

{
  "trigger": {
    "schedule": {
      "interval": "5m"
    }
  },
  "input": {
    "search": {
      "request": {
        "search_type": "query_then_fetch",
        "indices": ["web-logs-*"],
        "body": {
          "query": {
            "bool": {
              "filter": [
                {
                  "range": {
                    "@timestamp": {
                      "gte": "now-5m"
                    }
                  }
                },
                {
                  "range": {
                    "status": {
                      "gte": 500
                    }
                  }
                }
              ]
            }
          }
        }
      }
    }
  },
  "condition": {
    "compare": {
      "ctx.payload.hits.total": {
        "gt": 10
      }
    }
  },
  "actions": {
    "send_email": {
      "email": {
        "to": ["[email protected]"],
        "subject": "High Error Rate Alert",
        "body": "Error rate is above threshold. {{ctx.payload.hits.total}} errors in the last 5 minutes."
      }
    }
  }
}

Docker Compose設定

version: '3.8'

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.15.0
    container_name: elasticsearch
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms1g -Xmx1g"
    ports:
      - "9200:9200"
    volumes:
      - elasticsearch_data:/usr/share/elasticsearch/data
    networks:
      - elastic

  kibana:
    image: docker.elastic.co/kibana/kibana:8.15.0
    container_name: kibana
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
      - xpack.security.enabled=false
      - xpack.monitoring.ui.enabled=true
    ports:
      - "5601:5601"
    depends_on:
      - elasticsearch
    networks:
      - elastic
    volumes:
      - ./kibana.yml:/usr/share/kibana/config/kibana.yml

  logstash:
    image: docker.elastic.co/logstash/logstash:8.15.0
    container_name: logstash
    volumes:
      - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
    ports:
      - "5044:5044"
      - "9600:9600"
    environment:
      - "LS_JAVA_OPTS=-Xmx256m -Xms256m"
    depends_on:
      - elasticsearch
    networks:
      - elastic

volumes:
  elasticsearch_data:
    driver: local

networks:
  elastic:
    driver: bridge

カスタムVisualizationプラグイン

// Custom visualization plugin example
import { PluginInitializerContext } from '../../../core/public';
import { KibanaVisualizationPlugin } from './plugin';

export function plugin(initializerContext: PluginInitializerContext) {
  return new KibanaVisualizationPlugin(initializerContext);
}

export class KibanaVisualizationPlugin {
  constructor(initializerContext) {
    this.initializerContext = initializerContext;
  }

  public setup(core, { visualizations, expressions, data }) {
    // カスタム可視化の登録
    visualizations.createBaseVisualization({
      name: 'custom_heatmap',
      title: 'Custom Heatmap',
      icon: 'heatmap',
      description: 'Custom heatmap visualization',
      visConfig: {
        defaults: {
          colorSchema: 'Blues',
          invertColors: false,
          percentageMode: false
        }
      },
      editorConfig: {
        optionsTemplate: '<custom-heatmap-options></custom-heatmap-options>'
      },
      requestHandler: 'courier',
      responseHandler: 'none',
      visualization: CustomHeatmapVisualization
    });

    return {};
  }

  public start(core) {
    return {};
  }

  public stop() {}
}

class CustomHeatmapVisualization {
  constructor(element, vis) {
    this.element = element;
    this.vis = vis;
  }

  async render(visData, status) {
    // カスタム可視化のレンダリングロジック
    const container = d3.select(this.element);
    
    // データ処理
    const data = this.processData(visData);
    
    // ヒートマップ描画
    this.drawHeatmap(container, data);
  }

  processData(visData) {
    // Elasticsearchからのデータを処理
    return visData.tables[0].rows.map(row => ({
      x: row[0],
      y: row[1],
      value: row[2]
    }));
  }

  drawHeatmap(container, data) {
    // D3.jsを使用したヒートマップ描画
    const margin = { top: 50, right: 50, bottom: 50, left: 50 };
    const width = 800 - margin.left - margin.right;
    const height = 600 - margin.top - margin.bottom;

    const svg = container.append('svg')
      .attr('width', width + margin.left + margin.right)
      .attr('height', height + margin.top + margin.bottom);

    const g = svg.append('g')
      .attr('transform', `translate(${margin.left},${margin.top})`);

    // ヒートマップのセル描画
    const colorScale = d3.scaleSequential(d3.interpolateBlues)
      .domain(d3.extent(data, d => d.value));

    g.selectAll('.cell')
      .data(data)
      .enter().append('rect')
      .attr('class', 'cell')
      .attr('x', d => d.x * 20)
      .attr('y', d => d.y * 20)
      .attr('width', 20)
      .attr('height', 20)
      .attr('fill', d => colorScale(d.value));
  }

  destroy() {
    // クリーンアップ処理
  }
}