Site icon GRIP.News

Fluentd 의 활용. ElasticSearch, Kibana 을 사용한 Nginx Log 수집

앞선 글에서 LogStash 와 Fluentd 의 차이를 간단히 알아봤다. 이번엔 Fluentd 를 활용해 로그를 취합하고, 통계를 볼 수 있는 구조를 만들어보자. ElasticSearch, Fluentd, Kibana 조합으로 Nginx 의 Access Log 를 시각화 하는것이 목적이다.

Fluentd 와 LogStash 비교

 

로그 분석에 ElasticSearch를 사용하는 이유에 대한 질문이 많았다. 물론, 로그 데이터를 MySQL 이나 MongoDB에 입력하는것도 방법이나, 정형화된 형식을 넣어야 하고, 값이 바뀔 때 마다 SQL 구문을 수정해야 한다. 더 중요한 것은 이들은 시각화 도구를 제공하지 않고 통신을 위한 API 설계가 필요하다. 즉, 손이 많이 간다는 이야기다.

ElasticSearch의 분석 능력이 뛰어나고, 실시간 검색 능력도한 훌륭하다. 떼이터를 REST 형태로 쉽게 처리할 수 있고 요청할 수 있다. 함께 사용할 수 있는 Kibana 는 훌륭한 시각화 도구 이다.

 

Access Log 가 이런 형태로 저장된다면.

1.211.40.28 - - [11/Jun/2019:08:56:30 +0900] "GET /api/v4/users/me/teams/9kztki3nz7foi81je6cy9t1nqr/channels HTTP/1.1" 200 926 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) MTWORKS/4.1.4 Chrome/66.0.3359.181 Electron/3.0.13 Safari/537.36"
1.211.40.28 - - [11/Jun/2019:08:56:30 +0900] "GET /api/v4/channels/wqjqexrdibgapmr69t5381wsdc/posts?page=0&per_page=60 HTTP/1.1" 200 430 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) MTWORKS/4.1.4 Chrome/66.0.3359.181 Electron/3.0.13 Safari/537.36"
1.211.40.28 - - [11/Jun/2019:08:56:35 +0900] "GET /api/v4/users/me/teams/unread HTTP/1.1" 200 128 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) MTWORKS/4.1.4 Chrome/66.0.3359.181 Electron/3.0.13 Safari/537.36"
10.10.101.150 - - [11/Jun/2019:08:57:25 +0900] "GET / HTTP/1.1" 200 1262 "-" "ELB-HealthChecker/2.0"

 

ElasticSearch 와 Kibana 는 팬시한 화면과 생산성을 높혀줄 것이다.

 

아키텍쳐


인터넷에서 유입된 트래픽은 복수의 Reverse Proxy. 즉 Nginx를 통해서 Application Server 로 전달된다. 분산된 Nginx 의 Access Log 를 한곳에 모아 ElasticSearch 로 전달하고, Kibana 가 이쁘게 그려줄 것이다.

이 포스트에서는 N 개의 Nginx 가 아닌 1개의 Nginx 를 대상으로, S3 로의 백업은 생략했다. 즉, 점선 안 내용을 설명하고자한다. 점선 안 내용을 다시 드로잉하면 다음과 같이 표현할 수 있다.

 

포스트는 ‘수집(집계)’를 처리하는 왼쪽(STEP #1)과, ‘분석, 처리’하는 오른쪽(STEP #2)으로 나눠 설명하고 있다.

 

STEP #1

Nginx Access Log


분석 대상은 Nginx 의 Access Log이며 동적으로 변하는 Log 를 대상으로 한다. Ubuntu 는 패키지 관리자로 Nginx 를 설치하면 필요한 부가 설정들이 기본적으로 세팅 되어 있고, 수정하지 않는다면 다음과 같은 규칙을 갖고 있다.

  1. 로그 저장소는 /var/log/nginx 이다.
  2. 현재 저장되고 있는 Log 는 access.log 다.
  3. 하루 지난 Log 는 access.log.1 이다.
ubuntu@ip-10-10-101-43:/var/log/nginx# ls -al
total 3228
drwxr-xr-x  2 root     adm       4096 Jun 11 06:25 .
drwxrwxr-x 13 root     syslog    4096 Jun 11 06:25 ..
-rw-r-----  1 mtapp	 mtapp    1214668 Jun 11 20:57 access.log
-rw-r-----  1 mtapp	 mtapp    1284593 Jun 11 06:24 access.log.1
-rw-r-----  1 mtapp	 mtapp      19863 Jun 10 06:24 access.log.2.gz
-rw-r-----  1 mtapp	 mtapp          0 May 23 06:25 error.log
-rw-r-----  1 mtapp	 mtapp     249173 May 22 18:29 error.log.1
-rw-r-----  1 mtapp	 mtapp      13798 May 21 20:21 error.log.2.gz

중요한 것은 Access Log 의 Format 이다. 반드시 ‘분석 가능한’ 형태로 저장되어야 한다. Nginx 의 HTTP Log 에 대한 내용은 다음 링크를 참고하자.

기본 Log Format. 분석 해야 하는 값이 함께 들어가 있다. $request 부분을 보면 Method 와 URI 그리고 Query String 이 포함되기 때문에 분석하기 쉽지 않다.

1.211.40.28 - - [11/Jun/2019:08:56:30 +0900] "GET /api/v4/users/me/teams/9kztki3nz7foi81je6cy9t1nqr/channels HTTP/1.1" 200 926 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) MTWORKS/4.1.4 Chrome/66.0.3359.181 Electron/3.0.13 Safari/537.36"

