installation EFK

개요

EFK 조합을 구성하여 작동 방식을 알아보자.

  • E : Elastic search
  • F : Fluentd
  • K : kibana

Fluentd?

Fluentd란 오픈소스 데이터 수집기이다.

Fluentd는 유연성을 위해 Ruby로 작성되었으며, 성능에 민감한 부분은 C로 작성되었다.

Fluentd 프로젝트는 Treasure Data 에서 후원한다. 참 감사하다.

라이센스는 아파치 라이선스 2.0 이다.

Fluentd를 이용한 로그 수집 아키텍쳐와 내부 구조는 아래 링크를 참조하여 이해했다.

Before Installation

시험환경

  • OS : CentOS 7.5
  • Hostname : jgs-fluentd

Set Up NTP

NTP(ntpd, chrony) 를 설정하여 정확한 현재 time stamp를 설정해야 하는 것을 권장한다.

ntp를 설치한다.:

[root@jgs-fluentd ~]# yum install ntp

File Descriptor

File Descriptor를 최대 수로 늘려야 한다.

아래 명령으로 현재 file descriptor를 확인한다.:

[root@jgs-fluentd ~]# ulimit -n
1024
  • 1024는 충분하지 않다.

아래 파일에 다음 줄을 추가한다.:

[root@jgs-fluentd ~]# vi /etc/security/limits.conf
...
root soft nofile 65536
root hard nofile 65536
* soft nofile 65536
* hard nofile 65536
  • 적용하려면 서버를 재시작 해야한다.

Optimize Network Kernel Parameters

많은 Fluentd 인스턴스로 구성된 대규모 환경의 경우 아래 매개변수를 추가한다.:

[root@jgs-fluentd ~]# cat /etc/sysctl.conf
...
net.core.somaxconn = 1024
net.core.netdev_max_backlog = 5000
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_wmem = 4096 12582912 16777216
net.ipv4.tcp_rmem = 4096 12582912 16777216
net.ipv4.tcp_max_syn_backlog = 8096
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 10240 65535

Install by RPM Package

Fluentd 배포 rpm 패키지는 td-agent 이다.

이 패키지는 Treasure Data 에서 관리한다.

td-agent를 설치하자.:

[root@jgs-fluentd ~]# curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent3.sh | sh
...
========================================================================================================================================================================
 Package                                           Arch                          Version                                      Repository                           Size
========================================================================================================================================================================
Installing:
 td-agent                                          x86_64                        3.4.1-0.el7                                  treasuredata                         48 M
Installing for dependencies:
 at                                                x86_64                        3.1.13-24.el7                                base                                 51 k
 avahi-libs                                        x86_64                        0.6.31-19.el7                                base                                 61 k
 bc                                                x86_64                        1.06.95-13.el7                               base                                115 k
 cups-client                                       x86_64                        1:1.6.3-35.el7                               base                                151 k
 cups-libs                                         x86_64                        1:1.6.3-35.el7                               base                                357 k
 ed                                                x86_64                        1.9-4.el7                                    base                                 72 k
 m4                                                x86_64                        1.4.16-10.el7                                base                                256 k
 mailx                                             x86_64                        12.5-19.el7                                  base                                245 k
 patch                                             x86_64                        2.7.1-10.el7_5                               base                                110 k
 psmisc                                            x86_64                        22.20-15.el7                                 base                                141 k
 redhat-lsb-core                                   x86_64                        4.1-27.el7.centos.1                          base                                 38 k
 redhat-lsb-submod-security                        x86_64                        4.1-27.el7.centos.1                          base                                 15 k
 spax                                              x86_64                        1.5.2-13.el7                                 base                                260 k
 time                                              x86_64                        1.7-45.el7                                   base                                 30 k

Transaction Summary
========================================================================================================================================================================
Install  1 Package (+14 Dependent packages)
  • 위 쉘 스크립트는 새로운 rpm 저장소를 등록하고 rpm 패키지를 설치한다.

아래와 같이 repo가 추가 된다.:

[root@jgs-fluentd ~]# cat /etc/yum.repos.d/td.repo
[treasuredata]
name=TreasureData
baseurl=http://packages.treasuredata.com/3/redhat/$releasever/$basearch
gpgcheck=1
gpgkey=https://packages.treasuredata.com/GPG-KEY-td-agent

fluentd를 시작하고 살펴보자.:

[root@jgs-fluentd ~]# systemctl start td-agent

