목적: Fluentbit를 적용하여 쿠버네티스 기반 로그환경 마련 자체 로그환경 구성 |
EFK 소개

EFK 스택을 활용한 Kubernetes환경에서의 통합 로깅체계 구축 및 로깅 중앙화
- Pod가 삭제 되어도 삭제된 Pod 내의 컨테이너 로그를 남길 수 있음
- 로그를 중앙 집중식으로 저장하고 분석해 MSA 환경에서의 로그 관리의 어려움을 해결
- 고가용성을 제공하고 많은 양의 데이터를 저장할 수 있고 실시간으로 검색, 분석, 저장 가능
- 저장된 로그를 웹으로 시각화 해서 차트나 그래프로 볼 수 있음
- 보내지는 데이터의 유형에 따라 로그 대쉬보드 + Metric 대쉬보드 구현 가능
- cluster내에 container log 수집 가능
- cpu, memory, disk 현황 데이터 수집 가능
Fluent-bit 소개

INPUT -> (PARSER) -> FILTER -> BUFFER -> (ROUTER) -> OUTPUT
fluent bit은 위와같은 파이프라인을 가지고 log를 지정한 목적지로 보내쥽니다
Fluent-bit Config
Fluent Option | 설명 | 기타 |
INPUT | fluent bit이 수집한 로그, 메트릭은 event 또는 record라고 합니다. 해당 파이프라인에 어떤 record를 수집할지를 정하게 됩니다. | (공식) Document |
PARSER | parser에서 비 정형데이터를 정형 데이터로 변경시킬 수 있습니다. 정규표현식 기반입니다. docker 파서는 json형태로 변경해서 log라는 key에 stdout을 담아줍니다. | 공식) Document |
FILTER | FILTER에 적절한 옵션을 지정하면 원하는 정보만 목적지로 보낼 수 있습니다. 혹은 설정에 따라 원하는 데이터만 추가적인 Parser를 적용할 수도 있습니다. | |
BUFFER | 버퍼는 입출력의 속도차이가 생기는것을 고려해 잠시 저장하는 공간입니다. memory(ram)이나 file에 저장할수 있으며 tag단위로 chunk가 생성됩니다. | |
OUTPUT | 결과를 어디로 보낼지 지정하는 곳입니다. cloudwatch, s3, firehose, htt, ES, Logstash 등 원하는 OUTPUT으로 지정한 RECORD를 보낼 수 있습니다. | 공식) Document |
Kubernetes Cluster 배포

배포 파일 리스트
배포시에 아래의 파일들이 필요합니다. 각 파일 순서대로 배포 해야 하며 저희 환경기준에서는 Logging Namespace에 구성 하였습니다.
각각의 로그들은 Configmap에서 설정한 옵션에 따라 데이터를 수집하고 필터하고 파씽하여 최종적으로 보내는 작업까지 거치게 됩니다.
Service-account.yaml
- 서비스 계정을 만들어 준다고 생각하면 됩니다
- 우리는 서비스 계정을 통해 Role을 지정하고 바인딩 해줄 생각입니다.
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluent-bit
namespace: logging
Role.yaml
- fluent-bit-read라는 Role을 생성하고 읽는 권한만 추가(RBAC)하였습니다 (except Update)
- RBAC 를 통해 Role을 사용하는 주된 목적은 보완적인 측면때문 입니다. (아무 리소스나 접근 하면 안됨)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: fluent-bit-read
rules:
- apiGroups: [""]
resources:
- namespaces
- pods
verbs: ["get", "list", "watch"]
Role-binding.yaml
- 위에서 생성한 Role을 기준으로 service-account를 바인딩하여 줍니다.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: fluent-bit-read
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: fluent-bit-read
subjects:
- kind: ServiceAccount
name: fluent-bit
namespace: logging
ConfigMap.yaml
- 실제 Fluent-bit의 각 레이어별(수집 → 전송) 설정을 할 수 있습니다.
- Krakend Log 수집을 위해 Input 옵션을 사용하고 있습니다.
- Kubernetes 자원들의 정보들을 수집하기 위해서는 Filter 옵션을 사용합니다.
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: logging
labels:
k8s-app: fluent-bit
data:
# Configuration files: server, input, filters and output
# ======================================================
fluent-bit.conf: |
[SERVICE]
Flush 1
Log_Level info
Daemon off
Parsers_File parsers.conf
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
[OUTPUT]
Name stdout
Match *
[OUTPUT]
Name http
Match *
Host amazonaws.com
Port 8080
Format json
@INCLUDE input-kubernetes.conf
@INCLUDE filter-kubernetes.conf
input-kubernetes.conf: |
[INPUT]
Name tail
Tag kube.*
#Path /var/log/containers/*.log
Path /var/log/containers/hello*.log
Parser docker
DB /var/log/flb_kube.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
filter-kubernetes.conf: |
[FILTER]
Name kubernetes
Match kube.*
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
parsers.conf: |
[PARSER]
Name apache
Format regex
Regex ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name apache2
Format regex
Regex ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name apache_error
Format regex
Regex ^\[[^ ]* (?<time>[^\]]*)\] \[(?<level>[^\]]*)\](?: \[pid (?<pid>[^\]]*)\])?( \[client (?<client>[^\]]*)\])? (?<message>.*)$
[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
[PARSER]
Name json
Format json
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep off
[PARSER]
# http://rubular.com/r/tjUt3Awgg4
Name cri
Format regex
Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<message>.*)$
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
[PARSER]
Name syslog
Format regex
Regex ^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$
Time_Key time
Time_Format %b %d %H:%M:%S
DeamonSet.yaml
- Daemonset은 각 노드별로 1개의 파드가 떠서 각 노드별 Contianer에서 발생되는 로그를 수집할 수 있도록 하는게 목적입니다.
- 각기 다른 노드에서 로그를 읽어 올 수도 있겠지만 비효율적이라 각 노드별로 로그를 수집하도록 하기 위해 DaemonSet을 활용하는 것입니다.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: logging
labels:
k8s-app: fluent-bit-logging
version: v1
kubernetes.io/cluster-service: "true"
spec:
selector:
matchLabels:
k8s-app: fluent-bit-logging
template:
metadata:
labels:
k8s-app: fluent-bit-logging
version: v1
kubernetes.io/cluster-service: "true"
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "2020"
prometheus.io/path: /api/v1/metrics/prometheus
spec:
containers:
- name: fluent-bit
image: fluent/fluent-bit:1.5
imagePullPolicy: Always
ports:
- containerPort: 2020
env:
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch"
- name: FLUENT_ELASTICSEARCH_PORT
value: "9200"
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: fluent-bit-config
mountPath: /fluent-bit/etc/
terminationGracePeriodSeconds: 10
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: fluent-bit-config
configMap:
name: fluent-bit-config
serviceAccountName: fluent-bit
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
- operator: "Exists"
effect: "NoExecute"
- operator: "Exists"
effect: "NoSchedule"
Fluent-bit 상세 설정

