Elasticsearch
2024年9月にAGPLv3ライセンスでオープンソース回帰した検索・分析エンジン。PyTorch機械学習統合、ベクトル検索機能強化、Lucene 9による性能向上が特徴。
監視サーバー
Elasticsearch
概要
Elasticsearchは2024年9月にAGPLv3ライセンスでオープンソース回帰した検索・分析エンジンです。PyTorch機械学習統合、ベクトル検索機能強化、Lucene 9による性能向上が特徴で、ELKスタック(Elasticsearch、Logstash、Kibana)の中核コンポーネントとして継続成長しています。AI時代に対応した次世代検索プラットフォームとして進化を続けています。
詳細
Elasticsearchは2010年にShay Banonによって開発が開始され、2024年9月にオープンソース回帰で注目を集めています。PyTorch機械学習統合、ベクトル検索機能強化でAI時代に対応し、ELKスタックの中核コンポーネントとして継続成長を続けています。バージョン8.16.0+では更なる機能強化が図られ、分散リアルタイム検索の新時代を牽引しています。
主要な技術的特徴
- 分散アーキテクチャ: 水平スケーリングとハイアベイラビリティ
- RESTful API: HTTP/JSONベースの統一インターフェース
- リアルタイム検索: near real-time検索とインデックス機能
- 豊富なクエリDSL: 複雑な検索条件の柔軟な表現
- アグリゲーション: 高度なデータ分析と集計機能
用途
- ログ分析とモニタリング
- 全文検索システム
- ビジネス分析とダッシュボード
- セキュリティ情報・イベント管理(SIEM)
- アプリケーション性能監視(APM)
メリット・デメリット
メリット
- 高性能検索: 大規模データでの高速検索機能
- スケーラビリティ: 水平分散による高いスケーラビリティ
- 豊富なエコシステム: Kibana、Logstash等との統合
- リアルタイム分析: 即座のデータ分析と可視化
- 柔軟なスキーマ: 動的マッピングによる柔軟なデータ構造
- オープンソース回帰: 2024年9月からAGPLv3で利用可能
デメリット
- メモリ消費: 大量のヒープメモリが必要
- 複雑な運用: クラスター管理の複雑さ
- データ永続性: プライマリストレージとしての制限
- ライセンス複雑性: 複数ライセンス体系の存在
- パフォーマンスチューニング: 最適化に専門知識が必要
参考ページ
書き方の例
基本的なElasticsearch設定
# elasticsearch.yml
cluster.name: my-application
node.name: node-1
network.host: 0.0.0.0
http.port: 9200
transport.port: 9300
# パス設定
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
# メモリ設定
bootstrap.memory_lock: true
# ディスカバリー設定
discovery.seed_hosts: ["host1", "host2", "host3"]
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]
# セキュリティ設定
xpack.security.enabled: true
xpack.security.http.ssl:
enabled: true
certificate: elastic-certificates.p12
key: elastic-certificates.p12
xpack.security.transport.ssl:
enabled: true
verification_mode: certificate
certificate: elastic-certificates.p12
key: elastic-certificates.p12
# 監視設定
xpack.monitoring.collection.enabled: true
# 機械学習設定
xpack.ml.enabled: true
# インデックス設定
action.destructive_requires_name: true
action.auto_create_index: .monitoring*,.watches,.triggered_watches,.watcher-history*,.ml*
# ネットワーク設定
http.compression: true
http.cors.enabled: true
http.cors.allow-origin: "*"
# パフォーマンス設定
indices.memory.index_buffer_size: 10%
indices.memory.min_index_buffer_size: 48mb
thread_pool.write.queue_size: 1000
インデックステンプレート設定
{
"name": "logs-template",
"index_patterns": ["logs-*"],
"template": {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"index.refresh_interval": "10s",
"index.max_result_window": 10000,
"analysis": {
"analyzer": {
"custom_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "stop"]
}
}
}
},
"mappings": {
"properties": {
"@timestamp": {
"type": "date",
"format": "date_optional_time||epoch_millis"
},
"level": {
"type": "keyword"
},
"message": {
"type": "text",
"analyzer": "custom_analyzer",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"service": {
"type": "keyword"
},
"host": {
"properties": {
"name": {
"type": "keyword"
},
"ip": {
"type": "ip"
}
}
},
"response_time": {
"type": "long"
},
"status_code": {
"type": "integer"
},
"user_agent": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"geoip": {
"properties": {
"location": {
"type": "geo_point"
},
"country_name": {
"type": "keyword"
},
"city_name": {
"type": "keyword"
}
}
}
}
}
},
"priority": 500,
"version": 1,
"_meta": {
"description": "Template for application logs"
}
}
検索クエリの例
{
"query": {
"bool": {
"must": [
{
"range": {
"@timestamp": {
"gte": "now-24h",
"lte": "now"
}
}
},
{
"term": {
"level": "ERROR"
}
}
],
"filter": [
{
"terms": {
"service": ["web", "api", "auth"]
}
}
],
"should": [
{
"match": {
"message": "exception"
}
},
{
"match": {
"message": "error"
}
}
],
"minimum_should_match": 1
}
},
"sort": [
{
"@timestamp": {
"order": "desc"
}
}
],
"size": 100,
"highlight": {
"fields": {
"message": {
"pre_tags": ["<mark>"],
"post_tags": ["</mark>"],
"fragment_size": 200
}
}
},
"_source": {
"includes": ["@timestamp", "level", "message", "service", "host.name"],
"excludes": ["user_agent"]
}
}
アグリゲーションクエリの例
{
"size": 0,
"query": {
"range": {
"@timestamp": {
"gte": "now-1d",
"lte": "now"
}
}
},
"aggs": {
"error_trends": {
"date_histogram": {
"field": "@timestamp",
"calendar_interval": "1h",
"time_zone": "Asia/Tokyo"
},
"aggs": {
"error_count": {
"filter": {
"term": {
"level": "ERROR"
}
}
},
"warning_count": {
"filter": {
"term": {
"level": "WARNING"
}
}
}
}
},
"top_services": {
"terms": {
"field": "service",
"size": 10
},
"aggs": {
"avg_response_time": {
"avg": {
"field": "response_time"
}
},
"error_rate": {
"bucket_script": {
"buckets_path": {
"errors": "error_count",
"total": "_count"
},
"script": "params.errors / params.total * 100"
}
},
"error_count": {
"filter": {
"term": {
"level": "ERROR"
}
}
}
}
},
"status_code_distribution": {
"terms": {
"field": "status_code",
"size": 20
}
},
"geographic_distribution": {
"geo_distance": {
"field": "geoip.location",
"origin": "35.6762, 139.6503",
"ranges": [
{
"to": 100,
"key": "Within 100km"
},
{
"from": 100,
"to": 500,
"key": "100-500km"
},
{
"from": 500,
"key": "Over 500km"
}
]
}
}
}
}
マッピング定義の例
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "standard",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
},
"suggest": {
"type": "completion"
}
}
},
"content": {
"type": "text",
"analyzer": "custom_html_analyzer",
"search_analyzer": "standard"
},
"tags": {
"type": "keyword"
},
"published_date": {
"type": "date",
"format": "yyyy-MM-dd||yyyy-MM-dd'T'HH:mm:ss||epoch_millis"
},
"author": {
"type": "nested",
"properties": {
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"email": {
"type": "keyword"
},
"bio": {
"type": "text"
}
}
},
"metadata": {
"type": "object",
"enabled": false
},
"view_count": {
"type": "integer"
},
"rating": {
"type": "float"
},
"location": {
"type": "geo_point"
},
"ip_address": {
"type": "ip"
},
"binary_data": {
"type": "binary"
}
}
},
"settings": {
"analysis": {
"analyzer": {
"custom_html_analyzer": {
"type": "custom",
"tokenizer": "standard",
"char_filter": ["html_strip"],
"filter": [
"lowercase",
"asciifolding",
"stop"
]
}
}
}
}
}
インデックスライフサイクル管理(ILM)
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_size": "10GB",
"max_docs": 10000000,
"max_age": "1d"
},
"set_priority": {
"priority": 100
}
}
},
"warm": {
"min_age": "7d",
"actions": {
"set_priority": {
"priority": 50
},
"allocate": {
"number_of_replicas": 0
},
"shrink": {
"number_of_shards": 1
},
"forcemerge": {
"max_num_segments": 1
}
}
},
"cold": {
"min_age": "30d",
"actions": {
"set_priority": {
"priority": 0
},
"allocate": {
"number_of_replicas": 0,
"require": {
"box_type": "cold"
}
}
}
},
"delete": {
"min_age": "90d",
"actions": {
"delete": {}
}
}
}
}
}
Ingest Pipeline設定
{
"description": "Log processing pipeline",
"processors": [
{
"grok": {
"field": "message",
"patterns": [
"%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} \\[%{DATA:thread}\\] %{DATA:logger} - %{GREEDYDATA:log_message}"
]
}
},
{
"date": {
"field": "timestamp",
"formats": ["ISO8601"]
}
},
{
"geoip": {
"field": "client_ip",
"target_field": "geoip",
"database_file": "GeoLite2-City.mmdb"
}
},
{
"user_agent": {
"field": "user_agent",
"target_field": "user_agent_parsed"
}
},
{
"script": {
"lang": "painless",
"source": """
if (ctx.status_code >= 400) {
ctx.error_flag = true;
} else {
ctx.error_flag = false;
}
"""
}
},
{
"remove": {
"field": ["timestamp", "message"]
}
}
],
"on_failure": [
{
"set": {
"field": "error.message",
"value": "{{ _ingest.on_failure_message }}"
}
}
]
}
Docker Compose設定
version: '3.8'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.16.0
container_name: elasticsearch
restart: unless-stopped
environment:
- node.name=es01
- cluster.name=es-docker-cluster
- discovery.type=single-node
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
- xpack.security.enabled=false
- xpack.security.http.ssl.enabled=false
- xpack.security.transport.ssl.enabled=false
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- elasticsearch-data:/usr/share/elasticsearch/data
- ./elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
ports:
- "9200:9200"
- "9300:9300"
networks:
- elastic
kibana:
image: docker.elastic.co/kibana/kibana:8.16.0
container_name: kibana
restart: unless-stopped
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
- xpack.security.enabled=false
ports:
- "5601:5601"
depends_on:
- elasticsearch
networks:
- elastic
logstash:
image: docker.elastic.co/logstash/logstash:8.16.0
container_name: logstash
restart: unless-stopped
volumes:
- ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
- ./logstash.yml:/usr/share/logstash/config/logstash.yml
ports:
- "5044:5044"
- "9600:9600"
environment:
- "LS_JAVA_OPTS=-Xmx1g -Xms1g"
depends_on:
- elasticsearch
networks:
- elastic
filebeat:
image: docker.elastic.co/beats/filebeat:8.16.0
container_name: filebeat
restart: unless-stopped
user: root
volumes:
- ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
depends_on:
- elasticsearch
networks:
- elastic
volumes:
elasticsearch-data:
driver: local
networks:
elastic:
driver: bridge
パフォーマンスチューニング設定
# elasticsearch.yml
# クラスター設定
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.memory.index_buffer_size: 10%
indices.memory.min_index_buffer_size: 48mb
indices.memory.max_index_buffer_size: unbounded
# 検索設定
search.max_buckets: 65536
search.allow_expensive_queries: false
# スレッドプール設定
thread_pool:
write:
queue_size: 1000
search:
queue_size: 1000
get:
queue_size: 1000
# HTTPクライアント設定
http.max_content_length: 100mb
http.max_chunk_size: 8kb
http.max_header_size: 8kb
# ディスカバリー設定
discovery.zen.minimum_master_nodes: 2
discovery.zen.ping.timeout: 3s
discovery.zen.fd.ping_timeout: 30s
discovery.zen.fd.ping_retries: 3
JavaクライアントAPIの例
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
public class ElasticsearchExample {
public static void main(String[] args) throws Exception {
// クライアント作成
RestClient restClient = RestClient.builder(
new HttpHost("localhost", 9200)).build();
ElasticsearchTransport transport = new RestClientTransport(
restClient, new JacksonJsonpMapper());
ElasticsearchClient client = new ElasticsearchClient(transport);
// ドキュメントのインデックス
IndexRequest<Object> indexRequest = IndexRequest.of(i -> i
.index("products")
.id("1")
.document(Map.of(
"name", "Laptop",
"price", 999.99,
"description", "High performance laptop"
))
);
IndexResponse indexResponse = client.index(indexRequest);
System.out.println("Document indexed: " + indexResponse.id());
// 検索
SearchRequest searchRequest = SearchRequest.of(s -> s
.index("products")
.query(q -> q
.match(t -> t
.field("name")
.query("laptop")
)
)
);
SearchResponse<Object> searchResponse = client.search(searchRequest, Object.class);
for (Hit<Object> hit : searchResponse.hits().hits()) {
System.out.println("Found: " + hit.source());
}
// アグリゲーション
SearchRequest aggRequest = SearchRequest.of(s -> s
.index("products")
.size(0)
.aggregations("price_stats", a -> a
.stats(st -> st.field("price"))
)
);
SearchResponse<Object> aggResponse = client.search(aggRequest, Object.class);
System.out.println("Price stats: " + aggResponse.aggregations());
// リソースのクリーンアップ
transport.close();
restClient.close();
}
}