본문 바로가기

인프라,데이터/Fluentd, Fluent Bit

Fluent Bit의 버퍼링Buffering (번역)

Fluent Bit의 공식 문서에서 버퍼링과 스토리지, 백프레셔에 관해 번역한 글입니다.

 

백프레셔

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

특정한 환경에서는 로그나 데이터가 목적지로 보낼 수 있는 여력보다 빠르게 유입됩니다. 흔한 케이스로는 큰 로그 파일을 읽어오고 네트워크를 통해 백엔드로 보내는 것이 있는데, 응답하기까지 시간이 걸리고, 이것은 서비스에서 큰 메모리 소모를 야기하는 백프레셔를 생성하게 됩니다.

 

버퍼링

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

파일 시스템 : 컴퓨터 파일에 이름을 붙이고, 저장이나 검색을 위해 논리적으로 그것들을 어디에 위치시켜야 하는지 등을 나타내는 방법.

인풋 플러그인에 설정된 메카니즘이 메모리라면, Fluent Bit는 메모리에 데이터를 가능한 한 저장합니다. 이것은 시스템 오버헤드가 적고 가장 빠른 방법이지만, 서비스가 느린 네트워크나 반응이 없는 서비스로 인해 기록을 빠르게 전달할 수 없다면, Fluent Bit 메모리 사용량은 늘어납니다. 전달할 수 있는 데이터보다 더 많은 양을 축적하기 때문이죠. 

백프레셔+고하중의(장애를 일으킬 여지가 많은) 환경에서 메모리 사용량이 높다면 Fluent Bit는 커널에 의해 종료될 수 있습니다.(OOM 킬러).

로그 프로세서(Fluent Bit)의 퍼포먼스 하락이나 스트리밍이 안 되는 현상은 사업의 크리티컬한 로그를 통째로 잃어버릴 수 있습니다. 혹은 로그가 일관성 없거나 듬성듬성하게 들어올 수 있죠.

 

Fluent Bit는 버퍼링에서 논리적인 queue를 사용합니다. Chunk*의 태그에 따라 데이터를 목적지로 처리하고, Chunk의 양을 제한합니다. 

청크Chunks : 인풋 플러그인(소스)가 기록을 내뿜으면 엔진 그룹은 청크에 기록됩니다. 청크 사이즈는 보통 2MB 정도로, 설정을 통해 엔진은 어디에 청크를 놓을 건지 정할 수 있습니다. 기본 설정에서 모든 청크는 메모리에서만 생성됩니다.

 

mem_but_limit

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

mem_buf_limit 설정이 특정 환경에서는 잘 먹힙니다. 이 설정은 서비스의 메모리 사용을 통제하는 걸 도와주지만, 만약 Fluentbit이 중지되는 동안 파일이 회전된다면, 새로운 기록을 할 수 없으므로 데이터를 잃을 수도 있습니다. 이는 어느 인풋 소스에서든 발생할 수 있는 문제인데, mem_buf_limit의 목적은 메모리를 컨트롤하고 서비스를 살리기 위해서입니다.

따라서 모든 데이터를 안전하게 보장받길 원한다면, 파일 시스템 버퍼링을 사용하는 게 좋습니다.

 

파일시스템 버퍼링

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

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

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

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

 

파일시스템에서 Chunks를 위한 공간 제어

위에서 Fluent Bit는 버퍼링에서 논리적인 queue를 사용하고 말씀드렸는데요, Chunk의 태그에 따라 데이터는 여러 목적지로 보내질(라우팅) 수 있습니다. 내부적으로 청크가 어디서 생성되고 어디로 갈지 설정에서 기재할 수 있죠.

청크가 여러 목적지를 가진다면, 목적지 중의 하나는 다른 목적지보다 느릴 수 있습니다. 그러면 백프래셔가 생성될 수 있죠. 이러한 시나리오에서 -논리적인 큐의 형태에서- 파일시스템은 청크의 양을 어떻게 제한할까요?

Fluent Bit 1.6버전부터, 아웃풋 플러그인에 storage.total_limit_size 라는 속성이 있는데, 특정한 목적지로 가는 파일시스템의 청크의 숫자를 제한합니다. 목적지 중의 하나가 이 임계점에 도달하면, 큐에서 가장 오래된 청크가 삭제됩니다.

 

추가적으로 2가지의 옵션을 설명해보겠습니다.

Buffer_Max_Size
모니터되는 파일의 버퍼 사이즈의 한계를 정합니다. 매우 긴 로그 등으로 인해 버퍼가 늘어야 한다면, 이 값은 메모리 버퍼가 얼마나 늘 수 있는지 제한하는 데 사용됩니다. 이 한계를 넘는 파일을 읽을 때, 파일은 모니터되는 파일 리스트에서 사라집니다.

Static_Batch_Size
모니터되는 파일 중 Fluentbit가 시작할 때 이미 존재하는 파일의 용량 크기를 제한합니다.