Elasticsearch 간편 가이드: 필수 명령어 및 팁

검색, 인덱싱 및 분석을 위한 Elasticsearch 명령어

Page content

Elasticsearch은 Apache Lucene 기반의 강력한 분산 검색 및 분석 엔진입니다.
이 포괄적인 체크리스트는 Elasticsearch 클러스터와 함께 작업할 때 필수 명령어, 최선의 실천 방법, 그리고 빠른 참조를 다룹니다.

elasticsearch

참고: 이 가이드의 대부분 예제는 HTTP 요청에 cURL을 사용합니다. cURL에 익숙하지 않거나 고급 옵션에 대한 빠른 참조가 필요하다면, cURL 체크리스트에서 자세한 명령줄 HTTP 요청 기술을 확인해 보세요.

클러스터 관리

클러스터 상태 확인

이 섹션의 모든 명령어는 Elasticsearch의 REST API와 상호작용하기 위해 cURL을 사용합니다. 필요에 따라 추가 헤더, 인증, 기타 옵션으로 이러한 요청을 맞춤화할 수 있습니다.

# 기본 상태 확인
curl -X GET "localhost:9200/_cluster/health?pretty"

# 세부 클러스터 상태 및 샤드 정보 확인
curl -X GET "localhost:9200/_cluster/health?level=shards&pretty"

# 노드 정보 확인
curl -X GET "localhost:9200/_cat/nodes?v"

# 클러스터 설정 확인
curl -X GET "localhost:9200/_cluster/settings?pretty"

노드 작업

# 모든 노드 목록 보기
curl -X GET "localhost:9200/_cat/nodes?v&h=name,node.role,heap.percent,ram.percent,cpu,load_1m"

# 노드 통계
curl -X GET "localhost:9200/_nodes/stats?pretty"

# 핫 스레드 (문제 해결)
curl -X GET "localhost:9200/_nodes/hot_threads"

인덱스 관리

인덱스 생성 및 삭제

# 인덱스 생성
curl -X PUT "localhost:9200/my_index?pretty"

# 설정을 포함한 인덱스 생성
curl -X PUT "localhost:9200/my_index" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  }
}
'

# 인덱스 삭제
curl -X DELETE "localhost:9200/my_index?pretty"

# 모든 인덱스 목록 보기
curl -X GET "localhost:9200/_cat/indices?v"

# 인덱스 통계
curl -X GET "localhost:9200/my_index/_stats?pretty"

인덱스 매핑

# 매핑 정의
curl -X PUT "localhost:9200/products" -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "properties": {
      "name": { "type": "text" },
      "price": { "type": "float" },
      "created_at": { "type": "date" },
      "tags": { "type": "keyword" },
      "description": { 
        "type": "text",
        "analyzer": "english"
      }
    }
  }
}
'

# 매핑 확인
curl -X GET "localhost:9200/products/_mapping?pretty"

# 매핑 업데이트 (필드 추가)
curl -X PUT "localhost:9200/products/_mapping" -H 'Content-Type: application/json' -d'
{
  "properties": {
    "category": { "type": "keyword" }
  }
}
'

인덱스 템플릿

# 인덱스 템플릿 생성
curl -X PUT "localhost:9200/_index_template/logs_template" -H 'Content-Type: application/json' -d'
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 1
    },
    "mappings": {
      "properties": {
        "timestamp": { "type": "date" },
        "message": { "type": "text" },
        "level": { "type": "keyword" }
      }
    }
  }
}
'

# 템플릿 목록 보기
curl -X GET "localhost:9200/_index_template?pretty"

문서 작업 (CRUD)

문서 생성

# 자동 생성된 ID로 문서 인덱싱
curl -X POST "localhost:9200/products/_doc?pretty" -H 'Content-Type: application/json' -d'
{
  "name": "Laptop",
  "price": 999.99,
  "tags": ["electronics", "computers"]
}
'

# 특정 ID로 문서 인덱싱
curl -X PUT "localhost:9200/products/_doc/1?pretty" -H 'Content-Type: application/json' -d'
{
  "name": "Laptop",
  "price": 999.99
}
'

# 대량 인덱싱
curl -X POST "localhost:9200/_bulk?pretty" -H 'Content-Type: application/json' -d'
{ "index": { "_index": "products", "_id": "1" }}
{ "name": "Laptop", "price": 999.99 }
{ "index": { "_index": "products", "_id": "2" }}
{ "name": "Mouse", "price": 29.99 }
'

문서 읽기

# ID로 문서 가져오기
curl -X GET "localhost:9200/products/_doc/1?pretty"

