Theme:

Pod이 수백 개인 클러스터에서 kubectl logs로 하나씩 확인하는 것이 현실적인 방법일까요?

개발 환경에서는 kubectl logs로 충분하지만, 프로덕션 환경에서는 중앙화된 로깅 시스템이 필수입니다. Pod은 언제든 재시작되거나 삭제될 수 있고, 그때 로그도 함께 사라지기 때문입니다.

Kubernetes 로깅 아키텍처

로그가 흐르는 경로

PLAINTEXT
애플리케이션 → stdout/stderr → 컨테이너 런타임 → 노드 파일시스템
                                                  (/var/log/containers/)

                                              로그 수집기 (DaemonSet)

                                              중앙 저장소 (ES/Loki)

                                              조회 (Kibana/Grafana)

왜 stdout/stderr인가?

Kubernetes는 컨테이너의 stdout/stderr를 캡처하여 노드에 저장합니다. 파일에 직접 쓰면 로그 로테이션, 디스크 관리 등을 직접 해야 하지만, stdout으로 출력하면 Kubernetes가 관리해 줍니다.

SHELL
# 로그 조회
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

로그 파일 위치

SHELL
# 노드에서 직접 확인
ls /var/log/containers/
# my-pod_default_app-abc123.log → 심볼릭 링크
# 실제 파일: /var/log/pods/<uid>/<container>/0.log

kubelet은 기본적으로 10MB, 5개 파일로 로그를 로테이션합니다.

PLAINTEXT
--container-log-max-size=10Mi
--container-log-max-files=5

로그 수집 패턴

패턴 1: 노드 에이전트 (DaemonSet)

가장 일반적인 패턴입니다. 각 노드에 로그 수집기를 DaemonSet으로 배포합니다.

YAML
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: 사이드카

특수한 로그 형식이나 추가 처리가 필요한 경우 사이드카로 수집합니다.

YAML
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 BitFluentd
언어CRuby + C
메모리~10MB~40MB
플러그인 수70+1000+
복잡한 라우팅제한적강력
권장 역할노드 수집기중앙 집계기

실무에서는 Fluent Bit로 수집하고 Fluentd로 집계/라우팅하는 조합을 많이 사용합니다.

PLAINTEXT
노드 1: [Fluent Bit] ──┐
노드 2: [Fluent Bit] ──┼──→ [Fluentd 집계기] ──→ Elasticsearch
노드 3: [Fluent Bit] ──┘                    └──→ S3 (장기 보관)

EFK 스택 (Elasticsearch + Fluent Bit + Kibana)

전통적인 Kubernetes 로깅 스택입니다.

PLAINTEXT
Fluent Bit (수집) → Elasticsearch (저장/검색) → Kibana (시각화)

Fluent Bit 설정 예제

INI
[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에서 영감을 받은 로그 집계 시스템입니다. 로그 내용을 인덱싱하지 않고 레이블만 인덱싱하여 리소스 효율이 높습니다.

PLAINTEXT
Promtail/Fluent Bit (수집) → Loki (저장) → Grafana (시각화)
SHELL
# Loki 스택 설치 (Helm)
helm install loki grafana/loki-stack \
  --namespace logging \
  --set promtail.enabled=true \
  --set grafana.enabled=true

EFK vs Loki 비교

기준EFKLoki
리소스 사용량높음낮음
전문 검색(full-text)강력제한적
비용높음낮음
학습 곡선높음낮음 (Grafana 연동)
대규모 클러스터운영 복잡운영 단순
쿼리 언어KQLLogQL

최근에는 Loki의 비용 효율성 때문에 많은 팀이 EFK에서 Loki로 전환하고 있습니다.

구조화된 로깅 (Structured Logging)

JSON 형태로 로그를 출력하면 파싱과 검색이 편해집니다.

JSON
{"timestamp":"2026-03-19T10:30:00Z","level":"ERROR","service":"user-api","message":"DB connection failed","error":"connection refused","trace_id":"abc123"}
YAML
# 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
YAML
# Elasticsearch ILM(Index Lifecycle Management)
# Hot → Warm (7일) → Cold (30일) → Delete (90일)

실무에서 주의할 점

  • 로그 볼륨 관리: 과도한 로그는 디스크와 비용을 소모합니다. 적절한 로그 레벨 설정이 중요합니다
  • 민감 정보 마스킹: 로그에 비밀번호, 토큰이 포함되지 않도록 주의하세요
  • 타임스탬프 통일: UTC 기준으로 일관된 타임스탬프를 사용하세요
  • 수집기 리소스 제한: DaemonSet의 리소스를 적절히 설정하여 노드에 영향을 주지 않도록 합니다

정리

Kubernetes 로깅의 기본은 stdout/stderr 출력입니다. DaemonSet으로 Fluent Bit를 배포하여 로그를 수집하고, Elasticsearch(EFK) 또는 Loki로 중앙 저장합니다. 비용과 운영 부담을 고려하면 Loki가 최근 트렌드이며, 구조화된 로깅과 적절한 보관 전략까지 갖추면 효과적인 로깅 시스템을 구축할 수 있습니다.

댓글 로딩 중...