DevOps

FluentBit 만으로 간단하게 Kubernetes에 올라간 서비스 logging 해보기

mokpolar 2023. 10. 21. 17:18
반응형

FluentBit 란?

 

FluentBit (https://docs.fluentbit.io/manual/about/fluentd-and-fluent-bit)

 

오픈소스 로그 프로세서 도구입니다. 

자체 비교표에 의하면,

같은 생태계의 Fluentd와 비교하여 고성능이고 메모리 사용량이 적다는 사실을 알 수 있습니다. 

출처: https://docs.fluentbit.io/manual/about/fluentd-and-fluent-bit

 

Fluentd와 Fluent Bit는 모두 Aggregator나 Forwarder로 독립적으로 사용될 수 있고 상호보완할 수도 있다고 설명하고 있습니다. 

 

출처 : https://docs.fluentd.org/deployment/high-availability

 

  • Log Forwarder : 로컬 이벤트를 수집하기 위해 일반적으로 모든 노드에 설치되며, 이벤트 수신시 Aggregator로 전달
  • Log Aggregator : Forwarder로부터 지속적으로 이벤트를 수신하는 데몬이며, 이벤트를 버퍼링하고 데이터를 업로드

 

 

어떤 상황에서 사용하는가?

 

위 내용과 관련해서 검색을 하다보면 아래와 같은 예시 구조를 볼 수 있습니다. 

그러니까 Forwarder와 Aggregator로 FluentBit와 Fluentd를 나눠서 쓰는 형태인 것 같습니다.

 

Daemonset 으로 FluentBit가 로그를 수집한 다음, 

Fluentd가 이를 모아서 처리하고 Elasticsearch나 Influexdb로 보내는 형태입니다. 

 

출처 : https://logz.io/blog/fluentd-vs-fluent-bit/

이 구조에서는 이렇게 설명하고 있습니다. 


"Fluent Bit can be used on it own of course but has far less to offer in terms of aggregation capabilities and with a much smaller amount of plugins for integrating with other solutions."

 

단독으로도 사용할 수 있지만, aggreator로서의 capability도 그렇고, 플러그인도 적다.. 는 것 같습니다. 

 

하지만 복잡한 구조가 필요하지 않을 때, logging 구조를 간단하게 갖고갈 때 그냥 FluentBit 만 쓸 수도 있을 것 같습니다. 

 

 

FluentBit만 올려서 특정 서비스의 로그를 수집해서 변형하여 보내보자

특정 인스턴스에만 Daemonset으로 올리기

 

공식 홈페이지에서 설명하는 일반적인 방법으로 쿠버네티스 클러스터에 설치를 해봅니다. 

https://docs.fluentbit.io/manual/installation/kubernetes

https://github.com/fluent/helm-charts

 

대신 설치할 때 원하는 특정 서비스가 올라간 인스턴스의 로그만 갖고 오기 위해

서비스를 특정 인스턴스에만 올리고,   toleartion과 affinity를 추가해줍니다.

 

그리고 helm value를 갖고와서 변경을 해줍니다. 

helm repo add fluent https://fluent.github.io/helm-charts
helm show values fluent/fluent-bit > fluent-bit-values.yaml

 

fluentbit helm values에 아래와 같은 형태로

toleration과 affinity가 들어가게 반영해줍니다. 

tolerations:
  - effect: NoSchedule
    key: type
    operator: Equal
    value: sample-service
 
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: type
          operator: In
          values:
          - sample-service

 

 

Fluentbit의 Data pipeline

Fluentbit 문서를 보면 Data pipeline은 아래와 같은 형태를 가집니다. 

 

Input으로 컨테이너 로그를 긁어와서 Parser들을 적용하여 Filter에서 변형을 하며, 

각각의 Output을 보내는 형태입니다. 

 

Input 

Kubernetes CRI 사용시,  Input Parser를 수정해야 컨테이너의 로그를 가져올 수 있습니다. 

 

이에 대한 공식 문서 설명은 아래와 같습니다

Fluent Bit by default assumes that logs are formatted by the Docker interface standard. However, when using CRI you can run into issues with malformed JSON if you do not modify the parser used. Fluent Bit includes a CRI log parser that can be used instead. An example of the parser is seen below:

 

설명에 따라, values에서 Input을 변경합니다. 

[PARSER]
    Name cri
    Format regex
    Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<message>.*)$
    Time_Key    time
    Time_Format %Y-%m-%dT%H:%M:%S.%L%z
    
    