# 여러 문서 가져오기
curl -X GET "localhost:9200/_mget?pretty" -H 'Content-Type: application/json' -d'
{
  "docs": [
    { "_index": "products", "_id": "1" },
    { "_index": "products", "_id": "2" }
  ]
}
'

# 문서 존재 여부 확인
curl -I "localhost:9200/products/_doc/1"

문서 업데이트

# 문서 업데이트
curl -X POST "localhost:9200/products/_update/1?pretty" -H 'Content-Type: application/json' -d'
{
  "doc": {
    "price": 899.99
  }
}
'

# 스크립트로 업데이트
curl -X POST "localhost:9200/products/_update/1?pretty" -H 'Content-Type: application/json' -d'
{
  "script": {
    "source": "ctx._source.price *= params.discount",
    "params": {
      "discount": 0.9
    }
  }
}
'

# 업데이트 또는 삽입 (업데이트 또는 삽입)
curl -X POST "localhost:9200/products/_update/1?pretty" -H 'Content-Type: application/json' -d'
{
  "doc": {
    "price": 899.99
  },
  "doc_as_upsert": true
}
'

문서 삭제

# ID로 삭제
curl -X DELETE "localhost:9200/products/_doc/1?pretty"

# 쿼리로 삭제
curl -X POST "localhost:9200/products/_delete_by_query?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "name": "old"
    }
  }
}
'

검색 쿼리

기본 검색

# 모든 문서 검색
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match_all": {}
  }
}
'

# 매치 쿼리
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "name": "laptop"
    }
  }
}
'

# 멀티 매치 쿼리
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "multi_match": {
      "query": "laptop gaming",
      "fields": ["name", "description"]
    }
  }
}
'

텀 레벨 쿼리

# 텀 쿼리 (정확한 일치)
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "term": {
      "tags": "electronics"
    }
  }
}
'

# 텀스 쿼리 (여러 값)
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "terms": {
      "tags": ["electronics", "computers"]
    }
  }
}
'

# 범위 쿼리
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "range": {
      "price": {
        "gte": 100,
        "lte": 1000
      }
    }
  }
}
'

# 존재 쿼리
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "exists": {
      "field": "description"
    }
  }
}
'

불린 쿼리

# 불린 쿼리 (must, should, must_not, filter)
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "laptop" }}
      ],
      "filter": [
        { "range": { "price": { "gte": 500 }}}
      ],
      "should": [
        { "term": { "tags": "gaming" }}
      ],
      "must_not": [
        { "term": { "tags": "refurbished" }}
      ]
    }
  }
}
'

고급 검색

# 와일드카드 쿼리
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "wildcard": {
      "name": "lap*"
    }
  }
}
'

# 퍼지 쿼리 (오타 허용)
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "fuzzy": {
      "name": {
        "value": "laptpo",
        "fuzziness": "AUTO"
      }
    }
  }
}
'

# 프리픽스 쿼리
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "prefix": {
      "name": "lap"
    }
  }
}
'

집계

메트릭 집계

# 평균, 합, 최소, 최대
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "avg_price": { "avg": { "field": "price" }},
    "max_price": { "max": { "field": "price" }},
    "min_price": { "min": { "field": "price" }},
    "total_sales": { "sum": { "field": "price" }}
  }
}
'

# 통계 집계
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "price_stats": {
      "stats": { "field": "price" }
    }
  }
}
'

버킷 집계

# 텀 집계 (그룹화)
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "popular_tags": {
      "terms": {
        "field": "tags",
        "size": 10
      }
    }
  }
}
'

# 범위 집계
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 50 },
          { "from": 50, "to": 100 },
          { "from": 100 }
        ]
      }
    }
  }
}
'

# 날짜 히스토그램
curl -X GET "localhost:9200/logs/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "logs_over_time": {
      "date_histogram": {
        "field": "timestamp",
        "calendar_interval": "day"
      }
    }
  }
}
'

중첩 집계

# 중첩 집계
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "categories": {
      "terms": { "field": "category" },
      "aggs": {
        "avg_price": {
          "avg": { "field": "price" }
        }
      }
    }
  }
}
'

정렬 및 페이지네이션

# 필드로 정렬
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "sort": [
    { "price": { "order": "desc" }},
    { "_score": { "order": "desc" }}
  ]
}
'

# from/size로 페이지네이션
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "from": 0,
  "size": 10,
  "query": { "match_all": {} }
}
'

# 깊은 페이지네이션을 위한 search_after
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 10,
  "query": { "match_all": {} },
  "sort": [{ "price": "asc" }, { "_id": "asc" }],
  "search_after": [100, "product_123"]
}
'

필드 선택 및 하이라이팅