[root@jgs-fluentd ~]# ss -nltp |grep fluentd
LISTEN     0      128          *:8888                     *:*                   users:(("ruby",pid=31738,fd=15),("fluentd",pid=31733,fd=15))

[root@jgs-fluentd ~]# ls -l /opt/td-agent/
total 40
drwxrwxr-x. 2 root root 4096 May 18 21:33 bin
drwxrwxr-x. 7 root root 4096 May 21 11:13 embedded
drwxrwxr-x. 5 root root 4096 May 21 11:13 etc
-rw-rw-r--. 1 root root 3783 May 18 21:33 LICENSE
drwxrwxr-x. 2 root root 4096 May 21 11:12 LICENSES
drwxrwxr-x. 4 root root 4096 May 21 11:13 usr
-rw-rw-r--. 1 root root 8690 May 18 21:33 version-manifest.json
-rw-rw-r--. 1 root root 3761 May 18 21:33 version-manifest.txt

[root@jgs-fluentd ~]# ls -l /var/log/td-agent/
total 8
drwxr-xr-x. 3 td-agent td-agent 4096 May 21 11:21 buffer
-rw-r--r--. 1 td-agent td-agent 3111 May 21 11:21 td-agent.log

프로세스.:

[root@jgs-fluentd ~]# ps auxww |grep td-agent
td-agent 31733  0.0  0.4 241296 37716 ?        Sl   11:21   0:00 /opt/td-agent/embedded/bin/ruby /opt/td-agent/embedded/bin/fluentd --log /var/log/td-agent/td-agent.log --daemon /var/run/td-agent/td-agent.pid
td-agent 31738  0.0  0.6 257592 55040 ?        Sl   11:21   0:01 /opt/td-agent/embedded/bin/ruby -Eascii-8bit:ascii-8bit /opt/td-agent/embedded/bin/fluentd --log /var/log/td-agent/td-agent.log --daemon /var/run/td-agent/td-agent.pid --under-supervisor
  • td-agent가 시작되면 마스터와 슬레이브 두 프로세스가 시작된다.
  • 마스터 프로세스는 슬레이브 프로세스의 수명주기를 관리하고,
  • 슬레이브가 실제 로그를 수집한다.

input 설정에 따라 아래 포트가 열린다.

  • in_tail : notthing
  • in_forward : tcp/24224, udp/24224
  • in_unix : /var/run/td-agent/td-agent.sock

에이전트 설정파일은 아래 위치한다.:

/etc/td-agent/td-agent.conf
  • 기본 설정은 HTTP에서 로그를 가져와서 stdout(/var/log/td-agent.log) 으로 라우트 하도록 구성되어 있다.

curl 명령을 사용하여 샘플 로그 레코드를 게시해보자.:

[root@jgs-fluentd ~]# curl -X POST -d 'json={"json":"message"}' http://localhost:8888/debug.test

stdout으로 설정된 /var/log/td-agent.log를 확인해보자.:

[root@jgs-fluentd ~]# tail -n1 /var/log/td-agent/td-agent.log
2019-05-21 11:29:33.743030358 +0900 debug.test: {"json":"message"}

이제 Fluentd를 사용하여 실제 로그를 수집할 준비가 되었다.

플러그인

Fluentd는 아래 7가지 플러그인 유형이 있다.

  • Input
  • Parser
  • Filter
  • Output
  • Formatter
  • Storage
  • Buffer : 이것은 중요한 부분이라 아래 자세히 정리한다.

Buffer

Buffer 플러그인은 output 플러그인 안에서 사용된다.

예를들어, out_s3(출력 플러그인) 은 기본적으로 buf_file을 사용하여 실제 S3로 로그 데이터를 전송하기 전에 버퍼 파일에 임시로 저장한다.

  • 버퍼는 file 혹은 메모리를 사용할 수 있다.

buffer 작동원리

버퍼는 chunk의 집합이다. Chunk는 단일 blob으로 연결되는 이벤트의 모음이다.

chunk를 화물상자라고 생각하면 된다. buffer plugin은 chunk를 경량 컨테이너로 사용하고, input source에서 들어오는 이벤트로 채우기 시작한다. chunk가 가득 차면 도착지로 배송한다.

내부적으로는 buffer plugin은 chunk를 저장할 두개의 분리된 장소를 사용한다.

  • stage : chunk가 이벤트로 채워지는 장소
  • queue : chunk가 배송전에 대기하는 대기열

chunk flow:

input event -> buffer(chunk) -> Queue(chunk) -> Output

