# /fluent-bit/etc/fluent-bit.conf
[SERVICE]
# 기본 설정
Flush 5
Grace 30
Daemon off
Log_Level info
# 파서 파일
Parsers_File parsers.conf
# HTTP 서버 (메트릭/헬스체크)
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
# 스토리지 (버퍼링)
storage.path /var/log/flb-storage/
storage.sync normal
storage.checksum off
storage.backlog.mem_limit 50M
storage.metrics on
#---------------------------------------------
# INPUT: 컨테이너 로그 수집
#---------------------------------------------
[INPUT]
Name tail
Tag kube.*
Path /var/log/containers/*.log
# kube-system 제외
Exclude_Path /var/log/containers/*_kube-system_*.log,/var/log/containers/*_kube-public_*.log
# 파서
multiline.parser docker, cri
# 상태 DB
DB /var/log/flb_kube.db
DB.locking true
# 메모리 제한
Mem_Buf_Limit 50MB
# 긴 라인 스킵
Skip_Long_Lines On
# 새로고침 간격
Refresh_Interval 10
# 로테이션 대기
Rotate_Wait 30
# 파일시스템 버퍼
storage.type filesystem
# 기존 파일 처리
Read_from_Head Off
#---------------------------------------------
# INPUT: 시스템 로그
#---------------------------------------------
[INPUT]
Name systemd
Tag host.systemd
Systemd_Filter _SYSTEMD_UNIT=kubelet.service
Systemd_Filter _SYSTEMD_UNIT=containerd.service
Systemd_Filter _SYSTEMD_UNIT=docker.service
DB /var/log/flb_systemd.db
Read_From_Tail On
Strip_Underscores On
#---------------------------------------------
# FILTER: Kubernetes 메타데이터 추가
#---------------------------------------------
[FILTER]
Name kubernetes
Match kube.*
# API 서버 설정
Kube_URL https://kubernetes.default.svc:443
Kube_CA_File /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Kube_Token_File /var/run/secrets/kubernetes.io/serviceaccount/token
Kube_Tag_Prefix kube.var.log.containers.
# 로그 병합
Merge_Log On
Merge_Log_Key log_processed
# 파서 자동 감지
K8S-Logging.Parser On
K8S-Logging.Exclude Off
# Kubelet 사용 (API 서버 부하 감소)
Use_Kubelet On
Kubelet_Port 10250
# 레이블/어노테이션
Labels On
Annotations Off
# 버퍼
Buffer_Size 0
#---------------------------------------------
# FILTER: 필드 추가/수정
#---------------------------------------------
[FILTER]
Name modify
Match *
# 클러스터 정보 추가
Add cluster_name eks-production
Add environment production
Add region ap-northeast-2
# 불필요한 필드 제거
Remove stream
Remove _p
#---------------------------------------------
# FILTER: 노이즈 제거
#---------------------------------------------
[FILTER]
Name grep
Match kube.*
# 헬스체크 로그 제외
Exclude log healthcheck
Exclude log readiness
Exclude log liveness
Exclude log health
Exclude log /health
Exclude log /ready
Exclude log /live
#---------------------------------------------
# FILTER: 로그 레벨 추출 (JSON이 아닌 경우)
#---------------------------------------------
[FILTER]
Name parser
Match kube.*
Key_Name log
Parser extract_level
Reserve_Data True
Preserve_Key True
#---------------------------------------------
# FILTER: 멀티라인 처리
#---------------------------------------------
[FILTER]
Name multiline
Match kube.*
multiline.key_content log
multiline.parser java_multiline, python_multiline, go_multiline
#---------------------------------------------
# FILTER: Lua 스크립트 (고급 처리)
#---------------------------------------------
[FILTER]
Name lua
Match kube.*
script /fluent-bit/scripts/process.lua
call process_log
#---------------------------------------------
# OUTPUT: Loki
#---------------------------------------------
[OUTPUT]
Name loki
Match kube.*
Host loki-gateway.loki.svc.cluster.local
Port 80
Labels job=fluentbit, namespace=$kubernetes['namespace_name'], app=$kubernetes['labels']['app'], pod=$kubernetes['pod_name']
# 배치 설정
BatchWait 1
BatchSize 1048576
# 라인 형식
LineFormat json
# 자동 레이블 추출
AutoKubernetesLabels off
# 재시도
Retry_Limit 5
# 테넌트 (멀티테넌시)
TenantID default
#---------------------------------------------
# OUTPUT: CloudWatch Logs
#---------------------------------------------
[OUTPUT]
Name cloudwatch_logs
Match kube.*
region ap-northeast-2
log_group_name /aws/containerinsights/${CLUSTER_NAME}/application
log_stream_prefix ${HOST_NAME}-
auto_create_group true
log_retention_days 30
# 압축
compress gzip
# 재시도
retry_limit 5
#---------------------------------------------
# OUTPUT: S3 (백업/아카이브)
#---------------------------------------------
[OUTPUT]
Name s3
Match kube.*
region ap-northeast-2
bucket my-logs-backup
total_file_size 100M
upload_timeout 10m
s3_key_format /logs/$TAG/%Y/%m/%d/%H/%M/%S
compression gzip
content_type application/gzip
# /fluent-bit/etc/parsers.conf
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep On
[PARSER]
Name cri
Format regex
Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<log>.*)$
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
Time_Keep On
[PARSER]
Name json
Format json
Time_Key timestamp
Time_Format %Y-%m-%dT%H:%M:%S.%LZ
[PARSER]
Name extract_level
Format regex
Regex (?<level>(DEBUG|INFO|WARN|WARNING|ERROR|FATAL|CRITICAL))
[PARSER]
Name nginx
Format regex
Regex ^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[MULTILINE_PARSER]
Name java_multiline
Type regex
Flush_timeout 1000
Rule "start_state" "/^\d{4}-\d{2}-\d{2}|^\[?\d{4}[-\/]\d{2}[-\/]\d{2}/" "cont"
Rule "cont" "/^[\s\t]+|^Caused by:|^[\w\.]+(Exception|Error)/" "cont"
[MULTILINE_PARSER]
Name python_multiline
Type regex
Flush_timeout 1000
Rule "start_state" "/^Traceback|^\d{4}-\d{2}-\d{2}/" "cont"
Rule "cont" "/^\s+|^[A-Za-z]+Error:/" "cont"
[MULTILINE_PARSER]
Name go_multiline
Type regex
Flush_timeout 1000
Rule "start_state" "/^panic:|^goroutine \d+/" "cont"
Rule "cont" "/^\s+/" "cont"
-- /fluent-bit/scripts/process.lua
function process_log(tag, timestamp, record)
-- 로그 레벨 정규화
if record["level"] then
record["level"] = string.upper(record["level"])
elseif record["log"] then
if string.match(record["log"], "ERROR") then
record["level"] = "ERROR"
elseif string.match(record["log"], "WARN") then
record["level"] = "WARN"
elseif string.match(record["log"], "DEBUG") then
record["level"] = "DEBUG"
else
record["level"] = "INFO"
end
end
-- 민감 정보 마스킹
if record["log"] then
record["log"] = string.gsub(record["log"], "password[=:][^%s]+", "password=***")
record["log"] = string.gsub(record["log"], "api[_-]?key[=:][^%s]+", "api_key=***")
record["log"] = string.gsub(record["log"], "token[=:][^%s]+", "token=***")
end
-- 메시지 길이 제한
if record["log"] and string.len(record["log"]) > 10000 then
record["log"] = string.sub(record["log"], 1, 10000) .. "...[TRUNCATED]"
end
return 1, timestamp, record
end
FluentBit 권장:
├── AWS 환경 (CloudWatch, OpenSearch)
├── 다중 목적지 필요
├── 최소 리소스 사용 필요
├── Lua 스크립트로 복잡한 처리
└── 레거시 시스템 통합
Promtail 권장:
├── Loki 전용 환경
├── 간단한 설정
├── Grafana 스택 표준화
└── 빠른 시작
Grafana Alloy 권장:
├── Grafana 통합 환경 (Loki + Prometheus + Tempo)
├── 새 프로젝트 (Promtail 대체)
├── River 설정 언어 선호
└── 메트릭 + 로그 + 트레이스 통합
OTEL Collector 권장:
├── 멀티 벤더 환경
├── 표준화된 텔레메트리 파이프라인
├── 기존 OTEL 계측 코드
└── 트레이스 중심 환경