본문 바로가기
Framework and Tool/fluentd

Fluentd - Overview

by ocwokocw 2022. 7. 4.

- 출처: https://docs.fluentd.org/

- 개요

Fluentd는 일종의 logging 계층을 통합하기 위한 data collector 라고 할 수 있다. 라이센스는 Apache License v2.0 이며, Treasure Data(https://www.treasuredata.com/) 에 의해 제작되었다.
 
Fluentd는 다양한 Input Source들(Apache access log, Front 및 Back end의 App log, System log) 들을 filter, buffer, routing 하는 과정을 거쳐 다양한 Output Source들(MySQL, MongoDB, Hadoop, Amazon S3)로 보낼 수 있다.
 
Fluentd는 일반적으로 많이 사용하는 JSON 형식으로 log를 다룬다.

- Fluentd event의 생명 주기


- 기본 설정

Fluentd가 어떻게 event를 처리하는지 알아보자. event를 처리하는 주요 단계는 Setup, Inputs, Filters, Matches, Labels가 있다.
 
configuration file은 가장 기본적인 부분이라고 할 수 있는데, 어떤 fluentd가 Inputs 이나 listener를 갖는지 그리고 Event 데이터를 특정 Output으로 보내는 matching 규칙을 설정한다.
 
간단하게 in_http와 out_stdout 플러그인을 사용하여 event 주기를 알아보자. 아래는 http input을 설정하는 간단한 configuration file 이며, HTTP 요청을 listening하게 된다.
 
<source>
  @type http
  port 8888
  bind 0.0.0.0
</source>

 

직관적으로 해석해도 크게 어렴운점은 없을것이다. HTTP server는 TCP 8888 port를 listening 하게 된다.
 
이제 들어오는 요청을 표준 출력으로 출력하기 위한 Matching rule을 정의해보자.
 
<match test.cycle>
  @type stdout
</match>
 
Match는 들어오는 event가 test.cycle 태그를 갖고 있으면 stdout 형식의 output 플러그인을 사용한다. 여기까지 정의하면 Input 형식과 Match, Output을 갖게되는것이다.
 
아래와 같이 curl을 날리면 
 
$ curl -i -X POST -d 'json={"action":"login","user":2}' http://localhost:8888/test.cycle
HTTP/1.1 200 OK
Content-type: text/plain
Connection: Keep-Alive
Content-length: 0
 
fluentd는 아래와 같이 출력할것이다. 
 
$ fluentd -c in_http.conf
2019-12-16 18:58:15 +0900 [info]: parsing config file is succeeded path="in_http.conf"
2019-12-16 18:58:15 +0900 [info]: gem 'fluentd' version '1.8.0'
2019-12-16 18:58:15 +0900 [info]: using configuration file: <ROOT>
  <source>
    @type http
    port 8888
    bind "0.0.0.0"
  </source>
  <match test.cycle>
    @type stdout
  </match>
</ROOT>
2019-12-16 18:58:15 +0900 [info]: starting fluentd-1.8.0 pid=44323 ruby="2.4.6"
2019-12-16 18:58:15 +0900 [info]: spawn command to main:  cmdline=["/path/to/ruby", "-Eascii-8bit:ascii-8bit", "/path/to/fluentd", "-c", "in_http.conf", "--under-supervisor"]
2019-12-16 18:58:16 +0900 [info]: adding match pattern="test.cycle" type="stdout"
2019-12-16 18:58:16 +0900 [info]: adding source type="http"
2019-12-16 18:58:16 +0900 [info]: #0 starting fluentd worker pid=44336 ppid=44323 worker=0
2019-12-16 18:58:16 +0900 [info]: #0 fluentd worker is now running worker=0
2019-12-16 18:58:27.888557000 +0900 test.cycle: {"action":"login","user":2}

- Event 구조

fluentd event는 3 부분으로 구성되어 있다.
  • tag: event가 어디로 부터 유입되었는지를 알게해주며 메시지 라우팅에 사용된다.
  • time: event가 언제 일어났는지 알려준다.
  • record: JSON object 형식으로 발생한 log
 
input 플러그인은 데이터 소스로 부터 Fluentd event를 발생시키는 역할을 한다. 예를 들어 in_tail은 text line에서 event를 발생시킨다. 만약 아래와 같은 Apache log 가 있다고 할 때,
 
192.168.0.1 - - [28/Feb/2013:12:00:00 +0900] "GET / HTTP/1.1" 200 777
 
아래와 같은 fluentd event를 갖게 된다.
 
tag: apache.access         # set by configuration
time: 1362020400.000000000 # 28/Feb/2013:12:00:00 +0900
record: {"user":"-","method":"GET","code":200,"size":777,"host":"192.168.0.1","path":"/"}

- Event 처리

Setup이 정의되면 Router Engine은 이를 다양한 input data에 적용하기 위해 미리 정의된 규칙들을 포함하게 된다. 내부적으로 Event는 생명주기를 변경할 수 있는 일종의 작업 chain을 통과하게 된다.
 
이제 어떻게 Event 주기가 변경될 수 있는지 설명하기 위해 앞의 예제의 Setup에서 몇 가지 단계를 추가해보자. 이를 위해 Filter라는 개념을 사용한다.
 
Filter는 이름에서도 유추할 수 있듯이 event를 통과시킬지 아니면 막을지에 대한 규칙이다. 아래 설정은 filter 정의를 추가한것이다.
 
<source>
  @type http
  port 8888
  bind 0.0.0.0
</source>

<filter test.cycle>
  @type grep
  <exclude>
    key action
    pattern ^logout$
  </exclude>
</filter>

<match test.cycle>
  @type stdout
</match>
 
 
위의 예제에서 흐름은 <source> -> <filter> -> <match> 순으로 이어진다고 볼 수 있다. Filter 정의는 제어권이 match 로 가기전에 통과해야하는 필수적인 단계라고 볼 수 있다. Filter는 기본적으로 type과 규칙에 의해 Event를 수락할것인지 막을것인지를 결정한다. 위의 예제에서는 logout action에 대한 log는 버리고 login action만 신경쓰고 있다. 이를 위해 filter에서 grep을 수행하여 action key가 logout 문자열을 가진 메시지들을 모두 제거한다.
 
터미널에서 서로 다른 action값을 가진 curl 명령을 실행해보자.
 
$ curl -i -X POST -d 'json={"action":"login","user":2}' http://localhost:8888/test.cycle
HTTP/1.1 200 OK
Content-type: text/plain
Connection: Keep-Alive
Content-length: 0

$ curl -i -X POST -d 'json={"action":"logout","user":2}' http://localhost:8888/test.cycle
HTTP/1.1 200 OK
Content-type: text/plain
Connection: Keep-Alive
Content-length: 0
 
 
fluentd log에는 logout event는 버려지고 login 메시지만 보일것이다.
 
$ fluentd -c in_http.conf
2019-12-16 19:07:39 +0900 [info]: parsing config file is succeeded path="in_http.conf"
2019-12-16 19:07:39 +0900 [info]: gem 'fluentd' version '1.8.0'
2019-12-16 19:07:39 +0900 [info]: using configuration file: <ROOT>
  <source>
    @type http
    port 8888
    bind "0.0.0.0"
  </source>
  <filter test.cycle>
    @type grep
    <exclude>
      key "action"
      pattern ^logout$
    </exclude>
  </filter>
  <match test.cycle>
    @type stdout
  </match>
</ROOT>
2019-12-16 19:07:39 +0900 [info]: starting fluentd-1.8.0 pid=44435 ruby="2.4.6"
2019-12-16 19:07:39 +0900 [info]: spawn command to main:  cmdline=["/path/to/ruby", "-Eascii-8bit:ascii-8bit", "/path/to/fluentd", "-c", "in_http.conf", "--under-supervisor"]
2019-12-16 19:07:40 +0900 [info]: adding filter pattern="test.cycle" type="grep"
2019-12-16 19:07:40 +0900 [info]: adding match pattern="test.cycle" type="stdout"
2019-12-16 19:07:40 +0900 [info]: adding source type="http"
2019-12-16 19:07:40 +0900 [info]: #0 starting fluentd worker pid=44448 ppid=44435 worker=0
2019-12-16 19:07:40 +0900 [info]: #0 fluentd worker is now running worker = 0
2019-12-16 19:08:06.934660000 +0900 test.cycle: {"action":"login","user":2}
 
보다시피 event는 위에서 부터 아래 방향 순서대로 단계별로 처리가됨을 알 수 있다. 새로운 엔진은 필요하다면 많은 filter를 통합할 수 있다. 또한 환경 설정 파일이 비대해지고 복잡해질 경우 Label 이라고 하는 새로운 기능을 이용하면 된다.
 
Label은 환경 설정 파일이 복잡해질 경우 위에서 아래 순으로 따라가지 않고 참조기반으로 동작할 수 있도록 Routing 섹션을 정의하는 기능이다. 앞선 예제의 setup을 아래와 같이 수정할 수 있다.
 
<source>
  @type http
  bind 0.0.0.0
  port 8888
  @label @STAGING
</source>

<filter test.cycle>
  @type grep
  <exclude>
    key action
    pattern ^login$
  </exclude>
</filter>

<label @STAGING>
  <filter test.cycle>
    @type grep
    <exclude>
      key action
      pattern ^logout$
    </exclude>
  </filter>

  <match test.cycle>
    @type stdout
  </match>
</label>
 
위 configuration의 <source>는 @label parameter를 포함하고 있는데 이는 남은 단계들 중에 @STAGING label 섹션에서 동작한다는것을 의미한다. 모든 Routing 엔진이 Source에서 발생한 모든 event 들을  @STAGING에서 계속 처리하게 된다. 그래서 이전의  filter(test.cycle의 login action 메시지를 제외하는 filter)를 건너뛰게 된다.
 
마지막으로 buffer에 대해 알아보자. 위의 예제에서는 버퍼가 없는 stdout을 사용했다. 하지만 production 환경에서는 보통 buffered 모드의 forward, mongodb, s3등과 같은 출력을 사용한다. buffered mode를 사용하는 output 플러그인 우선 buffer로 수신된 이벤트를 저장하고난 뒤 flush 조건을 만족하면 buffer의 내용을 목적지로 전송하게 된다. 그래서 buffered output을 사용할 때에는 버퍼가 없는 stdout과 달리 수신한 event를 즉각적으로 볼 수 없다.
 
buffer는 신뢰성과 처리량의 관점에서 매우 중요하므로 잘 알아두어야 한다.
 
 

댓글