변경된 Log Format. 본인이 수집하려는 기준으로 맞추면 되며, 나는 JSON 형태로 만들었다.

log_format main	'{"@time":"$time_iso8601",'
		'"IP":"$remote_addr",'
		'"Status":$status,'
		'"Method":"$request_method",'
		'"RequestTime":$request_time,'
		'"FileName":"$request_filename",'
		'"QueryString":"$query_string",'
		'"SentSize":$bytes_sent,'
		'"UA":"$http_user_agent",'
		'"Referer":"$http_referer"}';


# 결과
{"@time":"2019-06-13T20:20:17+09:00","IP":"1.5.6.252","Status":200,"Method":"POST","RequestTime":0.000,"FileName":"/usr/share/nginx/html/api/v4/users/status/ids","QueryString":"-","SentSize":827,"UA":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36","Referer":"-"}

# 
{
	"@time": "2019-06-13T20:20:17+09:00",
	"IP": "1.5.6.252",
	"Status": 200,
	"Method": "POST",
	"RequestTime": 0.000,
	"FileName": "/usr/share/nginx/html/api/v4/users/status/ids",
	"QueryString": "-",
	"SentSize": 827,
	"UA": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36",
	"Referer": "-"
}

 

Fluentd 설치


Ubuntu 의 기본 패키지 저장소에는 td-agent 가 존재하지 않는다. fluentd.org 제공하는 OS 별 스크립트를 참고하자.

ubuntu@ip-10-10-101-43:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.2 LTS
Release:        18.04
Codename:       bionic
ubuntu@ip-10-10-101-43:~$ sudo curl -L https://toolbelt.treasuredata.com/sh/install-ubuntu-bionic-td-agent3.sh | sh
.
.
ubuntu@ip-10-10-101-43:~$ which td-agent
/usr/sbin/td-agent
ubuntu@ip-10-10-101-43:~$ service td-agent
Usage: /etc/init.d/td-agent {start|stop|reload|restart|force-reload|status|configtest}

디스크립터 수를 증가시키자. Ubuntu 는 기본 8096 이다. 늘리자. 권한이 불충분 할 경우 Fluentd 를 실행할 때 LimitNOFILE=65536 옵션으로 사용해도 무관하다.

ubuntu@ip-10-10-101-43:/home/ubuntu# ulimit -n
8096
ubuntu@ip-10-10-101-43:/home/ubuntu# sudo vi /etc/security/limits.conf
.
.
root soft nofile 65536
root hard nofile 65536
* soft nofile 65536
* hard nofile 65536

 

Fluentd 플러그인 설치


Fluentd 에서 사용할 플러그인을 설치한다. 플러그인은 td-agent 와 함께 설치된 td-agent-gem 을 사용한다.

  1. td-agent-gem 을 사용하려면 2.5.0 이상의 Ruby 가 먼저 설치 되어 있어야 한다.
  2. agent 에 종속된 내용들을 갱신(update)하자.
  3. fluent-plugin-config-expander 는 설정파일 확장 적용을 위한 플러그인이다.
ubuntu@ip-10-10-101-43:/home/ubuntu# sudo td-agent-gem update
Updating installed gems
Updating aws-partitions
Fetching: aws-partitions-1.174.0.gem (100%)
Successfully installed aws-partitions-1.174.0
Parsing documentation for aws-partitions-1.174.0
Installing ri documentation for aws-partitions-1.174.0
Installing darkfish documentation for aws-partitions-1.174.0
Done installing documentation for aws-partitions after 0 seconds
Parsing documentation for aws-partitions-1.174.0
Done installing documentation for aws-partitions after 0 seconds
.
.
ubuntu@ip-10-10-101-43:/home/ubuntu# sudo td-agent-gem install fluent-plugin-config-expander


 

Fluentd 수집 환경 설정


Fluentd를 이용해 Access Log (/var/log/nginx/access.log)를 수집하자. td-agent 의 환경 설정을 수정 하면 된다.

  1. pos_file 은 처리 기준의 위치를 기록하기 위해 필요하다.
  2. 수집된 정보는 TCP 24224 포트를 통해 10.10.102.203 로 전달된다.
  3. format 은 JSON 이다.
ubuntu@ip-10-10-101-43:/home/ubuntu# sudo vi /etc/td-agent/td-agent.conf
# 소스 (nginx 의 access log)
<source>
        @type config_expander
        <config>
                @type tail
                path /var/log/nginx/access.log
                pos_file /var/log/nginx/access.log.pos
                format json
                tag ${hostname}/nginx.access
        </config>
</source>

# 집계된 데이터로 전송
<match *.**>
  type forward
  retry_limit 5
  flush_interval 5s
  <server>
    host 10.10.102.203 # 중앙 서버 IP
    port 24224
  </server>
</match>

 

pos 파일을 만들고 Fluentd 를 시작하자.

ubuntu@ip-10-10-101-43:/home/ubuntu# touch /var/log/nginx/access.log.pos
ubuntu@ip-10-10-101-43:/home/ubuntu# sudo service td-agent configtest
 * td-agent # 색상이 없으면 OK
ubuntu@ip-10-10-101-43:/home/ubuntu# sudo service td-agent start
td-agent 14620     1  0 Jun11 ?        00:00:11 /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 14626 14620  0 Jun11 ?        00:00:21 /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

 

 STEP #2

AWS ElasticSearch 생성


2015년 10월, AWS Service 에 ElasticSearch 가 도입됐다. Self-Install 하는것과 차이는 명확하다. 감이 오지 않는다면 EC2 Instance 에 MySQL 을 직접 설치하는것과 RDS 의 MySQL 을 사용하는 것의 차이를 생각해 보자.

  1. RDS 의 MySQL 은 플러그인 설치가 불가능하다.
  2. 최상위 관리자의 권한이 제한된다.
  3. 상대적으로 비싸다.
  4. 버전이 현행버전 대비 한단계 이상 뒤쳐진다.

물론 관리형 서비스가 추가 되었을 때의 장점은 명확하다.

  1. 전문 지식이 없어도 환경 구축이 가능하다.
  2. 기본적인 모니터링 및 관리가 편리하다.

개인적으로 AWS ElasticSearch Service는 공식 형태소 분석기 노리(Nori)를 지원하지 않아 불만이 있지만 (5.x 부터 운전한닢을 포함하고 있다. 그나마 다행) 이번 로그 분석에서는 한글 형태소 분석이 불필요하기 때문이 이 요소는 무시하고자 한다. (생각해 보면 RDS Serverless 는 MySQL 5.6 기반이라 N-Gram. Full-Text Search 를 지원하지 않는다. 이게 Cloud의 단점이라면 단점)

AWS ElasticSearch Service 를 생성하자.

ElasticSearch Service 를 생성하는건 간단하다. 본인 인프라에 맞게 환경을 설정하고 IAM 정책을 부여하면 된다.

  1. 도메인 이름은 ELB와 마찮가지로(ELB보다 더짧다. ELB 32자 제한) 제한 길이가 짧다.
  2. EC2 등의 다른 서비스와 달리 ElasticSearch는 t2.small 가 Free-tier 대상이다.
  3. Public Access 는 제한하고. Private Subnet 에 위치함으로서 서비스를 보호하자.

생성하는데 약 10분 내외가 소요된다. Kibana 로 생성 상태를 확인해 보자.

 

AWS IAM 정책 추가


불특정 다수가 ElasticSearch 에 접근하는 것을 막기 위한 가장 기본적인 조치로 IAM 으로 제어하는 방법이 있다. Cognito 를 사용하는 방법도 있지만, 앞서설치한 플러그인(fluent-plugin-aws-elasticsearch-service)은 IAM 기반 억세스를 지원한다. 추가 방법은 간단하다.

  1. 정책 편집기로 ElasticSearch 를 위한 정책을 추가한다.
  2. 다른 서비스와 달리 개발자 및 Fluentd 를 위해서라면 대부분의 권한을 부여하자
  3. 그리고 삭제 기능만 제한하자.

(자사 정책은 최고 책임자만 서비스 삭제 및 종료가 가능하고, 개발자가 제어하려면 관리 Tag 를 추가해 기록을 남기게 되어 있다.)

생성된 정책을 연결함으로서 완료 접근 준비가 완료됐다.

 

※ 해당 Group 전체가 아니라 특정 사용자만 접근 가능하게 하려면 ElasticSearch 내 사용자 액세스 수정이 필요하다.

 

 

취합용 EC2 Instance 에 Fluentd 설치


Access Log 를 집계한 데이터를 행선지(ElasticSearch)로 전달할 EC2 Instance 다. Fluentd(td-agent) 설치 방법은 동일하며, 플러그인에서 차이가 있다. 플러그인 목록 중 핵심은 fluent-plugin-aws-elasticsearch-service 으로 AWS ElasticSearch 에 맞게 커스터마이징 되어있는데, 사용자 인증을 IAM KEY 로 대신할 수 있게 도와준다. (뿐만 아니라, Region 및 접근 경로를 EndPoint 로 지정할 수 있다)

※ 만약 AWS ElasticSearch Service 를 사용하지 않는다면 fluent-plugin-elasticsearch 를 설치하면 되고, 내용상 큰 차이는 없다.

ubuntu@ip-10-10-102-203:~$ sudo td-agent-gem install fluent-plugin-config-expander
ubuntu@ip-10-10-102-203:~$ sudo td-agent-gem install fluent-plugin-filter
ubuntu@ip-10-10-102-203:~$ sudo td-agent-gem install fluent-plugin-forest
ubuntu@ip-10-10-102-203:~$ sudo td-agent-gem install fluent-plugin-typecast
ubuntu@ip-10-10-102-203:~$ sudo td-agent-gem install fluent-plugin-parser
ubuntu@ip-10-10-102-203:~$ sudo td-agent-gem install fluent-plugin-aws-elasticsearch-service

 

Fluentd 의 동작을 위해 td-agent 설정을 변경하자.

ubuntu@ip-10-10-102-203:~$ sudo vi /etc/td-agent/td-agent.conf 
# 집계 서버로 부터 데이터를 수신한다. Port 는 서로 일치해야 한다.
<source>
  type forward
  port 24224
</source>

# Parse Data의 일부를 Integer로 캐스팅한다. > JSON 형태로 값을 전달하기 때문에 불필요해짐
#<match parsed.*nginx.access**>
#  type typecast
#  item_types response_time:integer,size:integer,status:integer
#  prefix casted
#</match>

# Parse Data를 ElasticSearch 에 저장하자.
<match casted.parsed.*nginx.access**>
  type_name nginx
  @type "aws-elasticsearch-service"
  tag_key @log_name
  include_tag_key true
  logstash_format true
  flush_interval 10s
  buffer_type file
  buffer_path /var/log/td-agent/buffer/casted.nginx.access.buffer
  <endpoint>
    url {VPC 엔드포인트}
    region {Region}
    access_key_id "{Access key ID}"
    secret_access_key "{Secret access key}"
  </endpoint>
</match>

 

그리고 두 서버의 Fluentd 를 모두 실행 함으로서 AWS ElasticSearch Service에 Log 전달이 시작된다.

※ Log 의 위치는 /var/log/td-agent 이며, error 가 없다면 무시해도 된다.

# td-agent 시작하기
root@ip-10-10-101-43:/etc/td-agent# service td-agent start

# Log 예제
# 수집
2019-06-13 18:27:29 +0900 [info]: #0 starting fluentd worker pid=9135 ppid=9127 worker=0
2019-06-13 18:27:29 +0900 [info]: #0 delayed_commit_timeout is overwritten by ack_response_timeout
2019-06-13 18:27:29 +0900 [info]: #0 following tail of /var/log/nginx/access.log
2019-06-13 18:27:29 +0900 [info]: #0 fluentd worker is now running worker=0

# 취합
2019-06-13 09:26:43 +0000 [info]: #0 starting fluentd worker pid=1545 ppid=1540 worker=0
2019-06-13 09:26:43 +0000 [info]: #0 listening port port=24224 bind="0.0.0.0"
2019-06-13 09:26:43 +0000 [info]: #0 fluentd worker is now running worker=0
2019-06-13 09:27:26 +0000 [warn]: #0 no patterns matched tag="fluent.info"

 

Kibana 설정

Kibana Web 에 접속하면, 기본적으로 Management Tag 이 선택되어 있다.  Index 패턴을 지정해야 하기 때문이다. 우린 앞선 설정에서 logstash_format 를 true 로 했기 때문에 Kibana 는 logstash 의 패턴을 추천할 것이다.

기준 값은 앞서 지정한 @time 이다.

 

수집되는 데이터를 확인할 수 있다.

 

 

 

 

 

Exit mobile version