궁금한게 많은 개발자 노트

[ Fluent Bit ] Backpressure 해결책 Buffering 본문

Back End

[ Fluent Bit ] Backpressure 해결책 Buffering

궁금한게 많은 개발자 2024. 5. 28. 19:55

Fluent Bit의 목표는 중앙(ElasticSearch)으로 로그를 모으고, 파싱하고, 필터링하고, 적재하는 것입니다. 이 과정에서 처리할 새로운 데이터를 받는데, 데이터를 빠르게 전송하지 못한다면 Back Pressure를 마주하게 됩니다.

 

Fluent bit을 사용하는 특정 시나리오에서는 일부 Target으로 Flush하는 것보다 로그나 데이터가 더 빠르게 수집되거나 생성될 수 있습니다. 이러한 일반적인 시나리오 중 하나는 특히 큰 백로그가 있는 큰 로그 파일에서 읽고 응답하는 데 시간이 걸리는 네트워크를 통해 로그를 백엔드로 디스패치하는 경우입니다. 이로 인해 Back Pressure가 발생하여 서비스에서 메모리 소비가 높아집니다.

 

Fluent Bit는 버퍼링으로 백프레셔와 일반적인 전송 실패 문제를 해결하고자 합니다. 버퍼링은 데이터가 적재되기 전까지 임시 저장소에 데이터를 보내는 것으로, Fluent Bit에서의 버퍼링은 기본적으로 메모리를 사용합니다. 다른 방안으로는 파일 시스템을 사용하는 것이 있는데, 파일 시스템 버퍼링을 사용하면 데이터를 안전하게 모을 수 있습니다. 메모리+파일시스템 버퍼링으로 안전하고 고성능으로 데이터를 처리할 수도 있습니다. 

 

 

 

Back Pressure를 예방하기 위해서는 Fluent Bit config에서 INPUT 플러그인이 수집할 수 있는 데이터 양을 제한하는 메커니즘을 엔진에 전달할 수 있는데, 이는  Mem_Buf_Limit 및 Storage.Max_Chunks_Up 구성 매개변수를 통해 수행됩니다.

INPUT 플러그인에 storage.type이 memory로 설정되어 있다면, 일반적으로 Fluent Bit는 메모리에 데이터를 가능한 만큼 저장합니다. 이것은 이것은 시스템 오버헤드가 적고 가장 빠른 방법이지만, 서비스가 느린 네트워크나 반응이 없는 서비스로 인해 기록을 빠르게 전달할 수 없다면, Fluent Bit 메모리 사용량은 늘어납니다. 전달할 수 있는 데이터보다 더 많은 양을 축적하기 때문이죠.  또한 Back Pressure + 장애를 일으킬 여지가 많은 환경에서 메모리 사용량이 높다면 Fluent Bit는 커널에 의해 종료될 수 있습니다.(OOM Killer)

 

Fluent Bit는 버퍼링에서 논리적인 queue를 사용합니다. Chunk의 태그에 따라 데이터를 목적지로 처리하고, Chunk의 양을 제한합니다. Chunk는 인풋 플러그인이 로그를 내뿜으면 엔진 그룹은 청크에 기록합니다. 청크 사이즈는 보통 2MB 정도로, 설정을 통해 엔진은 어디에 청크를 놓을 건지 정할 수 있습니다. 기본 설정에서 모든 청크는 메모리에서만 생성됩니다.

 

 

 

Memory Buffering

Input plugin mem_but_limit
백프레셔의 제 2의 해결책으로는 인풋 플러그인에서 기록되는 메모리의 양을 제한하는 것이 있습니다. mem_buf_limit 옵션으로 메모리의 양을 제한할 수 있는데요, 설정한 mem_buf_limit보다 더 많은 데이터를 받고 있다면 데이터가 제대로 전달되거나 빠져나갈 때까지 인제스트되지 않습니다. 

