Kubernetes 로깅 — 애플리케이션 로그를 수집하고 조회하는 전략
Pod이 수백 개인 클러스터에서
kubectl logs로 하나씩 확인하는 것이 현실적인 방법일까요?
개발 환경에서는 kubectl logs로 충분하지만, 프로덕션 환경에서는 중앙화된 로깅 시스템이 필수입니다. Pod은 언제든 재시작되거나 삭제될 수 있고, 그때 로그도 함께 사라지기 때문입니다.
Kubernetes 로깅 아키텍처
로그가 흐르는 경로
애플리케이션 → stdout/stderr → 컨테이너 런타임 → 노드 파일시스템
(/var/log/containers/)
↓
로그 수집기 (DaemonSet)
↓
중앙 저장소 (ES/Loki)
↓
조회 (Kibana/Grafana)
왜 stdout/stderr인가?
Kubernetes는 컨테이너의 stdout/stderr를 캡처하여 노드에 저장합니다. 파일에 직접 쓰면 로그 로테이션, 디스크 관리 등을 직접 해야 하지만, stdout으로 출력하면 Kubernetes가 관리해 줍니다.
# 로그 조회
kubectl logs my-pod
kubectl logs my-pod -c sidecar # 특정 컨테이너
kubectl logs my-pod --previous # 이전(크래시된) 컨테이너
kubectl logs my-pod -f # 실시간 스트림
kubectl logs my-pod --tail=100 # 마지막 100줄
kubectl logs my-pod --since=1h # 최근 1시간
# 레이블 기반 여러 Pod 로그
kubectl logs -l app=web --all-containers
로그 파일 위치
# 노드에서 직접 확인
ls /var/log/containers/
# my-pod_default_app-abc123.log → 심볼릭 링크
# 실제 파일: /var/log/pods/<uid>/<container>/0.log
kubelet은 기본적으로 10MB, 5개 파일로 로그를 로테이션합니다.
--container-log-max-size=10Mi
--container-log-max-files=5
로그 수집 패턴
패턴 1: 노드 에이전트 (DaemonSet)
가장 일반적인 패턴입니다. 각 노드에 로그 수집기를 DaemonSet으로 배포합니다.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: logging
spec:
selector:
matchLabels:
app: fluent-bit
template:
metadata:
labels:
app: fluent-bit
spec:
serviceAccountName: fluent-bit
tolerations:
- operator: "Exists" # 모든 노드에서 실행
containers:
- name: fluent-bit
image: fluent/fluent-bit:2.2
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: true
- name: containers
mountPath: /var/log/containers
readOnly: true
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 256Mi
volumes:
- name: varlog
hostPath:
path: /var/log
- name: containers
hostPath:
path: /var/log/containers
패턴 2: 사이드카
특수한 로그 형식이나 추가 처리가 필요한 경우 사이드카로 수집합니다.
spec:
containers:
- name: app
image: my-app:1.0
volumeMounts:
- name: log-volume
mountPath: /var/log/app
- name: log-forwarder
image: fluent-bit:2.2
volumeMounts:
- name: log-volume
mountPath: /var/log/app
volumes:
- name: log-volume
emptyDir: {}
Fluent Bit vs Fluentd
| 특성 | Fluent Bit | Fluentd |
|---|---|---|
| 언어 | C | Ruby + C |
| 메모리 | ~10MB | ~40MB |
| 플러그인 수 | 70+ | 1000+ |
| 복잡한 라우팅 | 제한적 | 강력 |
| 권장 역할 | 노드 수집기 | 중앙 집계기 |
실무에서는 Fluent Bit로 수집하고 Fluentd로 집계/라우팅하는 조합을 많이 사용합니다.
노드 1: [Fluent Bit] ──┐
노드 2: [Fluent Bit] ──┼──→ [Fluentd 집계기] ──→ Elasticsearch
노드 3: [Fluent Bit] ──┘ └──→ S3 (장기 보관)
EFK 스택 (Elasticsearch + Fluent Bit + Kibana)
전통적인 Kubernetes 로깅 스택입니다.
Fluent Bit (수집) → Elasticsearch (저장/검색) → Kibana (시각화)
Fluent Bit 설정 예제
[SERVICE]
Flush 5
Daemon Off
Log_Level info
[INPUT]
Name tail
Path /var/log/containers/*.log
Parser cri
Tag kube.*
Mem_Buf_Limit 5MB
[FILTER]
Name kubernetes
Match kube.*
Merge_Log On
K8S-Logging.Parser On
K8S-Logging.Exclude On
[OUTPUT]
Name es
Match kube.*
Host elasticsearch.logging.svc.cluster.local
Port 9200
Index k8s-logs
Suppress_Type_Name On
Loki 스택 (Grafana Loki + Promtail + Grafana)
Loki는 Prometheus에서 영감을 받은 로그 집계 시스템입니다. 로그 내용을 인덱싱하지 않고 레이블만 인덱싱하여 리소스 효율이 높습니다.
Promtail/Fluent Bit (수집) → Loki (저장) → Grafana (시각화)
# Loki 스택 설치 (Helm)
helm install loki grafana/loki-stack \
--namespace logging \
--set promtail.enabled=true \
--set grafana.enabled=true
EFK vs Loki 비교
| 기준 | EFK | Loki |
|---|---|---|
| 리소스 사용량 | 높음 | 낮음 |
| 전문 검색(full-text) | 강력 | 제한적 |
| 비용 | 높음 | 낮음 |
| 학습 곡선 | 높음 | 낮음 (Grafana 연동) |
| 대규모 클러스터 | 운영 복잡 | 운영 단순 |
| 쿼리 언어 | KQL | LogQL |
최근에는 Loki의 비용 효율성 때문에 많은 팀이 EFK에서 Loki로 전환하고 있습니다.
구조화된 로깅 (Structured Logging)
JSON 형태로 로그를 출력하면 파싱과 검색이 편해집니다.
{"timestamp":"2026-03-19T10:30:00Z","level":"ERROR","service":"user-api","message":"DB connection failed","error":"connection refused","trace_id":"abc123"}
# Fluent Bit에서 JSON 파싱
[FILTER]
Name parser
Match kube.*
Key_Name log
Parser json
Reserve_Data On
로그 보관 전략
| 용도 | 보관 기간 | 저장소 |
|---|---|---|
| 실시간 디버깅 | 7일 | Elasticsearch/Loki |
| 감사/컴플라이언스 | 90일~1년 | S3/GCS (저비용) |
| 장기 보관 | 1년+ | Glacier/Coldline |
# Elasticsearch ILM(Index Lifecycle Management)
# Hot → Warm (7일) → Cold (30일) → Delete (90일)
실무에서 주의할 점
- 로그 볼륨 관리: 과도한 로그는 디스크와 비용을 소모합니다. 적절한 로그 레벨 설정이 중요합니다
- 민감 정보 마스킹: 로그에 비밀번호, 토큰이 포함되지 않도록 주의하세요
- 타임스탬프 통일: UTC 기준으로 일관된 타임스탬프를 사용하세요
- 수집기 리소스 제한: DaemonSet의 리소스를 적절히 설정하여 노드에 영향을 주지 않도록 합니다
정리
Kubernetes 로깅의 기본은 stdout/stderr 출력입니다. DaemonSet으로 Fluent Bit를 배포하여 로그를 수집하고, Elasticsearch(EFK) 또는 Loki로 중앙 저장합니다. 비용과 운영 부담을 고려하면 Loki가 최근 트렌드이며, 구조화된 로깅과 적절한 보관 전략까지 갖추면 효과적인 로깅 시스템을 구축할 수 있습니다.