Observability/Fluent-Bit

[EFK] 쿠버네티스 환경 내 Fluent-Bit 사이드카로 엘라스틱 서치 로깅

dev_ss 2024. 9. 13. 12:07

 

 

 


 

 

이전 글에서는 로그 수집기의 종류와 아키텍처를 알아보았다.

 

 

 

[ELK/EFK] 로그 수집 - Filebeat / Logstash / Fluentd / Fluent-bit

최근에는 많은 기업들이 Elastic 재단의 ELK stack(또는 EFK stack)을 활용하여 로그를 수집하고 이를 시각화하여 운영 상 발생하는 이슈를 잡아내고 비즈니스 의사결정에 참고를 한다. 저번의 글에서

ssnotebook.tistory.com

 

 

이번 글에서는 Fluent-Bit을 쿠버네티스 파드 내에서 사이드카 형식으로 구축을 하고, 수집한 로그 정보를 엘라스틱 서치로 바로 전송하는 과정을 알아볼 것이다.

 


사이드카란

 

사이드카란 이륜차 옆에 좌석을 설치하여, 두 사람이 함께 같이 탈 수 있게 한 유형의 이륜차를 의미한다.

아래 사진을 참고하면 좀 더 쉽게 이해할 수 있을 것이다.

 

[출처 : https://namu.wiki/w/%EC%82%AC%EC%9D%B4%EB%93%9C%EC%B9%B4]

 

 

이를 쿠버네티스 환경에 개념적으로 도입한다는 것인데, 쿠버네티스 파드는 일반적으로 한 개 이상의 컨테이너로 구성되어 있는데, 두 개 이상의 멀티 컨테이너 방식으로 운용하는 것을 사이드카 패턴이라고 한다.

 

[출처 : https://kubernetes.io/ko/docs/concepts/workloads/pods/]

 

 

 

기본적으로 파드 내부의 컨테이너 간 네트워크는 공유가 되지만, 파일 시스템은 공유가 되지 않기 때문에, 로그가 저장되는 디렉터리를 각 컨테이너에 마운트 하여 애플리케이션에서 발생시키는 로그를 로그 수집기가 이용할 수 있도록 만들어줘야 한다.

 

파드 내 구조를 보자면 아래와 같이 나타낼 수 있다.

[파드 내 사이드카 패턴 참고 자료]

 

 


 

쿠버네티스 Docs에서도 로깅 아키텍처에 대하여 다루고 있는 부분이 존재한다.

 

https://kubernetes.io/ko/docs/concepts/cluster-administration/logging/

 

로깅 아키텍처

애플리케이션 로그는 애플리케이션 내부에서 발생하는 상황을 이해하는 데 도움이 된다. 로그는 문제를 디버깅하고 클러스터 활동을 모니터링하는 데 특히 유용하다. 대부분의 최신 애플리케

kubernetes.io

 

 

위 사이트에서는 크게 두 가지 방식으로 수집하는 것을 확인할 수 있다.

 

 

[출처 : https://kubernetes.io/ko/docs/concepts/cluster-administration/logging/]

 

 

특정 노드에 띄워진 파드가 노드 내에 로그를 남기고, 노드에 Daemonset으로 존재하는 Agent가 이를 수집하는 노드 로깅 방식과,

 


 

[출처 : https://kubernetes.io/ko/docs/concepts/cluster-administration/logging/]

 

 

특정 파드 내에서 생성되는 로그를 사이드카 형식으로 동일한 파드 내에서 Agent가 Logging Backend로 전송하는 사이드카 로깅 방식을 볼 수 있다.

 

 

+ 사이드카 방식은 리소스 낭비(Agent의 단일 파드에 대한 활용)로 이어질 수 있고, kubelet에서 로그를 제어할 수 없는 단점이 존재한다.


 

실습

 

다음은 Deployment의 객체에서 하나의 파드에서 아래 두 개의 컨테이너가 동작하게끔 만든 yaml이다.

 

1. 로그를 남기는 ubuntu 컨테이너

2. 로그를 수집할 Fluent-bit의 컨테이너

 

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-log-deploy
  labels:
    deployment: test-log-deploy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test-log-app
  template:
    metadata:
      labels:
        app: test-log-app
        microservice: logger
        project: test
    spec:
      containers:
      - name: test-log-container
        image: ubuntu:latest
        volumeMounts:
        - name: log-volume
          mountPath: /logs
        command: ["/bin/sh", "-c"]
        args:
        - |
          while true; do echo $(date +"%Y-%m-%dT%H:%M:%S.%9N") [INFO] log message >> /logs/current_time.log; sleep 1; done
      - name: fluent-bit-sidecar
        image: fluent/fluent-bit:3.1.3
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: POD_UID
          valueFrom:
            fieldRef:
              fieldPath: metadata.uid
        - name: APP_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['app']
        - name: MICRO_SERVICE_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['microservice']
        - name: PROJECT_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['project']
        volumeMounts:
          - name: log-volume
            mountPath: /logs
          - name: fluent-bit-sidecar-config
            mountPath: /fluent-bit/etc/
      volumes:
      - name: log-volume
        emptyDir:
          sizeLimit: 2Gi
      - name: fluent-bit-sidecar-config
        configMap:
          name: test-fluent-config

 

 

ubuntu의 컨테이너에서 /logs 하위에 사진과 같은 로그를 남기고 해당 디렉터리를 Fluent-bit 컨테이너에 마운트 시켜 수집해갈 수 있도록 만든 것이다.

 

[저장되는 로그 예시]

 

 

다음은 Fluent-bit 컨테이너에서 사용할 config파일의 Configmap이다.

 

apiVersion: v1
kind: ConfigMap
metadata:
  name: test-fluent-config
data:
  parsers.conf: |
    [PARSER]
      Name parser
      Format Regex
      Regex /^(?<time>[0-9-T]*[^ ]*) (?<level>[\Sa-zA-Z]*[^ ]) (?<log>[.]*)?$/
      Time_Key time
      Time_Format %Y-%m-%dT%H:%M:%S.%L
      Time_Keep On

  fluent-bit.conf: |
    [SERVICE]
        Parsers_File        parsers.conf
    [INPUT]
        Name                tail
        Path                /logs/*.log
        Parser              parser
        Tag                 <date>
        Tag_Regex           /logs/(?<date>[\S]+).log
        Refresh_Interval    10
        Mem_Buf_Limit       50MB
        Skip_Long_Lines     On
    [FILTER]
        name parser
        Match *
        Parser parser
        Key_Name parser
    [FILTER]
        name modify
        Match *
        Add microservice ${MICRO_SERVICE_NAME}
    [OUTPUT]
        Name es
        Match *
        Host [엘라스틱 서치 HOST]
        Port [엘라스틱 서치 PORT]
        HTTP_USER [엘라스틱 서치 유저]
        HTTP_Passwd [엘라스틱 서치 비밀번호]
        tls Off
        tls.verify Off
        Logstash_Prefix ${PROJECT_NAME}-${MICRO_SERVICE_NAME}
        Logstash_Format On
        Retry_Limit False
        Buffer_Size 256KB
        Time_Key time

 

 

옵션에 대하여 간단하게 설명하자면, Fluent-bit에서 로그를 읽고 기입된 정규식 표현에 따라 파싱 하고, 환경 변수 등을 넘겨받고 추가한 데이터를 엘라스틱 서치로 전송하는 내용이다.

 

 

 

 

위 Deployment 및 Configmap의 작성 후 오브젝트를 생성하고, 키바나에 접속하여 엘라스틱 서치에 저장되는 데이터를 확인하려면 다음과 같이 접속하여 해당 인덱스의 패턴을 만들어 줘야 한다.

 

 

 

1. Management -> Stack Management 접속

 

 

 

 

 

 

 

 

2. Kibana -> Index Patterns 접속

 

 

 

 

 

 

3. Create index pattern 접속

 

 

 

위 과정을 정상적으로 따라온다면 아래와 같은 정보를 볼 수 있다.

 

 

 

하위에 Fluent-bit의 Config에서 설정한 양식으로 인덱스가 생성됐다는 것을 볼 수 있다.

 

이 인덱스를 활용하기 위해 인덱스 패턴을 등록해 준다.

 

 

 

 

 

4. Index-name 설정 및 패턴 등록

 

 

패턴으로는 생성된 index명과 뒤에는 정규식으로 모든 하위 모든 패턴을 등록해 준다는 *(Asterisk)로 입력한다.

 

그리고 Next step으로 넘어가면 Time으로 어떤 field를 사용할지 선택하는 부분이 나온다.

 

 

Fluent-bit의 Config에서 정규식으로 파싱 해서 얻은 time이라는 Key를 이용한다고 설정했으므로 time으로 선택 후 패턴을 생성한다.

 

 

 

 

5. 데이터 확인 (Analytics -> Discover 접속)

 

인덱스 패턴을 만들면 인덱싱 된 패턴으로 데이터를 확인할 수 있다.

 

 

 

 

 

좌측 상단에서 인덱스 패턴을 선택한다.

 

 

 

 

 

그러면 다음과 같이 실시간으로 저장되고 있는 데이터를 확인할 수 있다.

 

 

 


참고

 

아래는 데이터에 정규식 표현을 대입하여 어떻게 파싱이 되는지 미리 확인해 볼 수 있는 사이트다.

 

https://rubular.com/

 

Rubular

Ruby-based regular expression editor/tester

rubular.com

 

 

 

+ 실습에서는 App이 남기는 로그 파일에 대하여 데이터를 읽고 전송하는 방식으로 구현을 했었는데, 이를 stdout과 stderr을 활용하여 구축한다면 더 효율적인 아키텍처가 될 것이다.

 

 

 


 

 

 

반응형