Elasticsearch

Elasticsearch 総合技術ガイド

SREエンジニアのための包括的リファレンス


第1章: Elasticsearch とは

1.1 概要

Elasticsearch は、Apache Lucene をベースに構築された分散型の検索・分析エンジンである。RESTful API を通じて、大規模なデータセットに対するリアルタイムの全文検索、構造化検索、分析を提供する。

Elastic Stack(旧称 ELK Stack)の中核コンポーネントとして、Kibana(可視化)、Logstash(データ収集・変換)、Beats/Elastic Agent(軽量データシッパー)と連携して動作する。

1.2 主な特徴

  • 分散アーキテクチャ: 水平スケーリングが可能で、ペタバイト規模のデータを処理
  • リアルタイム検索: ドキュメントのインデックス後、ほぼリアルタイムで検索可能(デフォルト1秒のrefresh interval)
  • スキーマレス: JSON ドキュメントをそのまま格納可能(動的マッピング)
  • RESTful API: HTTP 経由で全機能にアクセス可能
  • 高可用性: レプリカシャードによるデータ冗長化とフェイルオーバー
  • 多言語対応: 日本語を含む多言語のアナライザーを内蔵

1.3 Elastic Stack の全体像

+------------------------------------------------------------------+
|                        Elastic Stack                              |
|                                                                   |
|  +------------+    +------------+    +-----------+    +--------+  |
|  |   Kibana   |    | Logstash   |    |   Beats   |    | Elastic|  |
|  | (可視化/UI)|    |(ETLパイプ  |    |(軽量データ|    | Agent  |  |
|  |            |    | ライン)    |    | シッパー) |    |        |  |
|  +------+-----+    +------+-----+    +-----+-----+    +---+----+  |
|         |                |                 |               |      |
|         v                v                 v               v      |
|  +----------------------------------------------------------+    |
|  |                   Elasticsearch                           |    |
|  |              (検索・分析エンジン)                          |    |
|  +----------------------------------------------------------+    |
+------------------------------------------------------------------+

1.4 Apache Lucene との関係

Elasticsearch は内部的に Apache Lucene ライブラリを使用している。Lucene は Java で書かれた高性能な全文検索エンジンライブラリであり、転置インデックス(Inverted Index)を使用してテキスト検索を実現する。

Elasticsearch が Lucene の上に追加する主な機能:

機能LuceneElasticsearch
分散処理なしクラスタ管理、シャーディング
REST APIなし完全な RESTful API
リアルタイムなしニアリアルタイム検索
レプリケーションなし自動レプリカ管理
クラスタ管理なしノード検出、障害検知
集約(Aggregation)限定的豊富な集約フレームワーク

1.5 ユースケース

  1. ログ分析・モニタリング: アプリケーション/インフラログの集約と分析
  2. 全文検索: Web サイト検索、ドキュメント検索
  3. セキュリティ分析(SIEM): セキュリティイベントのリアルタイム分析
  4. APM(Application Performance Monitoring): アプリケーションパフォーマンスの監視
  5. ビジネス分析: 売上データ、ユーザー行動分析
  6. 地理空間データ検索: 位置情報ベースの検索・分析

第2章: アーキテクチャ

2.1 クラスタ構成の全体像

+------------------------------------------------------------------+
|                    Elasticsearch Cluster                          |
|                    (cluster.name: production)                     |
|                                                                   |
|  +------------------+  +------------------+  +-----------------+  |
|  | Master Node (1)  |  | Master Node (2)  |  | Master Node (3) |  |
|  | [Elected Master] |  | [Master-eligible]|  | [Master-eligible]|  |
|  | クラスタ状態管理  |  | フェイルオーバー  |  | フェイルオーバー |  |
|  +------------------+  +------------------+  +-----------------+  |
|                                                                   |
|  +------------------+  +------------------+  +-----------------+  |
|  | Data Node (1)    |  | Data Node (2)    |  | Data Node (3)   |  |
|  | [Hot]             |  | [Hot]            |  | [Warm]          |  |
|  | P0  P1  R2       |  | P2  R0  R1       |  | P3  R3          |  |
|  | 高速SSD           |  | 高速SSD          |  | HDD             |  |
|  +------------------+  +------------------+  +-----------------+  |
|                                                                   |
|  +------------------+  +------------------+                       |
|  | Ingest Node      |  | Coordinating     |                      |
|  | パイプライン処理   |  | Node             |                     |
|  | データ変換        |  | リクエストルーティ|                      |
|  |                   |  | ング・集約        |                     |
|  +------------------+  +------------------+                       |
+------------------------------------------------------------------+
    P = Primary Shard (プライマリシャード)
    R = Replica Shard (レプリカシャード)

2.2 ノードの役割

Elasticsearch クラスタ内の各ノードには、1つ以上の役割を割り当てることができる。

2.2.1 マスターノード (master)

クラスタの状態管理を担当する。選出されたマスターノードは以下を管理する:

  • インデックスの作成・削除
  • シャードの割り当て
  • ノードの追加・削除の追跡
  • クラスタ設定の管理
# elasticsearch.yml - マスター専用ノード
node.roles: [ master ]
node.name: master-node-1
cluster.name: production

# マスターノードは軽量なリソースで十分
# CPU: 2-4コア, RAM: 4-8GB, Disk: 低速でも可

2.2.2 データノード (data)

実際のデータを保持し、CRUD 操作、検索、集約を実行する。

# elasticsearch.yml - データノード(Hot tier)
node.roles: [ data_hot ]
node.name: data-hot-node-1
cluster.name: production

# Hot ノード: 高速SSD、十分なRAM
# CPU: 8-16コア, RAM: 32-64GB, Disk: SSD 1-4TB
# elasticsearch.yml - データノード(Warm tier)
node.roles: [ data_warm ]
node.name: data-warm-node-1
cluster.name: production

# Warm ノード: HDD可、中程度のRAM
# CPU: 4-8コア, RAM: 16-32GB, Disk: HDD 4-8TB
# elasticsearch.yml - データノード(Cold tier)
node.roles: [ data_cold ]
node.name: data-cold-node-1
cluster.name: production

# Cold ノード: 最低限のリソース、大容量ストレージ
# CPU: 2-4コア, RAM: 8-16GB, Disk: HDD 8-16TB

2.2.3 インジェストノード (ingest)

ドキュメントのインデックス前にデータを変換・加工するパイプライン処理を実行する。

# elasticsearch.yml - インジェスト専用ノード
node.roles: [ ingest ]
node.name: ingest-node-1
cluster.name: production

2.2.4 コーディネーティングノード (coordinating)

クライアントからのリクエストをルーティングし、検索結果の集約を行う。全ノードはデフォルトでコーディネーティングの役割を持つが、専用ノードを設定することもできる。

# elasticsearch.yml - コーディネーティング専用ノード
node.roles: [ ]
node.name: coordinating-node-1
cluster.name: production

2.2.5 ML ノード (ml)

機械学習ジョブを実行する専用ノード。

# elasticsearch.yml - MLノード
node.roles: [ ml, remote_cluster_client ]
node.name: ml-node-1
cluster.name: production

2.3 インデックス、シャード、レプリカ

2.3.1 インデックスの構造

+------------------------------------------------------------------+
|                      Index: logs-2026.04                          |
|                                                                   |
|  Primary Shards              Replica Shards                       |
|  +--------+--------+        +--------+--------+                  |
|  | Shard 0| Shard 1|        |Replica0|Replica1|                  |
|  | (Node1)| (Node2)|        | (Node2)| (Node1)|                  |
|  +--------+--------+        +--------+--------+                  |
|  | Shard 2|                 |Replica2|                            |
|  | (Node1)|                 | (Node2)|                            |
|  +--------+                 +--------+                            |
|                                                                   |
|  各シャード = 1つの Lucene インデックス                            |
|  各 Lucene インデックス = 複数のセグメント                         |
+------------------------------------------------------------------+

2.3.2 転置インデックス(Inverted Index)

Elasticsearch が高速な全文検索を実現する核心技術:

ドキュメント:
  Doc1: "Elasticsearch は分散検索エンジンです"
  Doc2: "Elasticsearch は Lucene ベースです"
  Doc3: "分散システムは高可用性を提供します"

転置インデックス:
  +------------------+------------------+
  | Term             | Document IDs     |
  +------------------+------------------+
  | elasticsearch    | Doc1, Doc2       |
  | 分散             | Doc1, Doc3       |
  | 検索             | Doc1             |
  | エンジン         | Doc1             |
  | lucene           | Doc2             |
  | ベース           | Doc2             |
  | システム         | Doc3             |
  | 高可用性         | Doc3             |
  | 提供             | Doc3             |
  +------------------+------------------+

2.3.3 セグメント

各シャードは内部的に複数のセグメントで構成される。セグメントは不変(immutable)であり、新しいドキュメントは新しいセグメントとして書き込まれる。

Shard 0:
  +-------------------------------------------+
  |  Segment 0 (immutable, merged)            |
  |  - 10,000 documents                       |
  |  - Inverted Index                         |
  |  - Stored Fields                          |
  |  - Doc Values                             |
  +-------------------------------------------+
  |  Segment 1 (immutable)                    |
  |  - 5,000 documents                        |
  +-------------------------------------------+
  |  Segment 2 (新規、まだマージされていない)   |
  |  - 500 documents                           |
  +-------------------------------------------+
  |  Transaction Log (translog)                |
  |  - まだセグメントに書き込まれていない変更    |
  +-------------------------------------------+

2.4 クラスタ状態の管理

マスターノードが管理するクラスタ状態には以下が含まれる:

  • ノードの一覧と各ノードの役割
  • インデックスとそのマッピング/設定
  • シャードの配置情報
  • クラスタレベルの設定
# クラスタの状態を確認
curl -X GET "localhost:9200/_cluster/state?pretty"

# クラスタのヘルスチェック
curl -X GET "localhost:9200/_cluster/health?pretty"

# 応答例:
# {
#   "cluster_name": "production",
#   "status": "green",          # green/yellow/red
#   "number_of_nodes": 6,
#   "number_of_data_nodes": 3,
#   "active_primary_shards": 50,
#   "active_shards": 100,
#   "relocating_shards": 0,
#   "initializing_shards": 0,
#   "unassigned_shards": 0,
#   "delayed_unassigned_shards": 0
# }

2.5 ドキュメントのルーティング

ドキュメントがどのシャードに格納されるかは、以下の式で決定される:

shard_number = hash(routing_value) % number_of_primary_shards

デフォルトでは routing_value はドキュメントの _id フィールドである。

# カスタムルーティングを指定してインデックス
curl -X PUT "localhost:9200/my-index/_doc/1?routing=user123" -H 'Content-Type: application/json' -d'
{
  "user": "user123",
  "message": "Custom routing example"
}'

第3章: インデックス管理

3.1 インデックスの作成

# 基本的なインデックス作成
curl -X PUT "localhost:9200/my-index" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1,
    "refresh_interval": "5s",
    "analysis": {
      "analyzer": {
        "custom_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "stop", "snowball"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "custom_analyzer",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "timestamp": {
        "type": "date",
        "format": "strict_date_optional_time||epoch_millis"
      },
      "status_code": {
        "type": "integer"
      },
      "response_time": {
        "type": "float"
      },
      "tags": {
        "type": "keyword"
      },
      "geo_location": {
        "type": "geo_point"
      },
      "metadata": {
        "type": "object",
        "dynamic": true
      }
    }
  }
}'

