티스토리 뷰

 

 

구성 목표 요약

  • K8s 클러스터에 Splunk(Logging 시스템) 설치
  • Fluent Bit로 애플리케이션/노드 로그 수집
  • Splunk HEC(HTTP Event Collector)로 전송
  • Splunk에서 검색 및 대시보드 구성

1. 사전 준비 사항

  • Kubernetes 클러스터 준비 (kind, minikube, 실제 클러스터 등)
  • Splunk Docker Image
  • Splunk용 PVC 또는 임시볼륨
  • Fluent Bit 설치

2. Splunk 설치 (K8s에 배포)

2-1. Splunk Deployment YAML (예: splunk-deploy.yaml)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: splunk
  labels:
    app: splunk
spec:
  replicas: 1
  selector:
    matchLabels:
      app: splunk
  template:
    metadata:
      labels:
        app: splunk
    spec:
      containers:
        - name: splunk
          image: splunk/splunk:latest
          env:
            - name: SPLUNK_START_ARGS
              value: "--accept-license"
            - name: SPLUNK_PASSWORD
              value: changeme123!
          ports:
            - containerPort: 8000 # UI
            - containerPort: 8088 # HEC
---
apiVersion: v1
kind: Service
metadata:
  name: splunk
spec:
  selector:
    app: splunk
  type: NodePort
  ports:
    - port: 8000
      targetPort: 8000
      nodePort: 30080
    - port: 8088
      targetPort: 8088
      nodePort: 30088
kubectl apply -f splunk-deploy.yaml

3. Splunk HEC Token 발급

  1. 웹 UI 접속: http://:30080
  2. 설정(Settings) → Data Inputs → HTTP Event Collector
  3. 새 토큰 생성 (이름: k8s-logs, Token: ABC123456TOKEN...)

4. Fluent Bit 설치 및 Splunk 연동

4-1. Helm 설치 (Fluent Bit 공식 차트)

helm repo add fluent https://fluent.github.io/helm-charts
helm repo update

4-2. values.yaml 구성 (예시)

backend:
  type: splunk
  splunk:
    host: http://splunk.default.svc.cluster.local:8088
    token: ABC123456TOKEN...
    insecureSSL: true
    source: k8s
    sourcetype: _json
    index: main

