학습 목표
aggregation이 검색 hits와 어떻게 다른지, bucket·metric 집계를 구분할 수 있다.
terms·date_histogram으로 로그 건수·시계열 집계 쿼리를 작성할 수 있다.
query + aggs 조합과 keyword·date mapping 요구를 설명할 수 있다.
node·primary shard·replica·routing으로 document가 어느 shard에 가는지 설명할 수 있다.
샤드 수·replica 설계가 용량·가용성·집계 비용에 미치는 영향을 개요 수준에서 설명할 수 있다.
문제 상황
- 4편
size: 0없이 ERROR 로그 10만 건을 Kibana에 뿌렸더니 브라우저 멈춤 — 집계가 필요 message필드로 terms 집계했더니Text fields are not optimised— keyword 필드만service별 top 10만 보고 싶은데 수백 개 bucket —size·order미설정- 1분 단위 그래프가 필요한데
terms만 씀 — date_histogram on@timestamp - index primary 1인데 노드 3대 — shard가 1개라 CPU 한 코어에 몰림
- replica 없이 노드 1대 장애 — index red, 검색 불가
- document
_id만 알고 어느 shard인지 모름 — routing·_shard디버깅 필요
앞 편에서 Query DSL로 document를 골랐다. 이번 편은 요약·시계열(aggregation) 과 분산 저장 구조(cluster·shard). Beats 수집은 6편, ILM·샤드 사이징 심화는 13·22편이다.
1. Aggregation이란?
aggregation(집계) — 조건에 맞는 document들을 그룹하거나 통계를 낸다.

| query | aggregation | |
|---|---|---|
| 반환 | hits (document 목록) | buckets·metrics |
| 용도 | tail·상세 조회 | 건수·분포·시계열 |
| Kibana | Discover 행 목록 | Lens 막대·시간 차트 |
POST /app-logs/_search
{
"size": 0,
"query": { "bool": { "filter": [ ... ] } },
"aggs": { ... }
}
size: 0— hits 생략, aggs 결과만 (대시보드·알림에 흔함)size: 10+ aggs — 상위 10건 와 집계 동시 가능- 집계는 보통 filter context와 같이 — query로 범위 좁힌 뒤 aggs
Bucket vs Metric
| 유형 | 예 | 결과 |
|---|---|---|
| Bucket | terms, date_histogram, filters |
그룹 (key + doc_count) |
| Metric | avg, sum, max, cardinality |
숫자 하나 |
| Pipeline | derivative, moving_avg |
다른 agg 위 계산 |
- sub-aggregation — bucket 안에 metric·bucket 중첩
- 3편 — 집계·정렬은 keyword·date·numeric (doc values)
2. terms 집계
keyword 필드 값별 건수.

// code/log-aggregations.json (발췌)
{
"size": 0,
"query": {
"bool": {
"filter": [
{ "range": { "@timestamp": { "gte": "now-24h" } } }
]
}
},
"aggs": {
"by_level": {
"terms": {
"field": "level",
"size": 10,
"order": { "_count": "desc" }
}
},
"by_service": {
"terms": {
"field": "service",
"size": 20
}
}
}
}
| 옵션 | 역할 |
|---|---|
| field | keyword (또는 keyword 서브필드) |
| size | 반환 bucket 상한 (기본 10) |
| order | _count·_key 정렬 |
| min_doc_count | 0건 bucket 제외 |
level·service— 3편 mapping keywordmessageterms — 불가 (text) —message.keyword도 보통 비권장 (카디널리티 폭발)- cardinality agg — 고유 trace_id 수 (근사 HLL)
sub-aggregation 예
"aggs": {
"by_service": {
"terms": { "field": "service", "size": 10 },
"aggs": {
"error_count": {
"filter": { "term": { "level": "ERROR" } }
}
}
}
}
- 서비스별 전체 + ERROR만 doc_count
3. date_histogram
시간 축 버킷 — 로그 시계열의 기본.
"aggs": {
"errors_over_time": {
"date_histogram": {
"field": "@timestamp",
"fixed_interval": "1m",
"min_doc_count": 0
},
"aggs": {
"by_level": {
"terms": { "field": "level", "size": 5 }
}
}
}
}
| 옵션 | 설명 |
|---|---|
| fixed_interval | 1m, 1h, 1d — 고정 간격 |
| calendar_interval | 1M, 1w — 달력 (월·주) |
| field | date mapping (@timestamp) |
| extended_bounds | 빈 구간도 bucket 유지 (차트 끊김 방지) |
- Kibana 시간 차트 — 내부적으로
date_histogram - timezone —
time_zone옵션 (Kibana는 사용자 TZ) - interval 너무 촘촘 — bucket 수 폭증·메모리 — 범위에 맞게 조정
4. query와 aggs 함께 쓰기
{
"size": 0,
"query": {
"bool": {
"filter": [
{ "term": { "level": "ERROR" } },
{ "range": { "@timestamp": { "gte": "now-1h" } } }
]
}
},
"aggs": {
"top_services": {
"terms": { "field": "service", "size": 5 }
},
"per_minute": {
"date_histogram": {
"field": "@timestamp",
"fixed_interval": "1m"
}
}
}
}
- query — 집계 대상 document 필터 (4편 bool filter 패턴)
- aggs — 필터된 집합에만 적용
- post_filter — hits만 추가 필터, aggs에는 미적용 (드물게 사용 — 혼동 주의)
| 실수 | 결과 |
|---|---|
| text 필드 terms | 실패 또는 fielddata 경고 |
size 기본 10만 |
상위 10 외 서비스 누락 |
| query 없이 전체 agg | 전 index 스캔 — 시간 filter 권장 |
5. 클러스터 구조
cluster — 하나 이상 node가 마스터 선출·메타데이터·검색 조율로 묶인 단위.

