ALLSSU

containerd 런타임에서 Fluent Bit으로 애플리케이션 JSON 로그 깨짐 해결하기

Amazon EKS에서 containerd 런타임으로 변경 후 JSON 로그가 깨지는 현상과 aws-for-fluent-bit 헬름차트의 Parser 설정으로 해결하는 방법

2023년 4월 22일 토 18:14

로그에서 JSON 앞에 뭐가 붙어서 깨졌다

결론부터

애플리케이션 로그를 JSON으로 만들어서 Fleunt Bit을 통해 Amazon OpenSearch Service에서 보고 있는데, 쿠버네티스 업데이트 이후 JSON 데이터가 깨지는 현상이 발생했다. Amazon EKS v1.24부터 Dockershim 제거 및 containerd를 사용했을 때 기본 로그 포맷이 달라서 발생하는 문제였다. aws-for-fleunt-bit Helm Chart의 value를 오버라이드하고, CRI(컨테이너 런타임 인터페이스) Parser를 만들어서 해결했다.

conatinerd를 사용할 때의 JSON 로그 데이터 깨짐과 해결 방법

Kubernetes v1.24부터 컨테이너 런타임인 Dockershim이 제거됐다. Amazon EKS는 v1.24로 업데이트하면 컨테이너 런타임 인터페이스(CRI)가 conatinerd를 사용하도록 알아서 변경된다. (여러모로 관리형 서비스가 좋다)

Dockershim에 의존성이 없다면 containerd를 쓰는 것은 별문제가 없는데, 로그 파이프라인에서 사이드이펙트가 발생한다. Fluent Bit을 통해 ElasticSearch에 로그를 전달하는 EFK Stack을 사용한다고 가정하고, 애플리케이션에서 만든 JSON 로그를 Kibana에서 확인하면 아래와 같이 JSON 포맷이 깨져서 로그 트리 탐색을 할 수가 없게 된다.

2023-04-21T07:32:00.356711687Z stdout F {"field1":"data","field2":{"sub1":"data","sub2":"data"}}
conatinerd로 변경 후에 JSON 로그 포맷 앞에 날짜와 stdout F 또는 stderr F가 붙게 된다

애플리케이션에서 JSON 로그로 출력하면 ElasticSearch 또는 OpenSearch에서 애플리케이션의 로그를 트리 형태로 탐색할 수 있게 된다. 그러나 JSON 앞에 시간과 stdout F라는 문자가 붙게 되니 문자열로 보던 로그보다 더 불편하게 보게 되는 이슈가 생겼다. 더 찾아보니 containerd에서 사용하는 CRI(Container Runtime Interface) 로그 포맷 자체가 기존의 Docker(Dockershim)과 다른 이슈가 있었다. 그리고 Fluent Bit은 Docker Runtime에 대해 파서를 제공하고 있고, CRI에 대해서는 제공하고 있지 않아 커스텀 파서가 필요하다. 아래 링크들에서 여러 개발자의 고민이 보이는데, 예전부터 dockershim 종료에 따라 containerd로 변경했을 때 JSON 로그 깨짐을 겪었던 것 같다.

해당 이슈들을 보니 공통적으로 커스텀 파서를 만들고 Input에서 파서를 연결하는 방법을 제안한다. 아래와 같이 Fluent Bit에서 정규식이 포함된 Parser를 만들고, Input에서 파서를 연결하면 해결할 수 있어 보였다.

[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
Fluent Bit에서 새로운 Parser를 만들고, Input에 해당 Parser를 사용하게 하면 해결된다

나중에 알게 된건데😭, Fluent Bit 공식 문서에서도 CRI 파서에 대해 언급하고 있다. Fluent Bit은 Docker(Dockershim) 로그를 기본으로 가정한다고 하고, JSON 데이터를 파싱하려면 새로 추가하라고 친절하게 적혀져 있다. 또한, 문서에 적혀있는 주석에 따라 https://rubular.com/r/tjUt3Awgg4 링크로 들어가면 정규식이 어떻게 파싱 되는지 미리 확인할 수 있다.

aws-for-fluent-bit Helm Chart에서 Input과 Parser 구성하기

aws-for-fluent-bit은 도커 이미지 또는 헬름차트로 제공되는데, fluentbit을 Amazon ECS 또는 Amazon EKS에서 쉽게 설치하고 사용하기 편하도록 제공하는 소프트웨어다. 아래에 있는 AWS 서비스들을 간단한 설정으로 쉽게 연결할 수 있도록 fluentbit을 추상화해서 제공한다.

  • Amazon Cloudwatch (+ Cloudwatch Logs)
  • Amazon OpenSearch Service
  • Amazon Kinesis (+ Kinesis Data Firehose)
  • Amazon S3

aws-for-fluent-bit을 쿠버네티스 클러스터에 Helm Chart로 설치하면 기본적인 Input과 Parser가 세팅된다. 그 설정을 오버라이드 해주면 되는데, aws-for-fluent-bit Artifact Hub에서 기본값들을 확인하고, 클러스터에 적용한 Helm Chart의 values.yaml 파일을 편집하면 된다.

기본값 오버라이드 하는 방법

aws-for-fluent-bit에서 추상화 한 항목들을 오버라이드 하는 방법

service 부분의 extraParsers를 cri 파서로 추가해 준 다음 input 부분의 parser을 cri로 지정해 주면 간단하게 수정할 수 있다. 아래와 유사한 values.yaml을 만들고 배포하면 된다.

service:
  extraParsers: |
    [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

input:
  parser: cri
원하는 항목들만 오버라이드 해서 사용하면 된다. 기존에 AWS 서비스를 연결하던 value들은 그대로 놔두고.

정리 및 회고

  • aws-for-fluent-bit를 프로비저닝 할 때, CRI를 파싱하는 기본 파서를 하나 만들어 주면 좋을 것 같다. 어차피 EKS를 1.24로 올리거나 새로 만들면 containerd를 사용하게 될 텐데, CRI 용도의 parser을 미리 만들어 주면 굳이 새로 안 만들고 사용자가 선택하기 쉬울 것 같다. (CRI 런타임별로 로그 포맷이 달라지면 이 또한 골치 아플 것 같기도 하다)
  • 뭐든 상관 없이 생각하지도 못한 부분에서 사이드이팩트가 난다. 버전 업데이트를 포함한 모든 작업은 꼼꼼하게 보는 게 중요하다.
  • Fluent Bit을 추상화된 aws-for-fluent-bit으로 사용했기 때문에 Input, Parser을 오버라이드 하는 부분을 포함해서 모르는 부분이 많았다. 사이드이펙트와 장애는 매번 새로운 가르침을 준다. 발생이 안 되면 좋겠지만 이번에도 배워가는 부분이 하나 더 생겼다.