3.2 マッピングとフィールドタイプ

3.2.1 主要なフィールドタイプ

タイプ説明用途
text全文検索用、アナライズされるログメッセージ、説明文
keyword完全一致検索用、アナライズされないステータス、タグ、ID
long / integer / short / byte整数型カウント、ステータスコード
double / float / half_float浮動小数点型レスポンスタイム、スコア
date日時型タイムスタンプ
boolean真偽値フラグ
ipIPアドレスネットワーク分析
geo_point緯度経度地理空間検索
nestedネストされたオブジェクト独立した検索が必要な配列
objectJSONオブジェクト構造化データ
flattenedフラット化されたオブジェクト動的なキーを持つデータ

3.2.2 マッピングの例(ログ用インデックス)

curl -X PUT "localhost:9200/application-logs" -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "@timestamp": {
        "type": "date"
      },
      "log_level": {
        "type": "keyword"
      },
      "service_name": {
        "type": "keyword"
      },
      "host": {
        "properties": {
          "name": { "type": "keyword" },
          "ip": { "type": "ip" }
        }
      },
      "message": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 1024
          }
        }
      },
      "trace_id": {
        "type": "keyword"
      },
      "span_id": {
        "type": "keyword"
      },
      "duration_ms": {
        "type": "float"
      },
      "error": {
        "properties": {
          "type": { "type": "keyword" },
          "message": { "type": "text" },
          "stack_trace": {
            "type": "text",
            "index": false
          }
        }
      },
      "labels": {
        "type": "flattened"
      }
    }
  }
}'

3.3 動的マッピング

動的マッピングでは、Elasticsearch が未知のフィールドを自動的にマッピングする。

# 動的マッピングのテンプレートを設定
curl -X PUT "localhost:9200/dynamic-example" -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_keywords": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "keyword",
            "ignore_above": 512
          }
        }
      },
      {
        "longs_as_integers": {
          "match_mapping_type": "long",
          "mapping": {
            "type": "integer"
          }
        }
      },
      {
        "unindexed_fields": {
          "match": "debug_*",
          "mapping": {
            "type": "text",
            "index": false
          }
        }
      }
    ]
  }
}'

3.4 インデックステンプレート

3.4.1 コンポーネントテンプレート

# 共通設定のコンポーネントテンプレート
curl -X PUT "localhost:9200/_component_template/common-settings" -H 'Content-Type: application/json' -d'
{
  "template": {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "refresh_interval": "5s",
      "index.lifecycle.name": "logs-policy",
      "index.lifecycle.rollover_alias": "logs"
    }
  }
}'

# 共通マッピングのコンポーネントテンプレート
curl -X PUT "localhost:9200/_component_template/common-mappings" -H 'Content-Type: application/json' -d'
{
  "template": {
    "mappings": {
      "properties": {
        "@timestamp": { "type": "date" },
        "host.name": { "type": "keyword" },
        "host.ip": { "type": "ip" },
        "agent.type": { "type": "keyword" }
      }
    }
  }
}'

3.4.2 インデックステンプレート

# インデックステンプレートの作成
curl -X PUT "localhost:9200/_index_template/logs-template" -H 'Content-Type: application/json' -d'
{
  "index_patterns": ["logs-*"],
  "priority": 200,
  "composed_of": ["common-settings", "common-mappings"],
  "template": {
    "settings": {
      "number_of_shards": 5
    },
    "mappings": {
      "properties": {
        "log_level": { "type": "keyword" },
        "message": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 1024
            }
          }
        }
      }
    },
    "aliases": {
      "logs-current": {}
    }
  },
  "data_stream": {}
}'

3.5 インデックスライフサイクル管理(ILM)

ILM を使用すると、インデックスのライフサイクルを自動的に管理できる。

# ILM ポリシーの作成
curl -X PUT "localhost:9200/_ilm/policy/logs-policy" -H 'Content-Type: application/json' -d'
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_primary_shard_size": "50gb",
            "max_age": "1d",
            "max_docs": 100000000
          },
          "set_priority": {
            "priority": 100
          },
          "forcemerge": {
            "max_num_segments": 1
          }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "shrink": {
            "number_of_shards": 1
          },
          "forcemerge": {
            "max_num_segments": 1
          },
          "allocate": {
            "require": {
              "data": "warm"
            }
          },
          "set_priority": {
            "priority": 50
          }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "allocate": {
            "require": {
              "data": "cold"
            }
          },
          "set_priority": {
            "priority": 0
          },
          "freeze": {}
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}'

3.5.1 ILM ライフサイクルの図解

  Hot Phase          Warm Phase         Cold Phase        Delete Phase
  (0-7日)            (7-30日)           (30-90日)         (90日以降)
+---------------+  +---------------+  +---------------+  +----------+
| 高速SSD       |  | 中速ストレージ |  | 低速HDD       |  | データ   |
| 書き込み可能  |->| 読み取り専用  |->| 凍結済み      |->| 削除     |
| 高プライオリティ| | シュリンク    |  | 低プライオリティ| |          |
| ロールオーバー |  | フォースマージ|  |                |  |          |
+---------------+  +---------------+  +---------------+  +----------+

3.6 エイリアス

# エイリアスの作成
curl -X POST "localhost:9200/_aliases" -H 'Content-Type: application/json' -d'
{
  "actions": [
    {
      "add": {
        "index": "logs-2026.04.08",
        "alias": "logs-current"
      }
    },
    {
      "add": {
        "index": "logs-2026.04.*",
        "alias": "logs-this-month"
      }
    },
    {
      "add": {
        "index": "logs-*",
        "alias": "logs-all",
        "filter": {
          "range": {
            "@timestamp": {
              "gte": "now-30d"
            }
          }
        }
      }
    }
  ]
}'

# 書き込みエイリアス(ロールオーバー用)
curl -X PUT "localhost:9200/logs-000001" -H 'Content-Type: application/json' -d'
{
  "aliases": {
    "logs-write": {
      "is_write_index": true
    }
  }
}'

# ロールオーバーの実行
curl -X POST "localhost:9200/logs-write/_rollover" -H 'Content-Type: application/json' -d'
{
  "conditions": {
    "max_age": "1d",
    "max_primary_shard_size": "50gb",
    "max_docs": 100000000
  }
}'

3.7 データストリーム

データストリームは、時系列データ(ログ、メトリクスなど)のための簡素化されたインデックス管理を提供する。

# データストリーム用のインデックステンプレートを作成
curl -X PUT "localhost:9200/_index_template/metrics-template" -H 'Content-Type: application/json' -d'
{
  "index_patterns": ["metrics-*"],
  "data_stream": {},
  "priority": 200,
  "template": {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "index.lifecycle.name": "metrics-policy"
    },
    "mappings": {
      "properties": {
        "@timestamp": { "type": "date" },
        "metric_name": { "type": "keyword" },
        "metric_value": { "type": "double" },
        "host": { "type": "keyword" },
        "tags": { "type": "keyword" }
      }
    }
  }
}'

# データストリームへのドキュメント追加
curl -X POST "localhost:9200/metrics-app/_doc" -H 'Content-Type: application/json' -d'
{
  "@timestamp": "2026-04-08T10:00:00Z",
  "metric_name": "cpu_usage",
  "metric_value": 75.5,
  "host": "web-server-01",
  "tags": ["production", "web"]
}'

第4章: データインジェスト

4.1 単一ドキュメントのインデックス

# ドキュメントIDを指定してインデックス
curl -X PUT "localhost:9200/my-index/_doc/1" -H 'Content-Type: application/json' -d'
{
  "@timestamp": "2026-04-08T10:30:00Z",
  "service": "payment-service",
  "log_level": "ERROR",
  "message": "Payment processing failed: timeout exceeded",
  "trace_id": "abc123def456",
  "duration_ms": 30500
}'

# ドキュメントIDを自動生成してインデックス
curl -X POST "localhost:9200/my-index/_doc" -H 'Content-Type: application/json' -d'
{
  "@timestamp": "2026-04-08T10:31:00Z",
  "service": "payment-service",
  "log_level": "INFO",
  "message": "Payment processed successfully",
  "trace_id": "xyz789ghi012",
  "duration_ms": 250
}'

4.2 Bulk API

大量のドキュメントを効率的にインデックスするための API。

# Bulk API の使用
curl -X POST "localhost:9200/_bulk" -H 'Content-Type: application/x-ndjson' -d'
{"index": {"_index": "logs-2026.04.08", "_id": "1"}}
{"@timestamp": "2026-04-08T10:00:00Z", "service": "api-gateway", "log_level": "INFO", "message": "Request received", "status_code": 200}
{"index": {"_index": "logs-2026.04.08", "_id": "2"}}
{"@timestamp": "2026-04-08T10:00:01Z", "service": "api-gateway", "log_level": "WARN", "message": "Slow response detected", "status_code": 200, "duration_ms": 5000}
{"index": {"_index": "logs-2026.04.08", "_id": "3"}}
{"@timestamp": "2026-04-08T10:00:02Z", "service": "auth-service", "log_level": "ERROR", "message": "Authentication failed", "status_code": 401}
{"create": {"_index": "logs-2026.04.08", "_id": "4"}}
{"@timestamp": "2026-04-08T10:00:03Z", "service": "payment-service", "log_level": "INFO", "message": "Payment initiated", "status_code": 200}
{"update": {"_index": "logs-2026.04.08", "_id": "1"}}
{"doc": {"processed": true}}
{"delete": {"_index": "logs-2026.04.08", "_id": "999"}}
'

# Bulk API のベストプラクティス:
# - バッチサイズ: 5-15 MB が最適(ドキュメント数ではなくサイズで制御)
# - 並列度: データノード数 × 1-2 のスレッド数
# - エラーハンドリング: レスポンスの errors フラグを必ず確認

4.3 インジェストパイプライン

インジェストパイプラインは、ドキュメントがインデックスされる前にデータを変換する。

# インジェストパイプラインの作成
curl -X PUT "localhost:9200/_ingest/pipeline/logs-pipeline" -H 'Content-Type: application/json' -d'
{
  "description": "ログデータの前処理パイプライン",
  "processors": [
    {
      "date": {
        "field": "timestamp_string",
        "target_field": "@timestamp",
        "formats": ["ISO8601", "yyyy-MM-dd HH:mm:ss"],
        "timezone": "Asia/Tokyo"
      }
    },
    {
      "grok": {
        "field": "raw_message",
        "patterns": [
          "%{IP:client_ip} %{WORD:method} %{URIPATH:path} %{NUMBER:status_code:int} %{NUMBER:response_time:float}"
        ]
      }
    },
    {
      "geoip": {
        "field": "client_ip",
        "target_field": "geo"
      }
    },
    {
      "user_agent": {
        "field": "user_agent_string",
        "target_field": "user_agent"
      }
    },
    {
      "set": {
        "field": "ingest_timestamp",
        "value": "{{_ingest.timestamp}}"
      }
    },
    {
      "lowercase": {
        "field": "log_level"
      }
    },
    {
      "remove": {
        "field": ["raw_message", "timestamp_string", "user_agent_string"],
        "ignore_missing": true
      }
    },
    {
      "script": {
        "lang": "painless",
        "source": "if (ctx.status_code != null) { if (ctx.status_code >= 500) { ctx.severity = 'critical'; } else if (ctx.status_code >= 400) { ctx.severity = 'warning'; } else { ctx.severity = 'info'; } }"
      }
    }
  ],
  "on_failure": [
    {
      "set": {
        "field": "_index",
        "value": "failed-logs"
      }
    },
    {
      "set": {
        "field": "error.message",
        "value": "{{ _ingest.on_failure_message }}"
      }
    },
    {
      "set": {
        "field": "error.processor",
        "value": "{{ _ingest.on_failure_processor_type }}"
      }
    }
  ]
}'