# 특정 필드 선택
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "_source": ["name", "price"]
}
'

# 하이라이팅
curl -X GET "localhost:9200/products/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": { "description": "gaming laptop" }
  },
  "highlight": {
    "fields": {
      "description": {}
    }
  }
}
'

인덱스 별칭

# 별칭 생성
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
  "actions": [
    { "add": { "index": "products_v1", "alias": "products" }}
  ]
}
'

# 새 인덱스로 별칭 전환 (중단 없는 전환)
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
  "actions": [
    { "remove": { "index": "products_v1", "alias": "products" }},
    { "add": { "index": "products_v2", "alias": "products" }}
  ]
}
'

# 별칭 목록 보기
curl -X GET "localhost:9200/_cat/aliases?v"

재인덱싱

# 하나의 인덱스에서 다른 인덱스로 재인덱싱
curl -X POST "localhost:9200/_reindex?pretty" -H 'Content-Type: application/json' -d'
{
  "source": {
    "index": "old_products"
  },
  "dest": {
    "index": "new_products"
  }
}
'

# 쿼리와 함께 재인덱싱
curl -X POST "localhost:9200/_reindex?pretty" -H 'Content-Type: application/json' -d'
{
  "source": {
    "index": "products",
    "query": {
      "range": {
        "price": { "gte": 100 }
      }
    }
  },
  "dest": {
    "index": "expensive_products"
  }
}
'

스냅샷 및 백업

# 스냅샷 저장소 등록
curl -X PUT "localhost:9200/_snapshot/my_backup?pretty" -H 'Content-Type: application/json' -d'
{
  "type": "fs",
  "settings": {
    "location": "/mount/backups/my_backup"
  }
}
'

# 스냅샷 생성
curl -X PUT "localhost:9200/_snapshot/my_backup/snapshot_1?wait_for_completion=true&pretty"

# 스냅샷 복원
curl -X POST "localhost:9200/_snapshot/my_backup/snapshot_1/_restore?pretty"

# 스냅샷 목록 보기
curl -X GET "localhost:9200/_snapshot/my_backup/_all?pretty"

# 스냅샷 삭제
curl -X DELETE "localhost:9200/_snapshot/my_backup/snapshot_1?pretty"

성능 최적화

인덱스 설정

# 대량 인덱싱 중 refresh 비활성화
curl -X PUT "localhost:9200/products/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "refresh_interval": "-1"
  }
}
'

# 대량 인덱싱 후 refresh 다시 활성화
curl -X PUT "localhost:9200/products/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "refresh_interval": "1s"
  }
}
'

# 강제 병합 (최적화)
curl -X POST "localhost:9200/products/_forcemerge?max_num_segments=1&pretty"

캐시 정리

# 모든 캐시 정리
curl -X POST "localhost:9200/_cache/clear?pretty"

# 특정 캐시 정리
curl -X POST "localhost:9200/products/_cache/clear?query=true&pretty"

모니터링 및 문제 해결

# 대기 중인 작업
curl -X GET "localhost:9200/_cat/pending_tasks?v"

# 스레드 풀 통계
curl -X GET "localhost:9200/_cat/thread_pool?v"

# 세그먼트 정보
curl -X GET "localhost:9200/_cat/segments?v"

# 복구 정보
curl -X GET "localhost:9200/_cat/recovery?v&h=index,stage,time"

# 작업 API
curl -X GET "localhost:9200/_tasks?detailed=true&pretty"

Python 클라이언트 예제

from elasticsearch import Elasticsearch

# Elasticsearch에 연결
es = Elasticsearch(['http://localhost:9200'])

# 문서 인덱싱
doc = {
    'name': 'Laptop',
    'price': 999.99,
    'tags': ['electronics']
}
es.index(index='products', id=1, document=doc)

# 검색
resp = es.search(index='products', query={'match': {'name': 'laptop'}})
for hit in resp['hits']['hits']:
    print(hit['_source'])

# 대량 인덱싱
from elasticsearch.helpers import bulk

actions = [
    {
        '_index': 'products',
        '_id': i,
        '_source': {'name': f'Product {i}', 'price': i * 10}
    }
    for i in range(1000)
]
bulk(es, actions)

JavaScript/Node.js 클라이언트 예제

Elasticsearch JavaScript 클라이언트는 클러스터와 상호작용하는 데 안전한 타입을 제공합니다. 프로덕션 애플리케이션에서는 타입 안전성과 자동 완성 기능을 위해 TypeScript를 사용하는 것이 좋습니다. TypeScript 체크리스트에서 타입 정의 및 인터페이스에 대한 최선의 실천 방법을 확인해 보세요.