Config File Syntax

설정파일은 Fluentd가 가질 input 또는 listeners 를 정의할 수 있고, Fluentd는 이벤트 데이터를 특정 output으로 라우팅 하기 위해 규칙을 설정한다.

설정 파일은 아래 문법으로 구성된다.

  • source : 이것은 input source를 나타낸다. (로그를 어디서 수집할건지)
  • match : 이것은 output 의 대상을 나타낸다. (로그를 어디에 쓸건지)
  • filter : 이것은 이벤트 처리 파이프라인을 결정한다. (로그를 output에 보내기 전 사용자 정의 필터를 할 수 있음)
  • system : 이것은 시스템 전체 구성을 설정한다.
  • label : 이것은 output, filter를 그룹화한다.
  • @include : 이것은 다른 파일을 include한다.

input과 output

아래는 in_http 와 out_stdout 플러그인을 예제로 이벤트 사이클을 살펴본다.:

<source>
  @type http
  port 8888
  bind 0.0.0.0
</source>
  • <source> 섹션은 데이터를 수집할 input 플러그인을 선택하여 구성한다.
  • <source> 섹션에서 @type 에 플러그인을 지정하는데, http 플러그인은 Fluentd가 HTTP 엔드포인트로 전환하여 HTTP로 들어오는 메시지를 수집한다.
  • 이제 http 포트 8888에서 들어오는 메시지를 Fluentd가 수집할 것이다.

그럼 이제 수집한 데이터를 어디로 보낼지? output을 지정하자.:

<match test.cycle>
  @type stdout
</match>
  • <match>는 데이터를 매칭하여 어디로 데이터를 보낼건지를 정하는데, 이 매칭할 조건이 tag기준이다.
  • <match test.cycle>에서 test.cycle이 tag이다.
  • @type 으로 stdout을 정의했고, 이는 그냥 표준출력인 터미널로 데이터를 보낸다(output).

그럼 이제 http 포트 8888로 데이터를 보내고 Fluentd가 이 데이터를 수집해서 output인 터미널로 출력하는지 확인해보자.:

[root@jgs-fluentd td-agent]# curl -X POST -d 'json={"action":"login","user":2}' http://localhost:8888/test.cycle

[root@jgs-fluentd td-agent]# tail -f /var/log/td-agent/td-agent.log
...
2019-05-23 17:42:04.828064560 +0900 test.cycle: {"action":"login","user":2}
  • td-agent가 백그라운드 데몬으로 실행했기 때문에 stdout이 없고, stdout으로 출력되는 로그를 확인했다.
  • 매칭할 태그를 선정하는 기준이 http://localhost:8888/<Tag> 와 같은 형태이다.
  • http 포트 8888로 들어온 input 데이터를 output으로 잘 출력했다.

데이터는 크게 3가지 파트로 구성된다. * Time : 로그 데이터의 생성시간 * Record : 로그 데이터의 내용으로 JSON 형태로 정의된다. * Tag : 데이터의 분류이다. 각 로그 레코드는 tag를 통해서 로그의 종류가 정해진다. 이 tag에 따라서 로그에 대한 필터링, 라우팅과 같은 플러그인이 적용된다.

위 output으로 출력된 데이터를 기준으로 설명하자면 아래와 같다.

  • Time : 828064560 +0900
  • Tag : test.cycle:
  • Record : {“action”:”login”,”user”:2}

Filter

필터는 input에서 output으로 이동 하기 전에 input에서 받은 이벤트를 output으로 통과 시키거나 거부하는 규칙을 설정할 수 있다.

필터 규칙을 설정해보자.:

<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>
  • <fileter test.cycle> : 필터를 정의하고 test.cycle이라는 태그가 매칭되면 이 태그의 데이터를 검사한다.
  • @type grep : 특정 문자열을 grep하기 위해 grep 플러그인을 사용했다.
  • <exclude> 필터에 의해 exclude될 데이터에 대한 규칙을 정한다.
  • key action : 로그 데이터의 action 키에 ^logout$ value가 포함되어 있으면 이 로그 데이터는 output에 도달하지 못하고 버려진다.
  • patthern : regex로 필터할 데이터 규칙을 정한다.

아래 두 curl 명령으로 결과의 차이를 확인해보자.:

[root@jgs-fluentd td-agent]# curl -X POST -d 'json={"action":"login","user":2}' http://localhost:8888/test.cycle
[root@jgs-fluentd td-agent]# curl -X POST -d 'json={"action":"logout","user":2}' http://localhost:8888/test.cycle