# パイプラインを使用してドキュメントをインデックス
curl -X POST "localhost:9200/processed-logs/_doc?pipeline=logs-pipeline" -H 'Content-Type: application/json' -d'
{
  "timestamp_string": "2026-04-08T10:30:00+09:00",
  "raw_message": "192.168.1.100 GET /api/users 200 0.125",
  "user_agent_string": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
  "log_level": "INFO"
}'

# パイプラインのテスト(シミュレーション)
curl -X POST "localhost:9200/_ingest/pipeline/logs-pipeline/_simulate" -H 'Content-Type: application/json' -d'
{
  "docs": [
    {
      "_source": {
        "timestamp_string": "2026-04-08T10:30:00+09:00",
        "raw_message": "10.0.0.1 POST /api/orders 500 2.500",
        "user_agent_string": "curl/7.79.1",
        "log_level": "ERROR"
      }
    }
  ]
}'

4.4 Logstash との連携

4.4.1 Logstash 設定ファイルの例

# /etc/logstash/conf.d/application-logs.conf

input {
  # Filebeat からの入力
  beats {
    port => 5044
    ssl => true
    ssl_certificate => "/etc/logstash/certs/logstash.crt"
    ssl_key => "/etc/logstash/certs/logstash.key"
  }

  # Kafka からの入力
  kafka {
    bootstrap_servers => "kafka-01:9092,kafka-02:9092,kafka-03:9092"
    topics => ["application-logs"]
    group_id => "logstash-consumers"
    codec => json
    consumer_threads => 3
  }
}

filter {
  # JSON パース
  if [message] =~ /^\{/ {
    json {
      source => "message"
      target => "parsed"
    }
  }

  # Grok パターンでログをパース
  grok {
    match => {
      "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{LOGLEVEL:log_level}\] %{DATA:service} - %{GREEDYDATA:log_message}"
    }
  }

  # 日時フィールドの変換
  date {
    match => ["timestamp", "ISO8601"]
    target => "@timestamp"
  }

  # 不要なフィールドの削除
  mutate {
    remove_field => ["message", "timestamp", "agent", "ecs"]
  }

  # 条件付き処理
  if [log_level] == "ERROR" {
    mutate {
      add_tag => ["alert"]
    }
  }
}

output {
  # Elasticsearch への出力
  elasticsearch {
    hosts => ["https://es-node-01:9200", "https://es-node-02:9200"]
    index => "logs-%{[service]}-%{+YYYY.MM.dd}"
    ssl => true
    cacert => "/etc/logstash/certs/ca.crt"
    user => "logstash_writer"
    password => "${ES_PASSWORD}"

    # テンプレート管理
    template_name => "logs"
    template_overwrite => true

    # パフォーマンス設定
    pipeline => "logs-pipeline"
  }

  # エラーログは別途 Dead Letter Queue に送信
  if "alert" in [tags] {
    elasticsearch {
      hosts => ["https://es-node-01:9200"]
      index => "alerts-%{+YYYY.MM.dd}"
      user => "logstash_writer"
      password => "${ES_PASSWORD}"
    }
  }
}

4.5 Beats / Elastic Agent

4.5.1 Filebeat 設定例

# /etc/filebeat/filebeat.yml

filebeat.inputs:
  - type: filestream
    id: application-logs
    enabled: true
    paths:
      - /var/log/application/*.log
    parsers:
      - ndjson:
          target: ""
          add_error_key: true
    fields:
      environment: production
      service: payment-api
    fields_under_root: true

  - type: filestream
    id: system-logs
    enabled: true
    paths:
      - /var/log/syslog
      - /var/log/auth.log
    exclude_lines: ['^DBG']

processors:
  - add_host_metadata:
      when.not.contains.tags: forwarded
  - add_cloud_metadata: ~
  - add_docker_metadata: ~
  - add_kubernetes_metadata: ~
  - drop_fields:
      fields: ["agent.ephemeral_id", "agent.hostname"]

output.elasticsearch:
  hosts: ["https://es-node-01:9200", "https://es-node-02:9200"]
  protocol: "https"
  username: "filebeat_writer"
  password: "${ES_PASSWORD}"
  ssl:
    certificate_authorities: ["/etc/filebeat/certs/ca.crt"]
  indices:
    - index: "filebeat-app-%{+yyyy.MM.dd}"
      when.contains:
        fields.service: "payment-api"
    - index: "filebeat-system-%{+yyyy.MM.dd}"

# Kibana 接続(ダッシュボード設定用)
setup.kibana:
  host: "https://kibana:5601"

# ILM 設定
setup.ilm.enabled: true
setup.ilm.rollover_alias: "filebeat"
setup.ilm.pattern: "{now/d}-000001"
setup.ilm.policy_name: "filebeat-policy"

# モニタリング
monitoring.enabled: true
monitoring.elasticsearch:
  hosts: ["https://monitoring-es:9200"]

4.5.2 Metricbeat 設定例

# /etc/metricbeat/metricbeat.yml

metricbeat.modules:
  - module: system
    metricsets:
      - cpu
      - memory
      - network
      - diskio
      - filesystem
      - process
    period: 10s
    processes: ['.*']
    cpu.metrics: ["percentages", "normalized_percentages"]

  - module: docker
    metricsets:
      - container
      - cpu
      - diskio
      - memory
      - network
    hosts: ["unix:///var/run/docker.sock"]
    period: 10s

  - module: elasticsearch
    metricsets:
      - node
      - node_stats
      - index
      - index_summary
      - cluster_stats
      - shard
    period: 10s
    hosts: ["https://localhost:9200"]
    username: "monitoring_user"
    password: "${ES_MONITORING_PASSWORD}"

output.elasticsearch:
  hosts: ["https://monitoring-es:9200"]
  index: "metricbeat-%{+yyyy.MM.dd}"

第5章: 検索と Query DSL

5.1 基本的な検索

# 全ドキュメントを検索
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match_all": {}
  },
  "size": 10,
  "from": 0,
  "sort": [
    { "@timestamp": { "order": "desc" } }
  ]
}'

5.2 全文検索クエリ

5.2.1 match クエリ

# match クエリ(アナライザーによりトークン化される)
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "message": {
        "query": "payment timeout error",
        "operator": "and",
        "minimum_should_match": "75%",
        "fuzziness": "AUTO"
      }
    }
  }
}'

# match_phrase クエリ(語順を考慮)
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match_phrase": {
      "message": {
        "query": "connection refused",
        "slop": 2
      }
    }
  }
}'

# multi_match クエリ(複数フィールドを検索)
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "multi_match": {
      "query": "database timeout",
      "fields": ["message^3", "error.message^2", "service_name"],
      "type": "best_fields"
    }
  }
}'

5.2.2 term クエリ(完全一致)

# term クエリ(keyword フィールドに使用)
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "term": {
      "log_level": {
        "value": "ERROR"
      }
    }
  }
}'

# terms クエリ(複数値のOR条件)
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "terms": {
      "status_code": [500, 502, 503, 504]
    }
  }
}'

5.3 Bool クエリ(複合クエリ)

# 複雑な Bool クエリの例
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "message": "error"
          }
        },
        {
          "range": {
            "@timestamp": {
              "gte": "2026-04-08T00:00:00Z",
              "lte": "2026-04-08T23:59:59Z"
            }
          }
        }
      ],
      "filter": [
        {
          "term": {
            "service_name": "payment-service"
          }
        },
        {
          "terms": {
            "log_level": ["ERROR", "CRITICAL"]
          }
        }
      ],
      "should": [
        {
          "match": {
            "message": "timeout"
          }
        },
        {
          "match": {
            "message": "connection refused"
          }
        }
      ],
      "minimum_should_match": 1,
      "must_not": [
        {
          "term": {
            "host.name": "test-server"
          }
        }
      ]
    }
  },
  "highlight": {
    "fields": {
      "message": {
        "pre_tags": ["<em>"],
        "post_tags": ["</em>"],
        "fragment_size": 200,
        "number_of_fragments": 3
      }
    }
  },
  "sort": [
    { "@timestamp": "desc" },
    "_score"
  ],
  "size": 20,
  "_source": ["@timestamp", "service_name", "log_level", "message", "host.name"]
}'

5.3.1 Bool クエリの各句の違い

+------------------------------------------------------------------+
| Bool クエリの句    | スコアリング | キャッシュ | 用途              |
+------------------------------------------------------------------+
| must              | あり         | なし       | スコアに影響する   |
|                   |              |            | 条件              |
| filter            | なし         | あり       | Yes/No のフィルタ  |
|                   |              |            | リング            |
| should            | あり         | なし       | オプションの条件   |
|                   |              |            | (スコアブースト) |
| must_not          | なし         | あり       | 除外条件           |
+------------------------------------------------------------------+

パフォーマンスのポイント:
- スコアリングが不要な条件は filter を使用
- filter はキャッシュされるため高速
- must_not もキャッシュされる

5.4 Range クエリ

# 数値範囲
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "duration_ms": {
              "gte": 1000,
              "lte": 30000
            }
          }
        },
        {
          "range": {
            "@timestamp": {
              "gte": "now-24h",
              "lte": "now",
              "format": "strict_date_optional_time"
            }
          }
        }
      ]
    }
  }
}'

5.5 Nested クエリ

# Nested オブジェクトのマッピング
curl -X PUT "localhost:9200/orders" -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "properties": {
      "order_id": { "type": "keyword" },
      "items": {
        "type": "nested",
        "properties": {
          "product_name": { "type": "text" },
          "quantity": { "type": "integer" },
          "price": { "type": "float" }
        }
      }
    }
  }
}'

# Nested クエリ
curl -X GET "localhost:9200/orders/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "nested": {
      "path": "items",
      "query": {
        "bool": {
          "must": [
            { "match": { "items.product_name": "laptop" } },
            { "range": { "items.price": { "lte": 1000 } } }
          ]
        }
      },
      "inner_hits": {
        "size": 3,
        "highlight": {
          "fields": {
            "items.product_name": {}
          }
        }
      }
    }
  }
}'

5.6 関連性スコアリング (BM25)

Elasticsearch はデフォルトで BM25(Best Matching 25)アルゴリズムを使用してドキュメントの関連性スコアを計算する。

BM25 スコア = IDF × (tf × (k1 + 1)) / (tf + k1 × (1 - b + b × dl/avgdl))

  IDF  = Inverse Document Frequency(そのタームを含むドキュメントが少ないほど高スコア)
  tf   = Term Frequency(ドキュメント内でのターム出現頻度)
  dl   = Document Length(ドキュメントの長さ)
  avgdl = Average Document Length(平均ドキュメント長)
  k1   = タームの出現頻度の影響度(デフォルト: 1.2)
  b    = ドキュメント長の正規化の影響度(デフォルト: 0.75)
# スコアの説明を取得
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "message": "payment error"
    }
  },
  "explain": true
}'

# Function Score クエリ(スコアのカスタマイズ)
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "function_score": {
      "query": {
        "match": { "message": "error" }
      },
      "functions": [
        {
          "gauss": {
            "@timestamp": {
              "origin": "now",
              "scale": "2h",
              "decay": 0.5
            }
          },
          "weight": 2
        },
        {
          "field_value_factor": {
            "field": "severity_score",
            "modifier": "log1p",
            "missing": 1
          }
        }
      ],
      "score_mode": "sum",
      "boost_mode": "multiply"
    }
  }
}'

5.7 ページネーション

# 基本的なページネーション(from/size)
# 注意: from + size <= 10,000(デフォルト制限)
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "from": 0,
  "size": 20,
  "sort": [{ "@timestamp": "desc" }]
}'

# search_after(深いページネーション用)
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "size": 20,
  "sort": [
    { "@timestamp": "desc" },
    { "_id": "asc" }
  ],
  "search_after": ["2026-04-08T10:00:00.000Z", "abc123"]
}'

# Point in Time (PIT) API + search_after(一貫性のあるページネーション)
# まず PIT を作成
curl -X POST "localhost:9200/logs-*/_pit?keep_alive=5m"
# 応答: { "id": "..." }

# PIT を使って検索
curl -X GET "localhost:9200/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "size": 100,
  "sort": [{ "@timestamp": "desc" }, { "_id": "asc" }],
  "pit": {
    "id": "<PIT_ID>",
    "keep_alive": "5m"
  }
}'