config:
  service: |
    [SERVICE]
        Flush        5
        Daemon       Off
        Log_Level    info
  inputs: |
    [INPUT]
        Name              tail
        Path              /var/log/containers/*.log
        Parser            docker
        Tag               kube.*
        Refresh_Interval  5
  outputs: |
    [OUTPUT]
        Name              splunk
        Match             *
        Host              splunk.default.svc.cluster.local
        Port              8088
        TLS               off
        Splunk_Token      ABC123456TOKEN...
        Splunk_Send_Raw   Off
        Retry_Limit       False

4-3. Fluent Bit 설치

helm install fluent-bit fluent/fluent-bit -f values.yaml

 

5. Splunk 검색 예제

Splunk UI → Search

index=main sourcetype=_json source=k8s
  • pod_name, container_name, log, namespace 등의 필드로 필터 가능
  • 알림(Alert), 대시보드 구성도 가능

실무 팁

항목 내용

Token 갱신 주기 보안 정책에 따라 주기적으로 갱신하여 Secret로 K8s에 저장
고가용성 구성 StatefulSet + PVC + Liveness/Readiness 설정 권장
로그 필터링 Fluent Bit에서 로그 수준 필터링, 특정 namespace/pod만 수집 가능
Helm 자동화 배포 GitOps (ArgoCD, Flux 등)와 연계하여 Helm chart 버전 관리
대시보드 확장 Splunk Dashboard Studio 사용 → 실시간 K8s 로그/이벤트/성능 모니터링 구성 가능

예제 디렉토리 구조

splunk-on-k8s/
├── splunk-deploy.yaml
├── fluent-bit/
│   └── values.yaml
└── README.md

마무리 요약

  • Splunk를 K8s에 직접 배포하거나 Helm 차트 사용 가능
  • Fluent Bit는 로그 수집의 표준이며 Splunk와 안정적으로 연동 가능
  • HEC 토큰 보안관리 중요, ConfigMap/Secret으로 관리
  • 실시간 로그 검색 + 대시보드 구성으로 운영 효율성 ↑

Splunk 사용법 모범 사례 (Best Practices)

Splunk 쿼리는 어떻게 작성하느냐에 따라 검색 속도, 리소스 사용량, 그리고 가독성에서 큰 차이를 보인다. 아래의 원칙들을 따르면 더 빠르고, 효율적이며, 유지보수가 쉬운 SPL 작성.

1. 데이터 범위를 최대한 좁힌다 (Filter Early and Often)

가장 중요한 원칙. 검색을 시작할 때 최대한 많은 데이터를 필터링하여 처리할 데이터의 양을 줄여야 한다.

  • 메타데이터 필드 활용: index, sourcetype, source, host 와 같은 메타데이터 필드를 검색어 가장 앞에 명시. 이 필드들은 Splunk에 의해 인덱싱(색인)되어 있어 매우 빠른 필터링 가능.

나쁜 예시 (Bad Practice):

 

index=* 
| search sourcetype="access_combined" host="webserver01" status=404

이유: index=*는 모든 인덱스를 스캔한 후 search 명령어로 필터링하므로 매우 비효율적이다.

좋은 예시 (Good Practice):

 

index="web" sourcetype="access_combined" host="webserver01" status=404

이유: 검색 시작과 동시에 web 인덱스의 access_combined 소스 타입 데이터만 대상으로 삼아 검색 범위를 획기적으로 줄인다.

2. 효율적인 명령어 사용 (Use Efficient Commands)

  • search vs where: wheresearch로 필터링할 수 없는 경우(예: stats로 계산된 필드)에만 사용. search는 인덱싱된 데이터를 활용하므로 훨씬 빠르다.

나쁜 예시:

index="web" sourcetype="access_combined"
| where status >= 400

좋은 예시:

 

index="web" sourcetype="access_combined" status>=400
  • tstats 활용: tstats 명령어는 일반 search와 달리 인덱싱된 메타데이터 요약 정보(.tsidx 파일) 사용. 데이터 모델이나 Accelerated sourcetype을 대상으로 집계 연산을 할 때 엄청나게 빠르다.

나쁜 예시 (수십억 건의 로그를 모두 스캔):

 

index="firewall" 
| stats count by src_ip

좋은 예시 (데이터 모델 사용 시):

 

| tstats count from datamodel=Network_Traffic where nodename=All_Traffic by All_Traffic.src_ip
| rename All_Traffic.src_ip as src_ip

이유: tstats는 원본 로그(raw data)가 아닌 요약 정보를 참조하므로 집계 속도가 월등히 빠르다.

3. 필드 추출 최적화 (Optimize Field Extraction)

  • 내장 필드 활용: Splunk가 자동으로 추출해주는 필드(status, user 등)를 최대한 활용.
  • rex는 필요할 때만: 정규표현식(rex)은 강력하지만 비용이 큰 연산. 꼭 필요할 때, 그리고 최대한 좁혀진 데이터 범위에만 사용.
  • 구체적인 정규표현식: (.*)처럼 광범위한 패턴 대신 (\w+), ([^\s]+) 와 같이 더 구체적인 패턴을 사용하면 성능 향상.

나쁜 예시 (불필요하게 넓은 범위):

 

index="app" "login failed"
| rex field=_raw "user=(?<username>.*) from"

좋은 예시 (더 구체적인 패턴):

 

index="app" "login failed"
| rex field=_raw "user=(?<username>\w+) from"

4. 검색 결과 가공은 마지막에 (Process Results Late)

  • fields vs table: 검색 파이프라인 중간에 필드를 선택해야 한다면 table 대신 fields 사용. table은 결과를 표 형태로 완전히 변환하지만, fields는 필요한 필드만 남기고 나머지 데이터는 유지하여 후속 연산 가능.
  • 정렬은 마지막에: sort는 많은 메모리를 사용하는 명령어. 꼭 필요한 경우에, 그리고 가급적 검색 파이프라인의 마지막 단계에서 사용.

나쁜 예시 (중간에 table을 사용하여 데이터 유실):

 

index="sales" action=purchase
| table user, product_id, price
| stats count by user

이유: table 명령어 다음에는 user, product_id, price 필드만 남으므로 stats count by user가 제대로 동작하지 않거나 원하는 결과를 얻을 수 없다.

좋은 예시:

 

index="sales" action=purchase
| stats count by user
| table user, count

5. Subsearch 사용 시 주의사항

Subsearch(하위검색)는 유용하지만, 기본적으로 10,000건의 결과60초의 시간 제한을 가진다. 대용량 데이터를 처리할 때는 성능 저하의 주된 원인이 될 수 있다. join, lookup, append 또는 stats를 활용한 재구성으로 대체 고려.

나쁜 예시 (Subsearch):

 

index=main sourcetype=access_log
[search index=main sourcetype=auth_log "login failure" | dedup user | fields user]

좋은 예시 (Join 사용):

 

index=main sourcetype=access_log 
| join user [search index=main sourcetype=auth_log "login failure" | dedup user | fields user]

더 좋은 예시 (stats 사용 - 종종 가장 빠름):

index=main (sourcetype=access_log OR sourcetype=auth_log)
| stats values(sourcetype) as sourcetypes by user
| where mvcount(sourcetypes)=2 AND "auth_log" IN sourcetypes

이유: 단일 검색으로 두 소스 타입의 데이터를 모두 가져온 후 stats로 사용자를 그룹화하는 것이 여러 검색을 실행하는 것보다 효율적.

6. 가독성 높은 SPL 작성 (Write Readable SPL)

  • 파이프(|) 기준으로 줄 바꿈: 쿼리가 길어지면 각 파이프 앞에서 줄을 바꿔 가독성 높인다.
  • 주석 활용: 복잡한 로직에는 주석을 추가하여 다른 사람(그리고 미래의 나)이 쉽게 이해할 수 있도록 만든다.
  • 필드명 변경(rename / as): as 키워드를 사용해 stats 결과의 필드명을 직관적으로 바꾸면 쿼리의 목적을 이해하기 쉬워진다.

나쁜 예시:

 

index=web status=200 action=purchase | stats count as c, dc(user_id) as u by product_id | sort -c | head 10

좋은 예시:

 

index=web status=200 action=purchase
`comment("웹서버에서 성공(200)한 구매(purchase) 로그만 필터링")`
| stats count as "구매 횟수", dc(user_id) as "순수 구매자 수" by product_id
`comment("제품 ID별로 구매 횟수와 순수 구매자 수를 집계")`
| sort -"구매 횟수"
| head 10

종합 예시: 나쁜 쿼리를 좋은 쿼리로 개선하기

요구사항: 지난 24시간 동안 웹 서버에서 404 에러를 발생시킨 사용자가 구매한 상품 목록 찾기

나쁜 쿼리 (Bad Query):

 

index=* sourcetype=access_combined
| search [search index=* sourcetype=access_combined status=404 | dedup clientip | fields clientip]
| search status=200
| rex "purchase\.php\?product=(?<product_id>\d+)"
| stats count by product_id
  • index=* 사용
  • 느린 Subsearch 사용
  • 불필요한 search 명령어 반복
  • 비효율적인 필터링 순서

좋은 쿼리 (Good Query):

 

`comment("1단계: 웹 로그에서 404 에러와 구매 기록이 있는 IP를 효율적으로 찾는다")`
index="web" sourcetype="access_combined" (status=404 OR status=200)
| stats values(status) as statuses by clientip
| where mvcount(statuses)=2 AND "404" IN statuses

`comment("2단계: 위에서 찾은 IP 목록을 사용하여 해당 IP의 구매 기록만 필터링한다")`
| join clientip [
    search index="web" sourcetype="access_combined" status=200 action=purchase
]

`comment("3단계: 구매한 상품 ID별로 판매량을 집계한다")`
| stats count by product_id
| sort -count
| rename count as "판매량", product_id as "상품ID"

이 쿼리는 join 대신 stats를 활용하여 더 최적화할 수도 있지만, 단계별로 로직을 명확히 보여주기 위해 join을 사용했다. 이처럼 먼저 필터링하고, 효율적인 명령어를 사용하며, 가독성을 고려하는 것만으로도 Splunk 쿼리의 품질을 크게 향상시킬 수 있다.


 

# Splunk 사용법 모범 사례 및 코드 예제

1. 검색 효율화
모범 사례: 검색 시간 범위 제한
나쁜 예: 시간 범위 없이 전체 데이터 검색
* | stats count by sourcetype

좋은 예: 적절한 시간 범위 지정
index=main earliest=-24h@h latest=now | stats count by sourcetype

모범 사례: 불필요한 필드 제거
나쁜 예: 모든 필드 유지
index=main | table *

# 좋은 예: 필요한 필드만 선택
index=main | table _time, host, sourcetype, message

2. 인덱싱 최적화
모범 사례: props.conf 설정 예제
[mysourcetype]
SHOULD_LINEMERGE = false
LINE_BREAKER = ([\r\n]+)
TIME_FORMAT = %Y-%m-%d %H:%M:%S
MAX_TIMESTAMP_LOOKAHEAD = 25

3. 필드 추출
모범 사례: 정규식 필드 추출
# 인라인 필드 추출
index=main | rex field=_raw "user=(?<user>\w+), action=(?<action>\w+)"

# 정규식으로 필드 추출 후 통계
index=web_logs | rex "GET (?<url>\S+) HTTP" | stats count by url

4. 서브검색 최적화
모범 사례: 서브검색 대신 join 사용
나쁜 예: 큰 데이터셋에 서브검색 사용
[search index=main error | table host] | search index=performance host=$host$

좋은 예: join 사용
index=main error | table host | join type=inner host [search index=performance | stats avg(cpu) by host]

5. 스케줄링 및 경고
모범 사례: 경고 설정 예제
index=security failed login | stats count by user | where count > 5

이 검색을 저장하고 경고로 설정:
- 트리거 조건: 결과 수 > 0
- 실행 시간: 매 15분
- 작업: 이메일 알림 전송

6. 대시보드 최적화
모범 사례: XML 대시보드 예제
<dashboard>
  <label>시스템 모니터링</label>
  <row>
    <panel>
      <title>CPU 사용률 Top 5</title>
      <chart>
        <search>
          <query>index=perfmon metric=cpu | stats avg(value) as avg_cpu by host | sort - avg_cpu | head 5</query>
          <earliest>-1h</earliest>
          <latest>now</latest>
        </search>
        <option name="charting.chart">bar</option>
      </chart>
    </panel>
  </row>
</dashboard>


7. 로그 포맷팅
모범 사례: 구조화된 로깅
# 애플리케이션에서 구조화된 로그 생성 예제
import logging
import json

log_data = {
    "timestamp": "2023-01-01T12:00:00Z",
    "level": "ERROR",
    "service": "payment",
    "user_id": "12345",
    "transaction_id": "txn-67890",
    "message": "Payment processing failed",
    "error_code": "PAY-402"
}

# JSON 형식으로 로깅
logging.info(json.dumps(log_data))

 

8. 데이터 샘플링
모범 사례: 대량 데이터 처리 시 샘플링
# 전체 데이터 대신 10% 샘플 사용
index=bigdata | head 10000 | stats count by type

# 또는
index=bigdata | sample ratio=0.1 | stats count by type

 

9. lookup 테이블 활용
모범 사례: lookup 테이블 생성 및 사용
# lookup 테이블 생성
| inputlookup mylookup.csv | table user_id, department

# lookup 테이블 조인
index=main | lookup mylookup.csv user_id as user OUTPUT department

 


10. 매크로 활용
모범 사례: 재사용 가능한 매크로 정의
```splunk
# 매크로 정의 (restmacro)
[error_count(1)]
search index=$index$ error | stats count as error_count

# 매크로 사용
`error_count(main)`

---

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2026/02   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
글 보관함