[root@jgs-fluentd td-agent]# tail -f /var/log/td-agent/td-agent.log
...
2019-05-23 19:02:37.625941479 +0900 test.cycle: {"action":"login","user":2}
  • 두 http 요청에 의해 하나만 output으로 출력이 되었다.
  • action 키의 logout value는 필터에 의해 output으로 도달하지 못하고 필터가 된것이다.

필터의 설정은 위에서 아래 순서대로 처리되는 단계별 절차를 따른다. 그래서 많은 필터가 사용될 경우 설정파일이 복잡해진다.

그래서 이에 대한 대응으로 여러 필터를 통합할 수 있는데, 이 방법이 label이다.

labels

label 은 구성 파일을 복잡성을 해결하고 위쪽에서 아래쪽 순서를 따르지 않는다.

라벨에 키 값에 링크된 새 라우팅 섹션에서 이를 처리한다.

아래 예시를 보자.:

<source>
  @type http
  port 8888
  bind 0.0.0.0
  @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>
  • input 소스에 STAGING 라벨을 부여했고,
  • <label @STAGING> 에서 직접 해당 input 데이터에 대한 필터 및 ountput 처리함.
  • <source> 섹션 바로 아래에 있는 필터는 무시되고, <label> 섹션 안에서 필터처리를 한다.

결과를 보자.:

[root@jgs-fluentd td-agent]# curl -X POST -d 'json={"action":"logout","user":2}' http://localhost:8888/test.cycle
[root@jgs-fluentd td-agent]# curl -X POST -d 'json={"action":"login","user":2}' http://localhost:8888/test.cycle

[root@jgs-fluentd td-agent]# tail -f /var/log/td-agent/td-agent.log
...
2019-05-23 22:50:23.228286212 +0900 test.cycle: {"action":"login","user":2}
  • action : logout 과 login에 대한 HTTP 요청을 했다.
  • 실제로 수집하여 output까지 전달된 데이터는 login이다
  • <source> 섹션 바로 아래에 있는 필터인 login이 필터되지 않았고, <label> 섹션안에 있는 필터로 logout만 필터되었다.

이제 이 로그를 Elastic Search와 연결하여 로그를 Elastic Search 로 저장하자.

이렇게 하기 위해서는 Elasticsearch 플러그인이 필요하다.:

[root@jgs-fluentd ~]# td-agent-gem install fluent-plugin-elasticsearch

먼저 elasticsearch 서버를 구성하자.

Fluentd 에서 ES로 로그 전송

Fluentd에서 /var/log/messages 로그를 수집하여 ES에 저장하는 설정을 하자.

Flentd 설정.:

[root@jgs-fluentd td-agent]# cat td-agent.conf
<source>
  @type tail
  path /var/log/messages
  pos_file /var/log/td-agent/messages.log.pos
  tag messages.log
  format json
</source>

<match messages.log>
  @type elasticsearch
  host 172.20.1.11
  port 9200
  logstash_format true
</match>
  • in_tail 플러그인으로 /var/log/messages 로그를 수집한다.
  • 수집한 데이터는 in_elasticsearch 플러그인으로 172.20.1.11(ES server)로 보낸다.

Kibana

Kibana는 강력하고 화려한 그래픽을 통해 데이터를 작업할 수 있도록 해주는 오픈소스 데이터 시각화 플랫폼이다.

여기선 ES에 저장된 로그 데이터를 시각화하기 위해 Kibana를 사용한다.

Kibana를 설치한다.:

[root@jgs-es ~]# wget https://artifacts.elastic.co/downloads/kibana/kibana-5.4.0-x86_64.rpm
[root@jgs-es ~]# rpm -ivh kibana-5.4.0-x86_64.rpm
  • jgs-es 서버에 Kibana를 설치하였다.

프로세스 및 포트.:

[root@jgs-es ~]# ps -ef |grep kibana
kibana    4385     1  9 12:13 ?        00:00:03 /usr/share/kibana/bin/../node/bin/node --no-warnings /usr/share/kibana/bin/../src/cli -c /etc/kibana/kibana.yml

[root@jgs-es ~]#  ss -nltp
LISTEN     0      128                    127.0.0.1:5601                                       *:*                   users:(("node",pid=4385,fd=11))

kibana 설정.:

[root@jgs-es ~]# vi /etc/kibana/kibana.yml
...
server.host: "0.0.0.0"