# Scroll API(大量のドキュメントをエクスポートする場合)
# 注意: 新しいプロジェクトではPIT + search_after推奨
curl -X POST "localhost:9200/logs-*/_search?scroll=5m&pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "size": 1000,
  "sort": ["_doc"]
}'

# 次のページを取得
curl -X POST "localhost:9200/_search/scroll?pretty" -H 'Content-Type: application/json' -d'
{
  "scroll": "5m",
  "scroll_id": "<SCROLL_ID>"
}'

第6章: アグリゲーション(集約)詳解

6.1 バケットアグリゲーション

6.1.1 terms アグリゲーション

# サービス別のエラー件数
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "query": {
    "term": { "log_level": "ERROR" }
  },
  "aggs": {
    "errors_by_service": {
      "terms": {
        "field": "service_name",
        "size": 20,
        "order": { "_count": "desc" },
        "min_doc_count": 1
      },
      "aggs": {
        "error_types": {
          "terms": {
            "field": "error.type",
            "size": 5
          }
        },
        "avg_duration": {
          "avg": {
            "field": "duration_ms"
          }
        }
      }
    }
  }
}'

6.1.2 date_histogram アグリゲーション

# 時間帯別のリクエスト数とレスポンスタイムの推移
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "query": {
    "range": {
      "@timestamp": {
        "gte": "now-24h",
        "lte": "now"
      }
    }
  },
  "aggs": {
    "requests_over_time": {
      "date_histogram": {
        "field": "@timestamp",
        "fixed_interval": "5m",
        "time_zone": "Asia/Tokyo",
        "min_doc_count": 0,
        "extended_bounds": {
          "min": "now-24h",
          "max": "now"
        }
      },
      "aggs": {
        "avg_response_time": {
          "avg": { "field": "duration_ms" }
        },
        "p95_response_time": {
          "percentiles": {
            "field": "duration_ms",
            "percents": [95]
          }
        },
        "error_rate": {
          "filter": {
            "range": { "status_code": { "gte": 500 } }
          },
          "aggs": {
            "count": { "value_count": { "field": "status_code" } }
          }
        },
        "status_codes": {
          "terms": {
            "field": "status_code",
            "size": 10
          }
        }
      }
    }
  }
}'

6.1.3 histogram アグリゲーション

# レスポンスタイムの分布
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "response_time_distribution": {
      "histogram": {
        "field": "duration_ms",
        "interval": 100,
        "min_doc_count": 0,
        "extended_bounds": {
          "min": 0,
          "max": 5000
        }
      }
    }
  }
}'

6.1.4 range アグリゲーション

# レスポンスタイムのレンジ分類
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "response_time_ranges": {
      "range": {
        "field": "duration_ms",
        "ranges": [
          { "key": "fast", "to": 100 },
          { "key": "normal", "from": 100, "to": 500 },
          { "key": "slow", "from": 500, "to": 2000 },
          { "key": "very_slow", "from": 2000 }
        ]
      },
      "aggs": {
        "by_service": {
          "terms": { "field": "service_name", "size": 10 }
        }
      }
    }
  }
}'

6.2 メトリックアグリゲーション

# 包括的な統計情報
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "query": {
    "bool": {
      "filter": [
        { "range": { "@timestamp": { "gte": "now-1h" } } },
        { "term": { "service_name": "api-gateway" } }
      ]
    }
  },
  "aggs": {
    "response_stats": {
      "stats": { "field": "duration_ms" }
    },
    "extended_stats": {
      "extended_stats": { "field": "duration_ms" }
    },
    "percentile_values": {
      "percentiles": {
        "field": "duration_ms",
        "percents": [50, 75, 90, 95, 99, 99.9]
      }
    },
    "percentile_ranks": {
      "percentile_ranks": {
        "field": "duration_ms",
        "values": [100, 500, 1000, 5000]
      }
    },
    "cardinality_users": {
      "cardinality": {
        "field": "user_id",
        "precision_threshold": 3000
      }
    },
    "value_count": {
      "value_count": { "field": "duration_ms" }
    }
  }
}'

6.3 significant_terms アグリゲーション

# エラーログに特徴的なキーワードを検出
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "query": {
    "term": { "log_level": "ERROR" }
  },
  "aggs": {
    "significant_error_terms": {
      "significant_terms": {
        "field": "message.keyword",
        "size": 10,
        "min_doc_count": 5,
        "background_filter": {
          "range": {
            "@timestamp": { "gte": "now-24h" }
          }
        }
      }
    }
  }
}'

6.4 composite アグリゲーション

大量のバケットを効率的にページネーションする。

# composite アグリゲーション(最初のページ)
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "service_status_composite": {
      "composite": {
        "size": 100,
        "sources": [
          {
            "service": {
              "terms": { "field": "service_name" }
            }
          },
          {
            "status": {
              "terms": { "field": "status_code" }
            }
          },
          {
            "date": {
              "date_histogram": {
                "field": "@timestamp",
                "calendar_interval": "1h"
              }
            }
          }
        ]
      },
      "aggs": {
        "avg_duration": {
          "avg": { "field": "duration_ms" }
        },
        "doc_count": {
          "value_count": { "field": "_id" }
        }
      }
    }
  }
}'

# 次のページを取得(after キーを指定)
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "service_status_composite": {
      "composite": {
        "size": 100,
        "sources": [
          { "service": { "terms": { "field": "service_name" } } },
          { "status": { "terms": { "field": "status_code" } } },
          { "date": { "date_histogram": { "field": "@timestamp", "calendar_interval": "1h" } } }
        ],
        "after": {
          "service": "payment-service",
          "status": 200,
          "date": 1712566800000
        }
      }
    }
  }
}'

6.5 パイプラインアグリゲーション

# パイプラインアグリゲーションの例
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "requests_per_hour": {
      "date_histogram": {
        "field": "@timestamp",
        "fixed_interval": "1h"
      },
      "aggs": {
        "avg_response_time": {
          "avg": { "field": "duration_ms" }
        },
        "error_count": {
          "filter": {
            "range": { "status_code": { "gte": 500 } }
          }
        }
      }
    },
    "max_avg_response_time": {
      "max_bucket": {
        "buckets_path": "requests_per_hour>avg_response_time"
      }
    },
    "avg_hourly_errors": {
      "avg_bucket": {
        "buckets_path": "requests_per_hour>error_count._count"
      }
    },
    "response_time_derivative": {
      "derivative": {
        "buckets_path": "requests_per_hour>avg_response_time"
      }
    },
    "moving_avg_response": {
      "moving_fn": {
        "buckets_path": "requests_per_hour>avg_response_time",
        "window": 6,
        "script": "MovingFunctions.unweightedAvg(values)"
      }
    },
    "cumulative_errors": {
      "cumulative_sum": {
        "buckets_path": "requests_per_hour>error_count._count"
      }
    }
  }
}'

第7章: クラスタ管理

7.1 クラスタヘルスの監視

# クラスタヘルスの確認
curl -X GET "localhost:9200/_cluster/health?pretty"

# インデックスレベルのヘルスチェック
curl -X GET "localhost:9200/_cluster/health?level=indices&pretty"

# シャードレベルのヘルスチェック
curl -X GET "localhost:9200/_cluster/health?level=shards&pretty"

# 特定インデックスのヘルスチェック
curl -X GET "localhost:9200/_cluster/health/logs-*?pretty"

7.1.1 クラスタステータスの意味

+------------------------------------------------------------------+
| ステータス | 意味                                                  |
+------------------------------------------------------------------+
| Green      | 全てのプライマリシャードとレプリカシャードが割り当て済み |
| Yellow     | 全プライマリシャードは割り当て済みだが、一部レプリカが   |
|            | 未割り当て                                             |
| Red        | 一部のプライマリシャードが未割り当て                     |
|            | (データの一部にアクセスできない可能性)                 |
+------------------------------------------------------------------+

7.2 シャード割り当て

# 未割り当てシャードの原因を調査
curl -X GET "localhost:9200/_cluster/allocation/explain?pretty" -H 'Content-Type: application/json' -d'
{
  "index": "logs-2026.04.08",
  "shard": 0,
  "primary": false
}'

# シャードの割り当てを手動で再試行
curl -X POST "localhost:9200/_cluster/reroute?retry_failed=true&pretty"

# シャードの手動移動
curl -X POST "localhost:9200/_cluster/reroute?pretty" -H 'Content-Type: application/json' -d'
{
  "commands": [
    {
      "move": {
        "index": "logs-2026.04.08",
        "shard": 0,
        "from_node": "data-node-1",
        "to_node": "data-node-2"
      }
    },
    {
      "allocate_replica": {
        "index": "logs-2026.04.08",
        "shard": 1,
        "node": "data-node-3"
      }
    }
  ]
}'

# シャード割り当てのフィルタリング
curl -X PUT "localhost:9200/logs-2026.04.08/_settings" -H 'Content-Type: application/json' -d'
{
  "index.routing.allocation.require.data": "hot",
  "index.routing.allocation.exclude._name": "data-node-old"
}'

7.3 クラスタ設定

# クラスタ設定の確認
curl -X GET "localhost:9200/_cluster/settings?include_defaults=true&pretty"

# 動的クラスタ設定の変更
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "cluster.routing.allocation.enable": "all",
    "cluster.routing.allocation.cluster_concurrent_rebalance": 2,
    "cluster.routing.allocation.node_concurrent_recoveries": 2,
    "cluster.routing.allocation.node_initial_primaries_recoveries": 4,
    "cluster.routing.allocation.disk.threshold_enabled": true,
    "cluster.routing.allocation.disk.watermark.low": "85%",
    "cluster.routing.allocation.disk.watermark.high": "90%",
    "cluster.routing.allocation.disk.watermark.flood_stage": "95%",
    "indices.recovery.max_bytes_per_sec": "100mb"
  },
  "transient": {
    "cluster.routing.allocation.enable": "primaries"
  }
}'

7.4 Hot/Warm/Cold アーキテクチャ

