데이터 수집기 Fluentd 이해하기 (v1.0)
Fluentd
빅데이터 시대가 오면서 데이터 수집기는 여러 방면에서 사용이 되는데,
대표적인 데이터 수집기는 ELK에서 사용되는 Logstash와 Fluentd로 나뉜다.
Fluentd는 쿠버네티스와 같은 CNCF 재단에 속해 있는 범용 로그 수집용 오픈소스 프로젝트로써 C와 Ruby로 개발되었다.
logstash에 비해 안정성이 높고 아키텍쳐가 단순한 편이며 메모리 또한 적게 쓰는 편이다.
Fluentd 주요 특징
Unified Logging with JSON
Fluentd의 데이터 구조는 JSON 형태로 되어 있다.
downstream data processing이 JSON으로 제공되어 더욱 쉽고, 이는 유연한 스키마를 유지하면서 accessible한 structure를 제공해준다.Pluggable Architecture
Fluentd의 커뮤니티에는 500개 이상의 유연한 플러그인들이 존재하고, 이를 통해 수많은 data sources와 data output을 연결해준다.
이 플러그인들을 이용하여 로그를 더 잘 활용할수 있게 해준다.Minimum Resources Required
Fluentd는 C 언어와 Ruby의 조합으로 작성되었으며 시스템 리소스가 거의 필요하지 않는다.
바닐라 인스턴스는 30 ~ 40MB의 메모리에서 실행되며 코어 당 13,000 개의 이벤트를 처리 할 수 있다.
Fluentd forwarder의 경량화 버전인 Fluent Bit을 이용시 ~450kb에서도 사용이 가능하다. (다만, Fluentd-Bit은 HA 구성이 되지 않는다)Built-in Reliability
Fluentd는 노드 간 데이터 손실을 방지하기 위해 메모리 및 파일 기반 버퍼링을 지원한다.
또한 Failover를 위한 HA를 설정할 수 있다.
공홈: https://www.fluentd.org/architecture
Fluentd Event Structure
입력 플러그인은 데이터 소스에서 Fluentd Event를 생성한다.
Fluentd의 이벤트 구조는 Tag, Time, Record 이렇게 3가지로 구성된다.
Tag
이벤트를 어디로 보낼지 결정하기 위한 데이터의 구분값이고 해당 값에 따라서 Parser, Filter, Output 등 라우팅 되는 기준이 됨Time
이벤트 데이터의 생성 시간Record
JSON 형태로 되어있는 이벤트 데이터의 내용
Fluentd Structure
Fluentd는 Input, Parser, Filter, Storage, Buffer, Service Discovery, Ouput, Formatter
이렇게 8개의 플러그인을 통하여 필요한 기능을 플러그인 방식으로 설정 파일에 추가하여 사용할 수 있다.
전반적인 데이터의 흐름은 Input -> Filter -> Buffer -> Output 으로 동작한다.
Plugin
Input
입력 플러그인은 Fluentd를 확장하여 외부 소스(로그 등)에서 이벤트 로그를 검색하고 가져온다.
입력 플러그인은 일반적으로 스레드 소켓과 청취 소켓을 만든다.
데이터 소스에서 주기적으로 데이터를 가져오도록 할 수 있다.
로그 손실 방지
Fluentd는 pos_file을 통하여 input source를 어디까지 읽었는지 체크한다.
그로인해 Fluentd가 재시작이 일어나도, pos_file을 통해 읽기 시작해야하는 source 지점을 찾을수 있다.
Output
output 플러그인은 다음 3가지의 buffering & flushing 모드를 지원한다.
Non-Buffered
데이터를 버퍼링하지 않고 바로 결과를 기록함Synchronous Buffered
단계별buffer chunks(이벤트의 집합)
와 청크의 큐 그리고 그것들의 동작을<buffer>
섹션별로 제어함.
위 Fluentd Structure 그림 참조.Asynchronous Buffered
stage
와queue
가 존재.
아웃풋 플러그인은 동기적으로 메소드에 쓰기 청크를 커밋하지 않고 나중에 커밋함.
Output 플러그인은 위 3가지 모드중에 한가지 모드만을 사용할 수 있으며,<buffer>
섹션을 입력하지 않으면 적절한 모드를 자동으로 선택함.
만약 사용자가 버퍼링을 지원하지 않는 플러그인에 <buffer>
섹션을 사용하면 구성 오류가 발생함.
Filtering
필터 플러그인을 이용해서 Fluentd 이벤트 스트림을 수정할수 있다.
명시된 필드값에 정규표현식과 매칭되는 값만 필터링한다.
Example)
- 하나 이상의 필드 값을 수집하여 이벤트 필터링.
- 새 필드를 추가하여 이벤트를 풍부하게 함.
- 개인 정보 보호 및 규정 준수를 위해 특정 필드를 삭제하거나 마스킹
Parser
input 플러그인(e.g in_tail
, in_syslog
, in_tcp
, in_udp
)의 <parser>
지시자의 경우 때론 사용자의 커스텀 데이터 포맷을 파싱하지 못한다. (정규식 파싱 등)
이러한 경우를 해결하기 위해 커스텀 파서 형식을 만들 수 있는 플러그인을 지원한다.
Formatter
Output 플러그인을 통해 저장소에 데이터를 쓸 때, Formatter를 이용하여 데이터의 포맷을 정의할 수 있다
Buffer
출력 플러그인에서 사용되고 버퍼는 File과 Memory 두가지를 사용할 수 있다.
버퍼는 chunk 집합을 담고 있으며, 각 chunk는 이벤트 묶음이 저장된 하나의 Blob 파일이다
Chunk Lifecycle
Input에서 들어온 데이터를 바로 Output으로 보내는게 아니라 청크에 이벤트를 채우고 이벤트가 가득 차면 목적지로 전송합니다.
내부적으로 버퍼 플러그인에는 청크를 저장할 두 개의 분리 된 위치가 있다.
- stage
청크가 이벤트로 채워지는 공간 - queue
청크가 전송 전에 대기하는 공간
새로 생성 된 모든 청크는 stage에서 시작하여 queue로 이동된 뒤 목적지로 전송된다.
Control Retry Behavior
청크는 여러가지 이유로 대상에 기록되지 않을 수 있다.
네트워크가 다운되거나 트래픽 볼륨이 대상 노드의 용량을 초과하는 경우가 그러한데, 이를 위해서 버퍼 플러그인에는 기본 재시도 매커니즘이 장착되어 있다.
Exponential Backoff Works
지수 백오프 동작방식은 Fluentd가 재시도 할 때마다 대기 시간을 기하 급수적으로 늘리는 방식이다.
만약, 초대 대기 간격을 1초로 설정하고 지수 요소를 2라고 설정하면 각 시도는 다음과 같이 발생한다.
1 2 4 8 16
x-x---x-------x---------------x-------------------------
│ │ │ │ └─ 4th retry (wait = 8s)
│ │ │ └───────────────── 3th retry (wait = 4s)
│ │ └───────────────────────── 2th retry (wait = 2s)
│ └───────────────────────────── 1th retry (wait = 1s)
└─────────────────────────────── Fail
Fluentd에서는 몇가지 옵션으로 지수 백오프 동작방식의 알고리즘을 좀더 디테일하게 조절할수 있다.
retry_randomize
대기 간격은 기본적으로 무작위로 설정되어 있어, 다음 대기 시간에서 0.875 ~ 1.125 사이의 값을 곱한 대기시간으로 적용한다. try_randomize 속성에 false를 주어 해당 속성을 끌수 있다.
retry_max_interval
대기 간격 시간의 maximum 값을 지정한다.
만약 위 예제에서 retry_max_interval 속성을 5로 설정하면, 4번째 시도의 대기 시간은 8초가 아닌 5초가 된다.
Handling Successive Failures
Fluentd는 연속적으로 실패한 chunk 전송 시도를 다음 규칙에 따라 중지한다.
retry_max_times
default 값은 none이며, 몇번까지 재시도를 허락할지 지정한다.
retry_timeout
defualt 값은 72h이고, 첫번째 재시도가 일어나고 경과된 최대 시간 값을 지정한다.
위 규칙에 따라 chunk 전송 시도가 최종적으로 실패할경우, 큐에 존재하는 모든 chunk는 폐기된다.
이를 원치 않는다면 retry_forever
속성을 통해 폐기하는 것을 막을수 있다.
Handling Unrecoverable Errors
모든 에러가 본질적으로 복구 가능한 것이 아니다.
예를 들어, 청크 파일의 내용이 손상된 것이라면 쓰기 작업을 재시도하는 것으로는 오히려 블라인드 재시도 상황을 학화시키는것이다.
Fluentd는 v1.2.0 부터 이러한 복구 불가능한 오류를 감지할 수 있다.
이러한 오류가 발생시, 청크를 중단하고 백업 디렉토리로 이동시키며, 해당 경로는 <system>
섹션 안의 root_dir
로 지정할수 있다.
${root_dir}/backup/worker${worker_id}/${plugin_id}/{chunk_id}.log
만약 청크를 백업할 필요가 없다면 <buffer>
섹션에서 disable_chunk_backup
를 활성화 시키면 된다. (v1.2.6부터 가능)
복구할수 없는 예외 목록
- Fluent::UnrecoverableError
출력 플러그인은 해당 예외를 사용해서, 복구 불가능한 오류에 대한 추가 재시도를 억제 할수 있음. - TypeError
이벤트의 대상 필드에 예기치 않은 유형이 있을때. - ArgumentError
플러그인 라이브러리를 잘못 사용했을때. - NoMethodError
Event와 configuration이 일치 하지 않을때.
로그 손실 방지
source에서 가져온 이벤트가 chunk로 queue에 축적이 되었을때 Fluentd가 재시작이 일어나면, 해당 queue에 있는 이벤트들이 손실될수 있다.
이를 막기 위해 Buffer Plugin은 buffer_path에 청크들을 파일로 저장한다.
복구 시에는 queue에 축적되어있는 chunk가 하나씩 pop되어 전송된다.
Storage
때론, input/filter/ouput 플러그인은 내부 상태를 key/value 상태의 메모리 혹은 스토리지에 저장해야할 필요성이 있는데 그러한때에 사용된다.
Service Discovery
몇 플러그인은 <service_discovery>
를 지원한다.(e.g. out_forward)
때론, output 플러그인이 사용자가 원하는 서비스 디스커버리를 지원하지 못한다.
이럴때 커스텀 output 서비스 디스커버리를 확장하고 재사용할수 있도록 서비스 디스커버리 플러그인을 지원한다.