Fluent-bit 가이드에 따른 설정으로는 Kubeernate cluster내에 생성되는 파드의 모든 로그를 보내게 되어 있습니다.
이번에 구축하면서 알게된 사실은 각각의 로그들은 Node Host의 /var/log/container/{POD_NAME}-{UID).log 형식으로 저장하게 됩니다. 또한, 시간에 따라 로그가 삭제될 수 있도록 LogRotate 형식을 취하고 있습니다.
데이터 수집 설정
@INCLUDE input-kubernetes.conf
input-kubernetes.conf: |
[INPUT]
Name tail
Tag kube.*
# 특정 서비스 파드에서 발생하는 로그 수집
Path /var/log/containers/hello*.log
Parser docker
DB /var/log/flb_kube.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
- 우리가 일반적으로 kubectl logs -f POD_NAME -n logging 을 하게 되면 kubernete를 통해 /var/log/container/{POD_NAME}-{UID).log 파일을 tailing 해주게 됩니다.
- 하지만 지금 프로젝트에서 필요한 로그는 hello에 관한 로그만 필요하기에 수집되는 데이터를 hello로 한정하여 줍니다.
- Path /var/log/containers/hello-lg*.log
데이터 전송
# 단순 확인을 위한 stdout 출력
[OUTPUT]
Name stdout
Match *
# Logstash 연동을 위한 전송설정(특이 케이스)
[OUTPUT]
Name http
Match *
Host amazonaws.com
Port 8080
Format json
- 수집된 데이터를 출력하여 확인하는 용도로 stdout으로 출력 합니다
- 최종적으로 Logstash와의 연동을 위해 http로 전송해 주는 옵션을 추가 하였습니다.
- 해당 설정이 일반적인 경우가 아니라 es와 같은 지정된 프로토콜 전송이 아닌 http전송을 활용 하였습니다.
- Logstash에 추가 pipeline configuration 이 필요합니다.
결과 및 확인

일전에는 kube-system로그와 다른 파드들에서 나오는 로그들로 인해 제대로 필터 되지 않았지만
Fluent-bit의 Configmap 설정을 프로젝트에 맞게 수정 후 오직 타겟 서비스 로그만, Fluent를 통해 수집되는 것을 확인 할 수 있었습니다.
참고
(공식) Fluent-bit kubernete EFK-Fluent-bit 사용법 |
'개발' 카테고리의 다른 글
Excalidraw 아키텍쳐 그릴때 쓰기 좋은 툴 (0) | 2024.02.14 |
---|---|
Fluent Operator 이해하기 (2) | 2024.01.25 |
ECR Multi Architecture(amd64/arm64) 이미지 저장하기 (0) | 2023.03.23 |
Kubernates Rolling Update 무중단 배포 (2) | 2022.12.28 |
MSA 성능 향상을 위한 gRPC 이해 및 적용 (0) | 2022.11.18 |