+------------------------------------------------------------------+
|                    データライフサイクル                             |
|                                                                   |
|  書き込み -> Hot Tier -> Warm Tier -> Cold Tier -> Frozen -> 削除  |
|                                                                   |
|  +------------+  +--------------+  +-------------+  +----------+  |
|  | Hot Tier   |  | Warm Tier    |  | Cold Tier   |  | Frozen   |  |
|  |            |  |              |  |             |  | Tier     |  |
|  | 高速SSD    |  | SATA SSD/HDD|  | HDD         |  | 共有     |  |
|  | 最新データ  |  | 1-4週間前    |  | 1-6ヶ月前    |  | ストレージ|  |
|  | 読み書き可  |  | 読み取り専用 |  | 最小限の     |  | スナップ |  |
|  | 高CPU/RAM  |  | 中CPU/RAM   |  | リソース     |  | ショット |  |
|  +------------+  +--------------+  +-------------+  +----------+  |
+------------------------------------------------------------------+
# elasticsearch.yml - Hot ノード
node.roles: [ data_hot, data_content ]
node.name: hot-node-01
node.attr.data: hot

# elasticsearch.yml - Warm ノード
node.roles: [ data_warm ]
node.name: warm-node-01
node.attr.data: warm

# elasticsearch.yml - Cold ノード
node.roles: [ data_cold ]
node.name: cold-node-01
node.attr.data: cold

# elasticsearch.yml - Frozen ノード
node.roles: [ data_frozen ]
node.name: frozen-node-01
node.attr.data: frozen

7.5 スナップショットとリストア

# スナップショットリポジトリの登録(S3)
curl -X PUT "localhost:9200/_snapshot/s3-backup" -H 'Content-Type: application/json' -d'
{
  "type": "s3",
  "settings": {
    "bucket": "es-backups-production",
    "region": "ap-northeast-1",
    "base_path": "elasticsearch/snapshots",
    "compress": true,
    "server_side_encryption": true,
    "max_snapshot_bytes_per_sec": "200mb",
    "max_restore_bytes_per_sec": "200mb"
  }
}'

# スナップショットリポジトリの登録(共有ファイルシステム)
curl -X PUT "localhost:9200/_snapshot/nfs-backup" -H 'Content-Type: application/json' -d'
{
  "type": "fs",
  "settings": {
    "location": "/mnt/backups/elasticsearch",
    "compress": true,
    "max_snapshot_bytes_per_sec": "100mb"
  }
}'

# スナップショットの作成
curl -X PUT "localhost:9200/_snapshot/s3-backup/snapshot-2026-04-08?wait_for_completion=false" -H 'Content-Type: application/json' -d'
{
  "indices": "logs-*,metrics-*",
  "ignore_unavailable": true,
  "include_global_state": false,
  "metadata": {
    "taken_by": "sre-team",
    "reason": "daily backup"
  }
}'

# スナップショットの状態確認
curl -X GET "localhost:9200/_snapshot/s3-backup/snapshot-2026-04-08/_status?pretty"

# スナップショット一覧
curl -X GET "localhost:9200/_snapshot/s3-backup/_all?pretty"

# スナップショットからのリストア
curl -X POST "localhost:9200/_snapshot/s3-backup/snapshot-2026-04-08/_restore" -H 'Content-Type: application/json' -d'
{
  "indices": "logs-2026.04.07",
  "ignore_unavailable": true,
  "include_global_state": false,
  "rename_pattern": "(.+)",
  "rename_replacement": "restored-$1",
  "index_settings": {
    "index.number_of_replicas": 0
  }
}'

# SLM(Snapshot Lifecycle Management)ポリシーの作成
curl -X PUT "localhost:9200/_slm/policy/daily-snapshots" -H 'Content-Type: application/json' -d'
{
  "schedule": "0 0 1 * * ?",
  "name": "<daily-snapshot-{now/d}>",
  "repository": "s3-backup",
  "config": {
    "indices": ["logs-*", "metrics-*"],
    "ignore_unavailable": true,
    "include_global_state": false
  },
  "retention": {
    "expire_after": "30d",
    "min_count": 5,
    "max_count": 50
  }
}'

# SLM ポリシーの手動実行
curl -X POST "localhost:9200/_slm/policy/daily-snapshots/_execute?pretty"

7.6 リバランスの制御

# メンテナンス時のシャード割り当ての無効化
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "cluster.routing.allocation.enable": "primaries"
  }
}'

# メンテナンス後の復旧
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "cluster.routing.allocation.enable": "all"
  }
}'

# ノードのグレースフルシャットダウン前の排出
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "transient": {
    "cluster.routing.allocation.exclude._ip": "10.0.1.100"
  }
}'

第8章: セキュリティ

8.1 認証(Authentication)

# elasticsearch.yml - セキュリティ基本設定
xpack.security.enabled: true
xpack.security.enrollment.enabled: true

# ネイティブレルム(内蔵ユーザーデータベース)
xpack.security.authc.realms.native.native1:
  order: 0

# LDAP レルム
xpack.security.authc.realms.ldap.ldap1:
  order: 1
  url: "ldaps://ldap.example.com:636"
  bind_dn: "cn=elasticsearch,ou=services,dc=example,dc=com"
  user_search:
    base_dn: "ou=users,dc=example,dc=com"
    filter: "(uid={0})"
  group_search:
    base_dn: "ou=groups,dc=example,dc=com"
  ssl:
    certificate_authorities: ["/etc/elasticsearch/certs/ca.crt"]
    verification_mode: full
  unmapped_groups_as_roles: false

# SAML レルム
xpack.security.authc.realms.saml.saml1:
  order: 2
  idp.metadata.path: "/etc/elasticsearch/saml/idp-metadata.xml"
  idp.entity_id: "https://idp.example.com/"
  sp.entity_id: "https://kibana.example.com/"
  sp.acs: "https://kibana.example.com/api/security/saml/callback"
  attributes.principal: "nameid"
  attributes.groups: "groups"

# API キー認証
xpack.security.authc.api_key.enabled: true
# 組み込みユーザーのパスワード設定
bin/elasticsearch-setup-passwords interactive

# ユーザーの作成
curl -X POST "localhost:9200/_security/user/sre_engineer" -H 'Content-Type: application/json' -d'
{
  "password": "SecureP@ssw0rd!",
  "roles": ["sre_role", "kibana_user"],
  "full_name": "SRE Engineer",
  "email": "sre@example.com",
  "metadata": {
    "team": "platform"
  }
}'

# API キーの作成
curl -X POST "localhost:9200/_security/api_key" -H 'Content-Type: application/json' -d'
{
  "name": "monitoring-api-key",
  "expiration": "30d",
  "role_descriptors": {
    "monitoring_role": {
      "cluster": ["monitor"],
      "indices": [
        {
          "names": ["logs-*", "metrics-*"],
          "privileges": ["read", "view_index_metadata"]
        }
      ]
    }
  },
  "metadata": {
    "application": "monitoring-system",
    "created_by": "sre-team"
  }
}'

8.2 認可(Authorization / RBAC)

# カスタムロールの作成
curl -X PUT "localhost:9200/_security/role/sre_role" -H 'Content-Type: application/json' -d'
{
  "cluster": [
    "monitor",
    "manage_index_templates",
    "manage_ilm",
    "manage_pipeline",
    "cluster:admin/snapshot/create",
    "cluster:admin/snapshot/status",
    "cluster:admin/repository/get"
  ],
  "indices": [
    {
      "names": ["logs-*", "metrics-*", "traces-*"],
      "privileges": ["read", "view_index_metadata", "manage"],
      "allow_restricted_indices": false
    },
    {
      "names": [".kibana*"],
      "privileges": ["read", "view_index_metadata"]
    }
  ],
  "applications": [
    {
      "application": "kibana-.kibana",
      "privileges": ["feature_discover.all", "feature_dashboard.all", "feature_visualize.read"],
      "resources": ["space:default", "space:monitoring"]
    }
  ],
  "run_as": [],
  "metadata": {
    "description": "SREチーム用のロール"
  }
}'

# 読み取り専用ロール
curl -X PUT "localhost:9200/_security/role/readonly_role" -H 'Content-Type: application/json' -d'
{
  "cluster": ["monitor"],
  "indices": [
    {
      "names": ["logs-*"],
      "privileges": ["read"],
      "query": {
        "bool": {
          "filter": [
            { "term": { "environment": "production" } }
          ]
        }
      },
      "field_security": {
        "grant": ["@timestamp", "service_name", "log_level", "message", "host.name"],
        "except": ["password", "secret", "token"]
      }
    }
  ]
}'

8.3 TLS/SSL の設定

# elasticsearch.yml - TLS 設定

# トランスポート層(ノード間通信)
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: full
xpack.security.transport.ssl.keystore.path: certs/transport.p12
xpack.security.transport.ssl.truststore.path: certs/transport.p12

# HTTP 層(クライアント通信)
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: certs/http.p12
xpack.security.http.ssl.truststore.path: certs/http.p12

# 証明書の設定(PEM形式の場合)
# xpack.security.transport.ssl.key: certs/node.key
# xpack.security.transport.ssl.certificate: certs/node.crt
# xpack.security.transport.ssl.certificate_authorities: ["certs/ca.crt"]
# 証明書の生成(elasticsearch-certutil)
bin/elasticsearch-certutil ca --out elastic-stack-ca.p12 --pass ""

bin/elasticsearch-certutil cert \
  --ca elastic-stack-ca.p12 \
  --ca-pass "" \
  --out elastic-certificates.p12 \
  --pass "" \
  --dns "es-node-01,es-node-02,es-node-03" \
  --ip "10.0.1.1,10.0.1.2,10.0.1.3"

# HTTP 証明書の生成
bin/elasticsearch-certutil http

8.4 監査ログ

# elasticsearch.yml - 監査ログ設定
xpack.security.audit.enabled: true
xpack.security.audit.logfile.events.include:
  - access_denied
  - access_granted
  - anonymous_access_denied
  - authentication_failed
  - authentication_success
  - connection_denied
  - tampered_request
  - run_as_denied
  - run_as_granted
  - security_config_change

xpack.security.audit.logfile.events.exclude:
  - system_access_granted

xpack.security.audit.logfile.events.emit_request_body: false

8.5 フィールドレベル・ドキュメントレベルセキュリティ

# ドキュメントレベルセキュリティ(DLS)
# 特定の条件に一致するドキュメントのみアクセス可能
curl -X PUT "localhost:9200/_security/role/team_a_role" -H 'Content-Type: application/json' -d'
{
  "indices": [
    {
      "names": ["logs-*"],
      "privileges": ["read"],
      "query": {
        "term": { "team": "team-a" }
      }
    }
  ]
}'

# フィールドレベルセキュリティ(FLS)
# 特定のフィールドのみアクセス可能
curl -X PUT "localhost:9200/_security/role/limited_fields_role" -H 'Content-Type: application/json' -d'
{
  "indices": [
    {
      "names": ["customer-*"],
      "privileges": ["read"],
      "field_security": {
        "grant": ["customer_id", "order_date", "total_amount"],
        "except": ["credit_card_number", "ssn", "password_hash"]
      }
    }
  ]
}'

第9章: パフォーマンスチューニング

9.1 インデックスパフォーマンス

9.1.1 Bulk インデックスの最適化

# リフレッシュインターバルの調整(大量インポート時)
curl -X PUT "localhost:9200/logs-*/_settings" -H 'Content-Type: application/json' -d'
{
  "index": {
    "refresh_interval": "30s",
    "number_of_replicas": 0,
    "translog.durability": "async",
    "translog.sync_interval": "30s",
    "translog.flush_threshold_size": "1gb"
  }
}'