| 구성 | 역할 |
|---|---|
| Cluster | 전체 ES 배포 단위 |
| Node | JVM 프로세스 1개 (서버·Pod) |
| Index | 논리적 document 집합 (app-logs) |
| Shard | index의 물리 분할 (Lucene index) |
| Primary shard | 쓰기 대상 원본 |
| Replica shard | primary 복사본 — 읽기·장애 대응 |
Index app-logs → 3 primary + 1 replica each → 6 shard copies
Node A: P0, R1
Node B: P1, R2
Node C: P2, R0
- primary 수 — index 생성 시 정함 — 줄이기 어려움 (reindex) — 22편 사이징
- replica —
number_of_replicas로 런타임 증감 가능 (0 = 복제 없음) - green — primary+replica 할당 완료; yellow — replica 미할당; red — primary 일부 없음
노드 역할 (개요)
| 역할 | 하는 일 |
|---|---|
| master-eligible | 클러스터 상태·index 생성·shard 할당 |
| data | shard 보유·쿼리·인덱싱 실행 |
| ingest | ingest pipeline (8편) |
| coordinating | 클라이언트 요청 fan-out·결과 병합 (모든 node가 가능) |
- 소규모 — data + master 겸용 node
- Elastic Cloud — 역할·용량 프리셋 (16편)
6. Routing
document가 어느 primary shard에 들어가는지 결정.
shard_num = hash(routing) % number_of_primary_shards
| routing 값 | 기본 |
|---|---|
명시 _routing |
지정 값 |
| 생략 | _id |
# 같은 order_id는 같은 shard (커스텀 routing)
PUT /app-logs/_doc/1?routing=9001
{ "order_id": 9001, "message": "..." }
GET /app-logs/_search?routing=9001
- 같은 routing — 한 shard에 모임 — 조회 지역성 (해당 shard만 검색)
- routing 남용 — shard 불균형 (hot shard)
_search응답hits.hits[]._shard— 어느 shard에서 왔는지
집계와 shard
- coordinating node가 각 shard에 요청 → partial agg → 병합
- shard 많을수록 — 병렬·오버헤드 트레이드오프
- too many shards — 클러스터 메타·메모리 부담 — 22편
7. 실무 체크리스트
| 항목 | 습관 |
|---|---|
| 대시보드 수치 | aggs + size: 0 |
| 그룹 필드 | keyword mapping (3편) |
| 시계열 | date_histogram on @timestamp |
| 시간 범위 | query filter로 먼저 좁히기 |
| primary shard | 생성 전 용량·노드 수 계획 |
| replica | prod ≥ 1 (가용성) |
| routing | 필요할 때만 — 균형 모니터링 |
- shard 사이징·ILM rollover — 13·22편
- 6편 — Filebeat가 index에 document 어떻게 넣는지
정리
- aggregation — hits 대신 bucket·metric; 로그 운영은 terms·date_histogram
- keyword·date mapping과 짝 — text로 terms 불가
- cluster — node·primary/replica shard로 분산
- routing — primary shard 선택; 기본
_id - query(4편) + agg(5편) + cluster — Kibana·용량 설계의 기반
다음에 다룰 것
- Beats
- Filebeat·Metricbeat, module, output to ES/Logstash
해당 내용은 Elasticsearch Guide (Aggregations, Size your shards, Cluster APIs) 를 기반으로 합니다.
'ELK & Elastic Cloud' 카테고리의 다른 글
| Logstash (0) | 2026.07.14 |
|---|---|
| Beats (0) | 2026.07.11 |
| Query DSL (0) | 2026.07.05 |
| Mapping과 Analyzer (0) | 2026.07.03 |
| Elasticsearch란 (0) | 2026.07.02 |