const { Client } = require('@elastic/elasticsearch');
const client = new Client({ node: 'http://localhost:9200' });

// 문서 인덱싱
async function indexDoc() {
  await client.index({
    index: 'products',
    id: 1,
    document: {
      name: 'Laptop',
      price: 999.99
    }
  });
}

// 검색
async function search() {
  const result = await client.search({
    index: 'products',
    query: {
      match: { name: 'laptop' }
    }
  });
  console.log(result.hits.hits);
}

// 대량 인덱싱
async function bulkIndex() {
  const operations = [];
  for (let i = 0; i < 1000; i++) {
    operations.push({ index: { _index: 'products', _id: i } });
    operations.push({ name: `Product ${i}`, price: i * 10 });
  }
  await client.bulk({ operations });
}

강력한 타이핑을 위한 TypeScript 예제

import { Client } from '@elastic/elasticsearch';

interface Product {
  name: string;
  price: number;
  tags?: string[];
  created_at?: Date;
}

const client = new Client({ node: 'http://localhost:9200' });

async function indexProduct(product: Product, id: number): Promise<void> {
  await client.index<Product>({
    index: 'products',
    id: id.toString(),
    document: product
  });
}

async function searchProducts(query: string): Promise<Product[]> {
  const result = await client.search<Product>({
    index: 'products',
    query: {
      match: { name: query }
    }
  });
  
  return result.hits.hits.map(hit => hit._source as Product);
}

최선의 실천 방법

인덱스 설계

  • 최적의 성능을 위해 샤드 크기를 20~50GB 사이로 유지하세요.
  • 시계열 데이터에 대해 인덱스 라이프사이클 관리(ILM)를 사용하세요.
  • 데이터 인덱싱 전에 매핑을 신중하게 설계하세요.
  • 적절한 필드 타입을 사용하세요 (keyword vs text, 날짜 형식 등).
  • 필요하지 않다면 대규모 문서에 대해 _source를 비활성화하세요.

쿼리 최적화

  • 점수 계산이 필요하지 않다면 필터 대신 쿼리를 사용하세요.
  • 구조화된 데이터에 대해 텀 레벨 쿼리를 선호하세요.
  • bool 쿼리를 사용하여 여러 조건을 효율적으로 결합하세요.
  • 깊은 페이지네이션을 위해 search_after를 사용하세요.
  • 자주 사용되는 필터를 캐시하세요.

인덱싱 성능

  • 배치 인덱싱을 위해 bulk API를 사용하세요 (요청당 1000~5000 문서).
  • 대량 인덱싱 중 refresh를 비활성화하세요.
  • 대량 인덱싱 중 index.refresh_interval을 증가시켜 주세요.
  • 병렬 인덱싱을 위해 여러 스레드/워커를 사용하세요.
  • 샤드 분배를 위해 라우팅을 고려하세요.

클러스터 관리

  • 클러스터 상태를 정기적으로 모니터링하세요.
  • 적절한 복제 구성 설정하세요.
  • 대규모 클러스터에 대해 전용 마스터 노드를 사용하세요.
  • 스냅샷을 사용하여 적절한 백업 전략을 구현하세요.
  • JVM 힙 사용량을 모니터링하세요 (75% 미만 유지).

보안

  • 인증 및 권한 부여(X-Pack 보안)를 활성화하세요.
  • 프로덕션 배포에 HTTPS를 사용하세요 (cURL에 --cacert, --cert, --key 옵션으로 SSL/TLS 구성).
  • 적절한 역할 기반 접근 제어를 구현하세요.
  • 정기적인 보안 업데이트 및 패치를 수행하세요.
  • 데이터를 저장 및 전송 중에 암호화하세요.

일반적인 사용 사례

전체 텍스트 검색

Elasticsearch는 다음과 같은 기능으로 전체 텍스트 검색에 탁월합니다:

  • 관련성 점수
  • 퍼지 매칭
  • 문장 매칭
  • 동의어 처리
  • 다국어 지원

로그 분석 (ELK 스택)

  • 로그스태시/파일비트로 로그 수집
  • Elasticsearch에 로그 인덱싱 및 검색
  • Kibana 대시보드로 시각화
  • 이상 징후에 대한 경고 설정

전자상거래 검색

  • 제품 카탈로그 검색
  • 집계를 통한 편리한 탐색
  • 자동 완성 및 제안
  • 개인화된 검색 결과

애플리케이션 성능 모니터링

  • 애플리케이션 메트릭 인덱싱
  • 실시간 모니터링 대시보드
  • 이상 탐지
  • 성능 추세 분석

유용한 링크

공식 Elasticsearch 자료

관련 체크리스트 및 가이드