# 大量インポート完了後、設定を戻す
curl -X PUT "localhost:9200/logs-*/_settings" -H 'Content-Type: application/json' -d'
{
  "index": {
    "refresh_interval": "1s",
    "number_of_replicas": 1,
    "translog.durability": "request"
  }
}'

9.1.2 マッピングの最適化

# 不要なフィールドのインデックスを無効化
curl -X PUT "localhost:9200/optimized-logs" -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "@timestamp": { "type": "date" },
      "message": {
        "type": "text",
        "norms": false
      },
      "log_level": {
        "type": "keyword",
        "doc_values": true
      },
      "stack_trace": {
        "type": "text",
        "index": false,
        "doc_values": false
      },
      "raw_data": {
        "type": "object",
        "enabled": false
      },
      "request_id": {
        "type": "keyword",
        "doc_values": false,
        "index": true
      }
    },
    "_source": {
      "excludes": ["raw_data_binary"]
    }
  }
}'

9.2 検索パフォーマンス

9.2.1 フィルターコンテキストの活用

# 悪い例: must でフィルタリング(スコアリングが不要なのに計算される)
# "must": [{ "term": { "status": "active" } }]

# 良い例: filter を使用(キャッシュされ、スコアリングなし)
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": { "message": "error timeout" } }
      ],
      "filter": [
        { "term": { "service_name": "api-gateway" } },
        { "range": { "@timestamp": { "gte": "now-1h" } } },
        { "terms": { "status_code": [500, 502, 503] } }
      ]
    }
  }
}'

9.2.2 検索プロファイリング

# プロファイル API で検索のボトルネックを特定
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "profile": true,
  "query": {
    "bool": {
      "must": [
        { "match": { "message": "error" } }
      ],
      "filter": [
        { "range": { "@timestamp": { "gte": "now-24h" } } }
      ]
    }
  },
  "size": 10
}'

9.3 ハードウェアサイジング

+------------------------------------------------------------------+
|               ハードウェアサイジングガイドライン                    |
+------------------------------------------------------------------+
| 役割            | CPU      | RAM       | ストレージ   | ネットワーク |
+------------------------------------------------------------------+
| Master          | 2-4コア  | 4-8 GB    | 低速SSD 50GB | 1 Gbps    |
| (3ノード推奨)   |          |           |              |           |
+------------------------------------------------------------------+
| Data (Hot)      | 8-16コア | 32-64 GB  | NVMe SSD     | 10 Gbps   |
|                 |          |           | 1-4 TB       |           |
+------------------------------------------------------------------+
| Data (Warm)     | 4-8コア  | 16-32 GB  | SATA SSD/HDD | 1-10 Gbps|
|                 |          |           | 4-8 TB       |           |
+------------------------------------------------------------------+
| Data (Cold)     | 2-4コア  | 8-16 GB   | HDD          | 1 Gbps   |
|                 |          |           | 8-16 TB      |           |
+------------------------------------------------------------------+
| Ingest          | 4-8コア  | 8-16 GB   | SSD 100GB    | 10 Gbps   |
+------------------------------------------------------------------+
| Coordinating    | 4-8コア  | 16-32 GB  | SSD 100GB    | 10 Gbps   |
+------------------------------------------------------------------+
| ML              | 8-16コア | 16-64 GB  | SSD 200GB    | 10 Gbps   |
+------------------------------------------------------------------+

一般的なガイドライン:
- RAM: JVM ヒープに50%(最大31GB)、残り50%はOSファイルキャッシュ
- ディスク: 1シャードあたり10-50GB(推奨上限50GB)
- シャード数: ノード数 × 1-3(ホットデータ)
- データノードあたりのシャード数: 600-1000以下

9.4 JVM / ヒープ設定

# jvm.options - JVM ヒープ設定
# /etc/elasticsearch/jvm.options.d/heap.options

# ヒープサイズ(物理メモリの50%、最大31GBに設定)
-Xms16g
-Xmx16g

# 重要: Xms と Xmx は同じ値にする
# 最大31GB(Compressed Oops の閾値を超えない)
# 残りのメモリはファイルシステムキャッシュに使用される
# elasticsearch.yml - メモリ関連設定

# メモリロック(スワップ防止)
bootstrap.memory_lock: true

# /etc/systemd/system/elasticsearch.service.d/override.conf
# [Service]
# LimitMEMLOCK=infinity
# メモリロックの確認
curl -X GET "localhost:9200/_nodes?filter_path=**.mlockall&pretty"

# JVM のヒープ使用量確認
curl -X GET "localhost:9200/_nodes/stats/jvm?pretty"

9.5 インデックス設定の最適化

# マージポリシーの設定
curl -X PUT "localhost:9200/logs-*/_settings" -H 'Content-Type: application/json' -d'
{
  "index": {
    "merge.policy.max_merge_at_once": 10,
    "merge.policy.max_merged_segment": "5gb",
    "merge.policy.segments_per_tier": 10,
    "merge.scheduler.max_thread_count": 1
  }
}'

# フォースマージ(読み取り専用インデックスに対して)
curl -X POST "localhost:9200/logs-2026.04.07/_forcemerge?max_num_segments=1"

9.6 サーキットブレーカー

# elasticsearch.yml - サーキットブレーカー設定

# 親サーキットブレーカー(全体のメモリ使用量制限)
indices.breaker.total.limit: 70%

# フィールドデータサーキットブレーカー
indices.breaker.fielddata.limit: 40%
indices.breaker.fielddata.overhead: 1.03

# リクエストサーキットブレーカー
indices.breaker.request.limit: 60%
indices.breaker.request.overhead: 1

# インフライトリクエストサーキットブレーカー
network.breaker.inflight_requests.limit: 100%
network.breaker.inflight_requests.overhead: 2
# サーキットブレーカーの状態確認
curl -X GET "localhost:9200/_nodes/stats/breaker?pretty"

9.7 Fielddata と Doc Values

+------------------------------------------------------------------+
|              Fielddata vs Doc Values                              |
+------------------------------------------------------------------+
| 特性         | Fielddata              | Doc Values              |
+------------------------------------------------------------------+
| ストレージ   | ヒープメモリ            | ディスク(OS キャッシュ) |
| 対象         | text フィールド         | keyword, 数値, 日付等    |
| 構築タイミング| クエリ時(遅延ロード)   | インデックス時           |
| メモリ圧力   | 高(OOM リスク)        | 低                       |
| 推奨         | 避けるべき              | デフォルトで有効          |
+------------------------------------------------------------------+

推奨: text フィールドでの集約には keyword サブフィールドを使用する
# Fielddata の使用状況確認
curl -X GET "localhost:9200/_nodes/stats/indices/fielddata?fields=*&pretty"

# Fielddata キャッシュのクリア
curl -X POST "localhost:9200/_cache/clear?fielddata=true"

# クエリキャッシュのクリア
curl -X POST "localhost:9200/_cache/clear?query=true"

# リクエストキャッシュのクリア
curl -X POST "localhost:9200/_cache/clear?request=true"

9.8 スレッドプール

# スレッドプールの状態確認
curl -X GET "localhost:9200/_cat/thread_pool?v&h=name,active,queue,rejected,completed&pretty"

# スレッドプールの詳細情報
curl -X GET "localhost:9200/_nodes/stats/thread_pool?pretty"
# elasticsearch.yml - スレッドプール設定

# 検索スレッドプール
thread_pool.search.size: 13  # デフォルト: int((# of allocated processors * 3) / 2) + 1
thread_pool.search.queue_size: 1000

# 書き込みスレッドプール
thread_pool.write.size: 8  # デフォルト: # of allocated processors
thread_pool.write.queue_size: 10000

第10章: モニタリングとオブザーバビリティ

10.1 Cat API

Cat API は、クラスタの状態をテーブル形式で簡潔に表示する。

# ノード一覧
curl -X GET "localhost:9200/_cat/nodes?v&h=name,ip,role,heap.percent,ram.percent,cpu,load_1m,disk.used_percent,node.role&pretty"

# インデックス一覧
curl -X GET "localhost:9200/_cat/indices?v&h=index,health,status,pri,rep,docs.count,store.size&s=store.size:desc&pretty"

# シャードの割り当て状況
curl -X GET "localhost:9200/_cat/shards?v&h=index,shard,prirep,state,docs,store,node&s=store:desc&pretty"

# 未割り当てシャード
curl -X GET "localhost:9200/_cat/shards?v&h=index,shard,prirep,state,unassigned.reason&s=state&pretty" | grep UNASSIGNED

# ノードのディスク使用量
curl -X GET "localhost:9200/_cat/allocation?v&pretty"

# セグメント情報
curl -X GET "localhost:9200/_cat/segments/logs-*?v&h=index,shard,segment,docs.count,size,size.memory&pretty"

# ペンディングタスク
curl -X GET "localhost:9200/_cat/pending_tasks?v&pretty"

# リカバリ状況
curl -X GET "localhost:9200/_cat/recovery?v&active_only=true&pretty"

# テンプレート一覧
curl -X GET "localhost:9200/_cat/templates?v&pretty"

# プラグイン一覧
curl -X GET "localhost:9200/_cat/plugins?v&pretty"

# スレッドプール
curl -X GET "localhost:9200/_cat/thread_pool?v&h=node_name,name,active,queue,rejected&pretty"

10.2 クラスタ統計

# クラスタ統計
curl -X GET "localhost:9200/_cluster/stats?pretty"

# クラスタヘルス
curl -X GET "localhost:9200/_cluster/health?pretty"

# ノード統計(詳細)
curl -X GET "localhost:9200/_nodes/stats?pretty"

# 特定のメトリクスのみ取得
curl -X GET "localhost:9200/_nodes/stats/jvm,os,fs,indices?pretty"

# ノードのホットスレッド(パフォーマンス問題のデバッグ)
curl -X GET "localhost:9200/_nodes/hot_threads?threads=5&interval=500ms"

10.3 インデックス統計

# インデックスの統計情報
curl -X GET "localhost:9200/logs-*/_stats?pretty"

# 特定のメトリクス
curl -X GET "localhost:9200/logs-*/_stats/indexing,search,merge?pretty"

# インデックスのリカバリ状況
curl -X GET "localhost:9200/logs-*/_recovery?pretty&active_only=true"

# インデックスのセグメント情報
curl -X GET "localhost:9200/logs-*/_segments?pretty"

10.4 スローログ

# 検索スローログの設定
curl -X PUT "localhost:9200/logs-*/_settings" -H 'Content-Type: application/json' -d'
{
  "index.search.slowlog.threshold.query.warn": "10s",
  "index.search.slowlog.threshold.query.info": "5s",
  "index.search.slowlog.threshold.query.debug": "2s",
  "index.search.slowlog.threshold.query.trace": "500ms",
  "index.search.slowlog.threshold.fetch.warn": "1s",
  "index.search.slowlog.threshold.fetch.info": "800ms",
  "index.search.slowlog.threshold.fetch.debug": "500ms",
  "index.search.slowlog.threshold.fetch.trace": "200ms",
  "index.search.slowlog.level": "info"
}'

# インデックススローログの設定
curl -X PUT "localhost:9200/logs-*/_settings" -H 'Content-Type: application/json' -d'
{
  "index.indexing.slowlog.threshold.index.warn": "10s",
  "index.indexing.slowlog.threshold.index.info": "5s",
  "index.indexing.slowlog.threshold.index.debug": "2s",
  "index.indexing.slowlog.threshold.index.trace": "500ms",
  "index.indexing.slowlog.level": "info",
  "index.indexing.slowlog.source": "1000"
}'
# log4j2.properties - スローログの出力設定
# /etc/elasticsearch/log4j2.properties