[INPUT]
    Name tail
    Path /var/log/containers/*.log
    Parser cri
    Tag kube.*
    Mem_Buf_Limit 5MB
    Skip_Long_Lines On

 

로그는 아래와 같이 생겼다고 가정합니다. 

timestamp stdout F {"level": "INFO", "log_type": "service", "message": "###", ... }

 

특정 서비스의 로그만 긁어오기 위해 Input 의 대상이 되는 Namespace 를 제한해보자 

 

로깅의 대상이 되는 서비스의 이름을 service라고 하고, sample이라는 네임스페이스에 올린다고 가정해봅니다. 

그래서 서비스는 service라는 이름은 Deployment로 배포를 했습니다. 

 

그리고 해당 인스턴스에 접근해서, 어떤 형태로 컨테이너의 로그가 남는지 확인해보겠습니다. 

$ cd /var/log/containers
$ ls
...
service-7cbf4f5c33_sample_service-ctr-...4f3c.log
...

확인한 바와 같이 해당 인스턴스에

{deployment명}-{hash값}_{namespace명}_{container명}-{hash값}.log

이런 형태로 로그파일이 떨어지는 모습을 볼 수 있습니다. 

 

그러니 다른 로그들을 빼고,

원하는 서비스의 로그만 갖고 올 수 있도록 Input을 좀 더 변형해보겠습니다.

 

아래와 같이 Path를 정의하면 sample 네임스페이스에 있는 service deployment의 로그들만 갖고 올 것으로 예상합니다. 

그리고 파이프라인을 제한하기 위해 service라고 Tag도 달아줍니다.

[INPUT]
    Name              tail
    Path              /var/log/containers/service*_sample_*.log
    Tag               service.*
    Parser            cri
    Refresh_Interval  5
    Mem_Buf_Limit     5MB
    Skip_Long_Lines   On

 

Filter

Filter에서는 기본 제공하는 Parser나 추가로 Custom parser를 정의해서 log를 처리할 수 있습니다. 

json parser는 기본으로 제공되기에 위 로그에서 간단하게 message만 갖고와서 보낼 수 있게 Filter를 정의하겠습니다.

 

Input에서 service.* 으로 태그를 만들었기 때문에 이 태그들을 처리할 수 있게 Match도 달아줍니다.

[FILTER]
    Name         parser
    Match        service.*
    Key_Name     message
    parser       json

 

다양한 Filter 들

https://docs.fluentbit.io/manual/pipeline/filters

 

Parser 말고도 다른 Filter들을 사용해서 log를 다룰 수 있습니다. 

 

예를 들면 Grep이 있습니다. 

https://docs.fluentbit.io/manual/pipeline/filters/grep

 

아래와 같이 순차적인 규칙을 나열하면서 log를 변형시켜 나갈 수 있습니다. 

[INPUT]
    name   tail
    path   lines.txt
    parser json

[FILTER]
    name   grep
    match  *
    regex  log aa

[OUTPUT]
    name   stdout
    match  *

 

CustomParser

https://docs.fluentbit.io/manual/pipeline/parsers/configuring-parser

 

추가로 정의한 Parser를 쓰고 싶을 수 있습니다.

이럴 때 아래 Parser를 정의하여 Filter에 적용해주면 됩니다. 

  customParsers: |
    [PARSER]
        Name        apache
        Format      regex
        Regex       ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<log>.*)$
        Time_Key    time
        Time_Format %Y-%m-%dT%H:%M:%S.%L%z

 

 

Output

이제 처리된 로그를 어디엔가로 보내서 저장해야 합니다. 

간단하게 AWS S3에 저장할 수 있도록, sample-bucket이라는 버킷을 만듭니다.

 

그리고 1시간마다 로그들을 긁어모아 fluentbit에서 S3 버킷으로 업로드 되도록 upload_timeout은 60m으로 해줍니다. 

[OUTPUT]
  Name                         s3
  Match                        service.*
  bucket                       sample-bucket
  region                       ap-northeast-2
  total_file_size              250M
  upload_timeout               60m
  s3_key_format                /$TAG[2]/$TAG[0]/yyyy=%Y/mm=%m/dd=%d/$UUID.json
  s3_key_format_tag_delimiters .-

 

key_format 설정을 위와 같이 하면 

 

원하는 모습대로 아래와 같이 파일이 떨어질 것입니다. 

s3://sample-bucket/service/yyyy=2023/mm=10/dd=21/XkuzSeq0.json

 

 

AWS 권한 문제

FluentBit가 무슨 권한으로 AWS S3에 저장하냐는 의문이 들 수 있습니다. 

운영환경이라 IRSA가 적용되어 있다는 가정하에, FluentBit가 사용할 Service Account에 

아래와 같이 arn을 주어서 설치할 수 있습니다. 

serviceAccount:
  create: true
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::******:role/*************

 

 

 

마무리

여러 상황이 있을 수 있겠지만, 

간단하게 타겟팅하는 서비스만 로깅하고 싶고 FluentBit 이상으로 무겁게 갖고 가고 싶지 않을 수도 있습니다. 

 

이럴때 이렇게 FluentBit를 이용해서 

특정 인스턴스에 올라간 서비스의 로그만 로깅하도록,  특정 네임스페이스의 원하는 pod의 로그만 갖고 올 수도 있을 것 같습니다. 

반응형