mem_buf_limit 설정이 특정 환경에서는 잘 먹힙니다. 이 설정은 서비스의 메모리 사용을 통제하는 걸 도와주지만, 만약 Fluent bit가 중지되는 동안 파일이 rotate된다면, 새로운 기록을 할 수 없으므로 데이터를 잃을 수도 있습니다.
이는 어느 인풋 소스에서든 발생할 수 있는 문제인데, mem_buf_limit의 목적은 메모리를 컨트롤하고 서비스를 살리기 위해서입니다. 따라서 모든 데이터를 안전하게 보장받길 원한다면, 파일 시스템 버퍼링을 사용하는 게 좋습니다.

[INPUT]
        Name tail
        Path /var/log/containers/*.log
        multiline.parser docker, cri
        Tag kube.*
        Mem_Buf_Limit 5MB
        storage.type filesystem
        Skip_Long_Lines On


 

 

File System Buffering

파일시스템 버퍼링으로도 백프레셔에 대처하고 메모리를 컨트롤할 수 있습니다. 메모리와 파일시스템 버퍼링은 둘 다 사용할 수 있어 상호 배타적이 아닙니다. 인풋 플러그인에 파일시스템 버퍼링을 사용하게 한다면 성능과 데이터 안정성을 둘 다 잡을 수 있습니다.

파일시스템 버퍼링이 허용되면, 엔진의 행동은 달라집니다. 청크가 생성될 때, 메모리에 데이터를 저장하면서 mmap을 통해 디스크에 데이터 복사본의 위치를 매핑해 해당 값이 다른 값을 가리키도록 합니다. 메모리에서 활성화된 청크는 디스크에 백업되는데, 이것을 up 이라 부릅니다. 청크 내용이 메모리에 올라갔기 때문입니다.

Fluent Bit은 메모리에 올라간 청크의 수를 조정합니다. 기본적으로는 128개의 청크가 메모리에 있을 수 있는데, storage.max_chunks_up 옵션으로 제어 가능합니다. 액티브 청크가 메모리에 올라가면 아웃풋으로 전달될 준비가 된 거고, 아직 로그를 받고 있습니다. 남아있는 청크는 down 상태인데, 파일시스템에만 있고 메모리에 올라가지 않았기 때문입니다. 이러한 청크가 전달될 준비가 되면 up 됩니다.

인풋 플러그인이 mem_buf_limit 과 storage.type 을 filesystem 에서 허용한다면, mem_buf_limit의 임계점에 도달했을 때, 플러그인이 멈추는 게 아니라 모든 새로운 데이터가 파일시스템의 다운 상태의 청크로 갑니다. 그래서 서비스의 메모리 사용량을 조절할 수 있고, 서비스가 데이터를 잃지 않게 보장해줍니다.

service: |
    [SERVICE]
        Daemon Off
        Flush 1
        Log_Level info
        Parsers_File /fluent-bit/etc/parsers.conf
        Parsers_File /fluent-bit/etc/conf/custom_parsers.conf
        HTTP_Server On
        HTTP_Listen 0.0.0.0
        HTTP_Port 2020
        Health_Check On
        storage.path /var/log/flb-storage/
        storage.sync normal
        storage.checksum off
        storage.max_chunks_up 128
        storage.backlog.mem_limit 5M

 

위 두 가지 버퍼링 방법을 조합하여, Fluent Bit를 안정적이면서 성능면에서도 이점을 가질 수 있도록 구현하면 좋을 것 같습니다. 언제든지 피드백은 환영이며, 더 자세한 내용은 아래 레퍼런스 링크 참고 부탁드립니다. 감사합니다 🙌

 

 

[ References ]

https://docs.fluentbit.io/manual/administration/buffering-and-storage

 

Buffering & Storage | 3.0 | Fluent Bit: Official Manual

If storage.path is set, Fluent Bit will look for data chunks that were not delivered and are still in the storage layer, these are called backlog data. Backlog chunks are filesystem chunks that were left over from a previous Fluent Bit run; chunks that cou

docs.fluentbit.io

https://docs.fluentbit.io/manual/administration/backpressure

 

Comments