appender.index_search_slowlog_rolling.type = RollingFile
appender.index_search_slowlog_rolling.name = index_search_slowlog_rolling
appender.index_search_slowlog_rolling.fileName = ${sys:es.logs.base_path}/${sys:es.logs.cluster_name}_index_search_slowlog.json
appender.index_search_slowlog_rolling.layout.type = JsonLayout
appender.index_search_slowlog_rolling.policies.type = Policies
appender.index_search_slowlog_rolling.policies.size.type = SizeBasedTriggeringPolicy
appender.index_search_slowlog_rolling.policies.size.size = 1GB
appender.index_search_slowlog_rolling.strategy.type = DefaultRolloverStrategy
appender.index_search_slowlog_rolling.strategy.max = 10

10.5 モニタリングダッシュボード用のクエリ例

# クラスタの主要メトリクスを一括取得するスクリプト
# #!/bin/bash
# ES_HOST="localhost:9200"
# 
# echo "=== Cluster Health ==="
# curl -s "$ES_HOST/_cluster/health" | jq '.'
# 
# echo "=== Node Disk Usage ==="
# curl -s "$ES_HOST/_cat/allocation?v"
# 
# echo "=== JVM Heap Usage ==="
# curl -s "$ES_HOST/_cat/nodes?v&h=name,heap.percent,ram.percent,cpu"
# 
# echo "=== Pending Tasks ==="
# curl -s "$ES_HOST/_cat/pending_tasks?v"
# 
# echo "=== Thread Pool Rejections ==="
# curl -s "$ES_HOST/_cat/thread_pool?v&h=node_name,name,rejected" | grep -v "^$" | awk '$3 > 0'
# 
# echo "=== Unassigned Shards ==="
# curl -s "$ES_HOST/_cat/shards?v" | grep UNASSIGNED
# 
# echo "=== Index Stats ==="
# curl -s "$ES_HOST/_cat/indices?v&h=index,health,pri,rep,docs.count,store.size&s=store.size:desc" | head -20

# タスク管理 API
curl -X GET "localhost:9200/_tasks?detailed=true&group_by=parents&pretty"

# 長時間実行中のタスクを確認
curl -X GET "localhost:9200/_tasks?actions=*search*&detailed=true&pretty"

# タスクのキャンセル
curl -X POST "localhost:9200/_tasks/<task_id>/_cancel"

第11章: Kibana との統合

11.1 Kibana の主要機能

+------------------------------------------------------------------+
|                        Kibana                                     |
|                                                                   |
|  +------------------+  +------------------+  +-----------------+  |
|  | Discover          |  | Dashboard        |  | Lens            |  |
|  | ログの検索/閲覧   |  | ダッシュボード    |  | 可視化ビルダー   |  |
|  +------------------+  +------------------+  +-----------------+  |
|                                                                   |
|  +------------------+  +------------------+  +-----------------+  |
|  | Alerting          |  | Dev Tools        |  | Stack Monitoring|  |
|  | アラート管理      |  | Console/Profiler |  | クラスタ監視    |  |
|  +------------------+  +------------------+  +-----------------+  |
|                                                                   |
|  +------------------+  +------------------+  +-----------------+  |
|  | Maps              |  | Canvas           |  | Machine Learning|  |
|  | 地理空間可視化    |  | プレゼンテーション|  | 異常検知         |  |
|  +------------------+  +------------------+  +-----------------+  |
+------------------------------------------------------------------+

11.1.1 Kibana の設定

# kibana.yml - 基本設定

server.port: 5601
server.host: "0.0.0.0"
server.name: "kibana-production"
server.basePath: "/kibana"
server.rewriteBasePath: true

# Elasticsearch 接続
elasticsearch.hosts: ["https://es-node-01:9200", "https://es-node-02:9200"]
elasticsearch.username: "kibana_system"
elasticsearch.password: "${KIBANA_ES_PASSWORD}"
elasticsearch.ssl.certificateAuthorities: ["/etc/kibana/certs/ca.crt"]
elasticsearch.ssl.verificationMode: full

# セキュリティ
xpack.security.encryptionKey: "something_at_least_32_characters"
xpack.encryptedSavedObjects.encryptionKey: "something_at_least_32_characters"
xpack.reporting.encryptionKey: "something_at_least_32_characters"

# ロギング
logging.root.level: info
logging.appenders.file.type: file
logging.appenders.file.fileName: /var/log/kibana/kibana.log
logging.appenders.file.layout.type: json

# アラート設定
xpack.actions.email:
  host: 'smtp.example.com'
  port: 587
  secure: true
  from: 'kibana-alerts@example.com'

11.1.2 アラートルール(Kibana Alerting)

# Kibana API を使ったアラートルールの作成
curl -X POST "localhost:5601/api/alerting/rule" \
  -H 'kbn-xsrf: true' \
  -H 'Content-Type: application/json' \
  -u "admin:password" -d'
{
  "name": "High Error Rate Alert",
  "rule_type_id": ".es-query",
  "consumer": "alerts",
  "schedule": { "interval": "5m" },
  "params": {
    "index": ["logs-*"],
    "timeField": "@timestamp",
    "esQuery": "{\"bool\":{\"filter\":[{\"term\":{\"log_level\":\"ERROR\"}},{\"range\":{\"@timestamp\":{\"gte\":\"now-5m\"}}}]}}",
    "threshold": [100],
    "thresholdComparator": ">",
    "timeWindowSize": 5,
    "timeWindowUnit": "m",
    "size": 100
  },
  "actions": [
    {
      "group": "query matched",
      "id": "<slack-connector-id>",
      "params": {
        "message": "🚨 High error rate detected: {{context.hits}} errors in the last 5 minutes"
      }
    },
    {
      "group": "query matched",
      "id": "<email-connector-id>",
      "params": {
        "to": ["sre-team@example.com"],
        "subject": "Alert: High Error Rate",
        "message": "Error count: {{context.hits}} in the last 5 minutes.\n\nCheck Kibana dashboard: https://kibana.example.com/dashboard/error-overview"
      }
    }
  ],
  "tags": ["production", "sre", "error-monitoring"]
}'

第12章: クロスクラスタ検索とレプリケーション

12.1 クロスクラスタ検索(CCS)

複数の Elasticsearch クラスタにまたがって検索を実行する機能。

# リモートクラスタの登録
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "cluster": {
      "remote": {
        "cluster_us": {
          "seeds": ["es-us-01:9300", "es-us-02:9300"],
          "transport.compress": true,
          "skip_unavailable": true
        },
        "cluster_eu": {
          "seeds": ["es-eu-01:9300", "es-eu-02:9300"],
          "transport.compress": true,
          "skip_unavailable": true
        },
        "cluster_ap": {
          "seeds": ["es-ap-01:9300", "es-ap-02:9300"],
          "transport.compress": true,
          "skip_unavailable": false
        }
      }
    }
  }
}'

# Proxy モードでのリモートクラスタ登録
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "cluster": {
      "remote": {
        "cluster_us": {
          "mode": "proxy",
          "proxy_address": "es-proxy-us:9443",
          "server_name": "cluster-us.example.com",
          "num_proxy_sockets_per_connection": 18
        }
      }
    }
  }
}'

# リモートクラスタの接続状態確認
curl -X GET "localhost:9200/_remote/info?pretty"

# クロスクラスタ検索の実行
curl -X GET "localhost:9200/logs-*,cluster_us:logs-*,cluster_eu:logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": { "message": "critical error" } }
      ],
      "filter": [
        { "range": { "@timestamp": { "gte": "now-1h" } } }
      ]
    }
  },
  "sort": [{ "@timestamp": "desc" }],
  "size": 50
}'

# クロスクラスタ検索で特定クラスタのインデックスのみ対象
curl -X GET "localhost:9200/cluster_us:logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match_all": {}
  },
  "size": 10
}'

12.2 クロスクラスタレプリケーション(CCR)

リーダーインデックスの変更をフォロワーインデックスに自動的にレプリケーションする機能。

# フォロワークラスタでの設定

# フォロワーインデックスの作成
curl -X PUT "localhost:9200/logs-follower" -H 'Content-Type: application/json' -d'
{
  "remote_cluster": "cluster_us",
  "leader_index": "logs-production"
}'

# 自動フォローパターンの設定
curl -X PUT "localhost:9200/_ccr/auto_follow/logs-pattern" -H 'Content-Type: application/json' -d'
{
  "remote_cluster": "cluster_us",
  "leader_index_patterns": ["logs-*"],
  "follow_index_pattern": "{{leader_index}}-replicated",
  "settings": {
    "index.number_of_replicas": 0
  },
  "max_read_request_operation_count": 5120,
  "max_outstanding_read_requests": 12,
  "max_read_request_size": "32mb",
  "max_write_request_operation_count": 5120,
  "max_write_request_size": "9223372036854775807b",
  "max_outstanding_write_requests": 9,
  "max_write_buffer_count": 2147483647,
  "max_write_buffer_size": "512mb",
  "max_retry_delay": "500ms",
  "read_poll_timeout": "1m"
}'

# CCR の状態確認
curl -X GET "localhost:9200/logs-follower/_ccr/stats?pretty"

# フォロワーインデックスの一時停止
curl -X POST "localhost:9200/logs-follower/_ccr/pause_follow"

# フォロワーインデックスの再開
curl -X POST "localhost:9200/logs-follower/_ccr/resume_follow" -H 'Content-Type: application/json' -d'{}'

# フォロワーを通常のインデックスに昇格(DR時)
curl -X POST "localhost:9200/logs-follower/_ccr/unfollow"
+------------------------------------------------------------------+
|                  クロスクラスタレプリケーション                     |
|                                                                   |
|  リーダークラスタ (US)          フォロワークラスタ (JP)            |
|  +--------------------+        +--------------------+             |
|  | logs-production    |  --->  | logs-production    |             |
|  | (読み書き可能)     |  CCR   | -replicated        |             |
|  +--------------------+        | (読み取り専用)     |             |
|  | metrics-production |  --->  +--------------------+             |
|  | (読み書き可能)     |  CCR   | metrics-production |             |
|  +--------------------+        | -replicated        |             |
|                                | (読み取り専用)     |             |
|  変更はリーダーから             +--------------------+             |
|  フォロワーへ自動同期                                              |
+------------------------------------------------------------------+

第13章: SRE のためのベストプラクティス

13.1 キャパシティプランニング

13.1.1 ストレージ計算

必要ストレージの計算:

1日のデータ量:          100 GB/日
レプリカ係数:            × 2 (プライマリ + 1レプリカ)
インデックスオーバーヘッド: × 1.1 (10%の追加)
保持期間:               × 30日

必要ストレージ = 100 × 2 × 1.1 × 30 = 6,600 GB (≈ 6.4 TB)

安全マージン (20%):      × 1.2
合計必要ストレージ:       ≈ 7.9 TB

ノード数計算:
- 各ノードの利用可能ストレージ: 2 TB
- 必要ノード数: 7.9 TB / 2 TB ≈ 4 ノード(切り上げ)

シャード計算:
- 推奨シャードサイズ: 10-50 GB
- 1日のプライマリデータ: 100 GB
- 推奨シャード数/日: 100 GB / 25 GB = 4 シャード

13.1.2 シャード数の見積もり

# シャードのサイズと数を確認
curl -X GET "localhost:9200/_cat/shards?v&h=index,shard,prirep,store,docs&s=store:desc&pretty" | head -50

# データノードあたりのシャード数を確認
curl -X GET "localhost:9200/_cat/allocation?v&pretty"

# 推奨事項:
# - 1シャードあたり: 10-50 GB
# - データノードあたり: 600シャード以下
# - 1 GBのヒープメモリあたり: 20シャード以下
# - クラスタ全体: シャード数 × 20MB のヒープメモリが必要

13.2 モニタリング戦略

13.2.1 重要なアラート条件

# アラート設定の推奨値

クラスタヘルス:
  - クラスタステータスが Yellow: WARNING
  - クラスタステータスが Red: CRITICAL

ディスク使用量:
  - ノードのディスク使用量 > 75%: WARNING
  - ノードのディスク使用量 > 85%: CRITICAL
  - ノードのディスク使用量 > 90%: EMERGENCY(書き込みブロックの可能性)

JVM ヒープ:
  - ヒープ使用量 > 75%: WARNING
  - ヒープ使用量 > 85%: CRITICAL
  - Old GC 時間 > 5秒/分: WARNING
  - Old GC 時間 > 15秒/分: CRITICAL

CPU:
  - CPU 使用率 > 80%(5分平均): WARNING
  - CPU 使用率 > 95%(5分平均): CRITICAL

検索レイテンシ:
  - P95 検索レイテンシ > 2秒: WARNING
  - P95 検索レイテンシ > 10秒: CRITICAL

インデックスレイテンシ:
  - P95 インデックスレイテンシ > 1秒: WARNING
  - P95 インデックスレイテンシ > 5秒: CRITICAL

スレッドプール:
  - search rejected > 0: WARNING
  - write rejected > 0: WARNING
  - bulk rejected > 0: CRITICAL

シャード:
  - 未割り当てシャード > 0: WARNING(5分以上継続)
  - 初期化中シャード > 0: INFO
  - リロケーティングシャード > 10: WARNING

13.2.2 Prometheus + Grafana でのモニタリング

# elasticsearch-exporter の設定(Prometheus 用)
# docker-compose.yml

version: '3'
services:
  elasticsearch-exporter:
    image: quay.io/prometheuscommunity/elasticsearch-exporter:latest
    command:
      - '--es.uri=https://elasticsearch:9200'
      - '--es.all'
      - '--es.indices'
      - '--es.indices_settings'
      - '--es.indices_mappings'
      - '--es.shards'
      - '--es.snapshots'
      - '--es.timeout=30s'
      - '--es.ssl-skip-verify'
      - '--es.ca=/certs/ca.crt'
    environment:
      ES_USERNAME: monitoring_user
      ES_PASSWORD: ${MONITORING_PASSWORD}
    ports:
      - "9114:9114"

13.3 バックアップ戦略

+------------------------------------------------------------------+
|                    バックアップ戦略                                |
+------------------------------------------------------------------+
| レベル     | 頻度       | 保持期間  | 方法                       |
+------------------------------------------------------------------+
| 日次       | 毎日 01:00 | 30日      | SLM (S3スナップショット)    |
| 週次       | 毎週日曜   | 90日      | SLM (完全スナップショット)   |
| 月次       | 毎月1日    | 1年       | SLM (完全スナップショット)   |
+------------------------------------------------------------------+
| DR テスト  | 四半期     | -         | リストアテストの実施         |
+------------------------------------------------------------------+
# バックアップの検証スクリプト
# #!/bin/bash
# set -e
# 
# ES_HOST="localhost:9200"
# REPO="s3-backup"
# 
# echo "最新のスナップショットを確認..."
# LATEST_SNAPSHOT=$(curl -s "$ES_HOST/_snapshot/$REPO/_all" | \
#   jq -r '.snapshots | sort_by(.start_time) | last | .snapshot')
# 
# echo "最新スナップショット: $LATEST_SNAPSHOT"
# 
# SNAPSHOT_STATE=$(curl -s "$ES_HOST/_snapshot/$REPO/$LATEST_SNAPSHOT" | \
#   jq -r '.snapshots[0].state')
# 
# if [ "$SNAPSHOT_STATE" != "SUCCESS" ]; then
#   echo "ERROR: スナップショットの状態が異常: $SNAPSHOT_STATE"
#   exit 1
# fi
# 
# echo "スナップショットの状態: $SNAPSHOT_STATE"
# 
# # リストアテスト(テスト用インデックスのみ)
# echo "リストアテストを実行..."
# curl -X POST "$ES_HOST/_snapshot/$REPO/$LATEST_SNAPSHOT/_restore" \
#   -H 'Content-Type: application/json' -d'
# {
#   "indices": "logs-2026.04.07",
#   "rename_pattern": "(.+)",
#   "rename_replacement": "restore-test-$1",
#   "index_settings": {
#     "index.number_of_replicas": 0
#   }
# }'
# 
# # リストア完了を待機
# sleep 30
# 
# # リストアされたインデックスのドキュメント数を確認
# DOC_COUNT=$(curl -s "$ES_HOST/restore-test-logs-2026.04.07/_count" | jq '.count')
# echo "リストアされたドキュメント数: $DOC_COUNT"
# 
# # テストインデックスの削除
# curl -X DELETE "$ES_HOST/restore-test-logs-2026.04.07"
# 
# echo "バックアップ検証完了"

13.4 障害対応ランブック

13.4.1 クラスタが Red の場合

# 1. 未割り当てシャードの確認
curl -X GET "localhost:9200/_cat/shards?v&h=index,shard,prirep,state,unassigned.reason" | grep UNASSIGNED

# 2. 割り当て失敗の原因を特定
curl -X GET "localhost:9200/_cluster/allocation/explain?pretty"

# 3. ノードの状態確認
curl -X GET "localhost:9200/_cat/nodes?v&h=name,ip,role,heap.percent,ram.percent,cpu,disk.used_percent"

# 4. ディスクスペースの確認
curl -X GET "localhost:9200/_cat/allocation?v"

# 5. 必要に応じてシャード割り当てをリトライ
curl -X POST "localhost:9200/_cluster/reroute?retry_failed=true"

# 6. 緊急時: 読み取り専用インデックスの解除
curl -X PUT "localhost:9200/_all/_settings" -H 'Content-Type: application/json' -d'
{
  "index.blocks.read_only_allow_delete": null
}'

13.4.2 JVM ヒープ圧迫時

# 1. ヒープ使用量の確認
curl -X GET "localhost:9200/_cat/nodes?v&h=name,heap.percent,heap.max,ram.percent,cpu"

# 2. フィールドデータの使用量確認
curl -X GET "localhost:9200/_cat/fielddata?v&fields=*"

# 3. 大きなフィールドデータのクリア
curl -X POST "localhost:9200/_cache/clear?fielddata=true"

# 4. サーキットブレーカーの状態確認
curl -X GET "localhost:9200/_nodes/stats/breaker?pretty"

# 5. 長時間実行タスクのキャンセル
curl -X GET "localhost:9200/_tasks?actions=*search*&detailed=true&pretty"

13.4.3 インデックスパフォーマンス低下時

# 1. バルクキューの確認
curl -X GET "localhost:9200/_cat/thread_pool/write?v&h=node_name,active,queue,rejected"

# 2. マージの状態確認
curl -X GET "localhost:9200/_cat/nodes?v&h=name,merges.current,merges.total"

# 3. リフレッシュの負荷確認
curl -X GET "localhost:9200/_cat/indices?v&h=index,refresh.total,refresh.time"

# 4. 一時的な対策: リフレッシュインターバルの延長
curl -X PUT "localhost:9200/logs-*/_settings" -H 'Content-Type: application/json' -d'
{
  "index.refresh_interval": "30s"
}'

13.5 セキュリティチェックリスト

SREセキュリティチェックリスト:

[ ] xpack.security.enabled: true が設定されている
[ ] TLS がトランスポート層と HTTP 層の両方で有効
[ ] デフォルトのパスワードが変更されている
[ ] 最小権限の原則に基づいたロールが設定されている
[ ] API キーに有効期限が設定されている
[ ] 監査ログが有効化されている
[ ] ネットワークアクセスが制限されている(ファイアウォール/セキュリティグループ)
[ ] 定期的なセキュリティパッチの適用
[ ] クラスタ間通信が暗号化されている
[ ] Kibana へのアクセスが認証/認可されている
[ ] フィールドレベル/ドキュメントレベルセキュリティが必要に応じて設定されている
[ ] バックアップが暗号化されている
[ ] 定期的なアクセスレビューが実施されている

付録

A. 便利な curl コマンド集

# クラスタ情報
curl -X GET "localhost:9200/?pretty"

# インデックスの作成確認
curl -X HEAD "localhost:9200/my-index"

# ドキュメントの取得
curl -X GET "localhost:9200/my-index/_doc/1?pretty"

# ドキュメントの更新
curl -X POST "localhost:9200/my-index/_update/1" -H 'Content-Type: application/json' -d'
{
  "doc": { "status": "updated" }
}'

# ドキュメントの削除
curl -X DELETE "localhost:9200/my-index/_doc/1"

# クエリによる削除
curl -X POST "localhost:9200/logs-old/_delete_by_query" -H 'Content-Type: application/json' -d'
{
  "query": {
    "range": {
      "@timestamp": { "lt": "now-90d" }
    }
  }
}'

# Reindex API
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
  "source": {
    "index": "logs-old",
    "query": {
      "range": {
        "@timestamp": { "gte": "2026-04-01" }
      }
    }
  },
  "dest": {
    "index": "logs-new",
    "pipeline": "logs-pipeline"
  }
}'

# Update by Query
curl -X POST "localhost:9200/logs-*/_update_by_query" -H 'Content-Type: application/json' -d'
{
  "query": {
    "term": { "service_name": "old-service" }
  },
  "script": {
    "source": "ctx._source.service_name = params.new_name",
    "params": {
      "new_name": "new-service"
    }
  }
}'

B. elasticsearch.yml 完全設定例

# ============================================================
# Elasticsearch プロダクション設定例
# ============================================================

# --- クラスタ設定 ---
cluster.name: production-cluster
node.name: ${HOSTNAME}
node.roles: [ data_hot, data_content ]

# --- ネットワーク設定 ---
network.host: 0.0.0.0
http.port: 9200
transport.port: 9300

# --- ディスカバリ設定 ---
discovery.seed_hosts:
  - "master-01:9300"
  - "master-02:9300"
  - "master-03:9300"
cluster.initial_master_nodes:
  - "master-01"
  - "master-02"
  - "master-03"

# --- パス設定 ---
path.data: /data/elasticsearch
path.logs: /var/log/elasticsearch

# --- メモリ設定 ---
bootstrap.memory_lock: true

# --- セキュリティ設定 ---
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: full
xpack.security.transport.ssl.keystore.path: certs/transport.p12
xpack.security.transport.ssl.truststore.path: certs/transport.p12
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: certs/http.p12

# --- モニタリング設定 ---
xpack.monitoring.collection.enabled: true
xpack.monitoring.elasticsearch.collection.enabled: true

# --- その他 ---
action.destructive_requires_name: true
indices.query.bool.max_clause_count: 4096

# --- ノード属性 ---
node.attr.data: hot
node.attr.rack: rack-1

本ドキュメントは SRE エンジニアのための Elasticsearch 包括的リファレンスガイドです。 内容は Elasticsearch 8.x 系に基づいています。 最終更新: 2026年4月8日