Table of Contents
Logstash 를 이용해 로그를 프로세싱 해보자. Logstash 에 대한 기초적인 설정은 다음글에서 확인 가능하다.
또, 이 글은 Elastic 홈페이지에 내용을 기반으로 한다.
filebeat 설정 및 기동
먼저 파일 filebeat 설정을 다음과 한다.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | filebeat.inputs: - type: log   enabled: true   paths:     - /var/log/nginx/linux.systemv.pe.kr.access.log   tags: ["nginx_access_log"]   fields:     server_name: linux.systemv.pe.kr     log_type: nginx-log     nginx: true filebeat.config.modules:   path: ${path.config}/modules.d/*.yml   reload.enabled: false output.logstash:   hosts: ["192.168.97.32:5044"] | 
Elastic 홈페이지에는 간단하게 설정하도록 나오지만 여기서는 몇가지 설정을 추가 하였다. tags 를 설정하였고 fields 도 추가 하였다. 다음과 같이 시작 한다.
| 1 | ]$ ./filebeat -e -c filebeat.yml -d "publish" | 
logstash Nginx pipeline 설정
먼저 filebeat 으로부터 메시지가 잘 들어오는지 디버깅을 먼저 해보자. 다음과 같이 간단하게 설정을 해본다.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | input {     beats {         port => "5044"     } } #filter { # #} output {     stdout {         codec => rubydebug     } } | 
INPUT 에는 filebeat 으로부터 전송을 받기 위한 port 를 지정해준다. OUTPUT 에는 디버깅을 위한 화면출력을 설정해 준다.
테스트
filebeat 의 로그 파일에 한 줄 넣어준다.
| 1 | ]# head -1 linux.systemv.pe.kr.access.log  > /var/log/nginx/linux.systemv.pe.kr.access.log | 
이렇게 하고 난 후에 logstash 의 출력 로그를 보면 다음과 같이 나온다.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | {           "tags" => [         [0] "nginx_access_log",         [1] "beats_input_codec_plain_applied"     ],          "agent" => {              "version" => "8.1.1",         "ephemeral_id" => "55b51681-f855-46ae-8abc-f6fb7d78ebaa",                 "name" => "venus8.systemv.local",                 "type" => "filebeat",                   "id" => "dd5018a6-31ef-4e42-95f9-2bf7b63b50f9"     },         "fields" => {            "log_type" => "nginx-log",               "nginx" => true,         "server_name" => "linux.systemv.pe.kr"     },          "input" => {         "type" => "log"     },           "host" => {         "name" => "venus8.systemv.local"     },        "message" => "112.171.18.83 - - [16/May/2021:21:01:46 +0900] \"GET /wp-admin/ HTTP/2.0\" 200 34391 \"https://linux.systemv.pe.kr/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36\" \"-\"",            "log" => {           "file" => {             "path" => "/var/log/nginx/linux.systemv.pe.kr.access.log"         },         "offset" => 0     },          "event" => {         "original" => "112.171.18.83 - - [16/May/2021:21:01:46 +0900] \"GET /wp-admin/ HTTP/2.0\" 200 34391 \"https://linux.systemv.pe.kr/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36\" \"-\""     },       "@version" => "1",     "@timestamp" => 2022-03-24T08:22:42.515Z,            "ecs" => {         "version" => "8.0.0"     } } | 
여기서 보아야 할 것은 filebeat 에서 설정한 tags 와 field 다. 이것이 logstash 로 전송될때에 그대로 전송이 된다. message 에는 log 파일에 내용을 담고 있는데 이것을, 이제 필드별로 구별을 해야 한다. 이를 위해서는 로그의 형식을 알아야 한다.
Nginx 로그 형식
Nginx 의 로그 형식은 nginx 설정 파일에 log_format 에 기록되어 있다.
| 1 2 3 |     log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '                       '$status $body_bytes_sent "$http_referer" '                       '"$http_user_agent" "$http_x_forwarded_for"'; | 
이 형식은 필요에 따라서 변경 될 수 있다. 이 형식을 알아야 한다.
Logstash FILTER pipeline 설정
이제 Nginx Pipeline 에 FILTER 를 설정해야 한다. 일 FILTER 는 들어오는 메시지를 가공처리해주는데, grok 을 이용한다. 메시지를 가공처리하는데 미리 정의된 형식도 지원한다. 다음과 같이 해보자.
| 1 2 3 4 5 | filter {     grok {         match => { "message" => "%{COMBINEDAPACHELOG}"}     } } | 
filebeat 는 한번 읽은 로그는 다시 읽지 않는다. filebeat 는 중지 시키고, 데이터 디렉토리에 registry 디렉토리를 삭제 한다.
| 1 2 3 | ]# cd data ]# rm -rf registry ]# filebeat -e -c filebeat.yml -d "publish" | 
이렇게 하면 파일을 다시 읽는다. 하지만 결과는 필드로 구분되지 않았다. 이것은 미리 정의된 FILTER 가 적용되지 않았음을 의미 한다.
FILTER 의 적용은 grok 을 사용하는데, 이것을 매번 해보는건 힘들다. 그래서 온라인으로 테스트를 할 수 있도록 도와주는 사이트가 있다.
여기에서 샘플데이터를 넣은 후에 패턴을 grok 패턴으로 적용하면 결과를 보여준다. 이 패턴을 이용하면 된다.
grok 의 사용법은 %{SYNTAX:SEMANTIC} 형식인데, SYNTAX 는 패턴이다. SEMANTIC 는 그 패턴을 담는 변수라고 보면 된다. 그런데, 이 패턴은 미리 정의되어 있는데, 다음에서 확인 가능하다.
Nginx 형식에 맞는 grok 패턴을 다음과 같이 입력 해줬다.
| 1 2 3 4 5 | filter {     grok {         match => { "message" => "%{IPORHOST:remote_addr} - %{USER:remote_user} \[%{HTTPDATE:time_local}\] \"(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})\" %{NUMBER:status} (?:%{NUMBER:body_bytes_sent}|-) \"%{GREEDYDATA:referrer}\" \"%{GREEDYDATA:http_user_agent}\" \"%{DATA:forwarder}\"" }     } } | 
이렇게 한 후에 로그를 전송하면 다음과 같이 잘 파싱된다.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | {                 "log" => {         "offset" => 0,           "file" => {             "path" => "/var/log/nginx/linux.systemv.pe.kr.access.log"         }     },                "host" => {         "name" => "venus8.systemv.local"     },         "remote_addr" => "112.171.18.83",     "body_bytes_sent" => "34391",           "forwarder" => "-",         "remote_user" => "-",     "http_user_agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",               "agent" => {              "version" => "8.1.1",                   "id" => "41c16096-d133-441c-8f28-ac6182518441",                 "name" => "venus8.systemv.local",                 "type" => "filebeat",         "ephemeral_id" => "a6ce3e3a-a00b-427a-b7fb-8ee54c9f12d4"     },         "httpversion" => "2.0",                 "ecs" => {         "version" => "8.0.0"     },                "verb" => "GET",              "status" => "200",               "input" => {         "type" => "log"     },                "tags" => [         [0] "nginx_access_log",         [1] "beats_input_codec_plain_applied"     ],          "time_local" => "16/May/2021:21:01:46 +0900",              "fields" => {               "nginx" => true,            "log_type" => "nginx-log",         "server_name" => "linux.systemv.pe.kr"     },             "message" => "112.171.18.83 - - [16/May/2021:21:01:46 +0900] \"GET /wp-admin/ HTTP/2.0\" 200 34391 \"https://linux.systemv.pe.kr/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36\" \"-\"",            "referrer" => "https://linux.systemv.pe.kr/",            "@version" => "1",               "event" => {         "original" => "112.171.18.83 - - [16/May/2021:21:01:46 +0900] \"GET /wp-admin/ HTTP/2.0\" 200 34391 \"https://linux.systemv.pe.kr/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36\" \"-\""     },             "request" => "/wp-admin/",          "@timestamp" => 2022-03-24T09:35:43.924Z } | 
Elasticsearch 보안
Elasticsearch 가 버전이 높아지면서 보안이 강화 됐다. 더군다나 Security 플러그인을 활성화할 경우에 각종 Rules와 Roles 들이 생성된다. 뭔가를 하기 위해서는 인증을 거쳐야 한다는 뜻이다.
Logstash 는 최종적으로 Elasticsearch 로 데이터를 보내야 한다. 이에 대한 보안 설정이 필요한데, 이에 대한 자세한 설명은 다음에 잘 나와 있다.
CA 인증서 설정
Elasticsearch 8.1 을 설치할때에 CA 인증서가 config/certs 디렉토리에 생성 되었다. RPM 으로 설치하였을 경우에 /etc/elasticsearch/certs 디렉토리인데, 여기에 http_ca.crt 파일로 존재 한다. 이것을 Logstash 에 OUTPUT 필터에서 사용해야 한다.
| 1 2 | ]# mkdir ${LOGSTASH_HOME}/config/certs ]# cp /etc/elasticsearch/certs/http_ca.crt ${LOGSTASH_HOME}/config/certs/ | 
Logstash 를 위한 자격증명 만들기
자격증명을 만들기 위해서는 권한을 부여한 역할(Role) 를 만들어야 한다. Kibana 를 설치했다면 Management > Roles 에서 생성할 수 있다. 다음과 같이 만든다.
- Role 이름: logstash_writer
- Cluster Privileges: manage_index_templates, monitor, manage_ilm
- Indices Name: nginx-access-*
- Indices Privileges: write, create, create_index, manage, manage_ilm
이것은 다음과 같이 curl 을 이용할 수도 있다. 먼저 Role 을 위한 JSON 파일을 작성한다.
| 1 2 3 4 5 6 7 8 9 | {   "cluster": ["manage_index_templates", "monitor", "manage_ilm"],   "indices": [     {       "names": [ "nginx-access-*" ],       "privileges": ["write","create","create_index","manage","manage_ilm"]     }   ] } | 
그리고 이제 다음과 같이 curl 명령어를 작성해 실행하면 된다.
| 1 2 3 | ]$ curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic:1H=APM9Dxdatua43TtRj -X POST -H "Content-Type: application/json" https://192.168.97.32:9200/_security/role/logstash_writer -d @logstash_role.json {"role":{"created":true}} ]$ | 
이제 사용자를 만들어야 한다. 사용자를 만들때에는 패스워드도 함께 생성하고 앞에서 만든 logstash_writer 롤을 할당해 준다.
역시 이것도 다음과 같이 JSON 형식으로 생성이 가능하다.
| 1 2 3 4 5 6 | POST _security/user/logstash_internal {   "password" : "x-pack-test-password",   "roles" : [ "logstash_writer"],   "full_name" : "Internal Logstash User" } | 
logstash OUTPUT 파이프라인 설정
이제 Logstash 의 OUTPUT 파이프라인을 설정해야 한다. 다음과 같다.
| 1 2 3 4 5 6 7 8 9 10 11 12 | output {     if "nginx_access_log" in [tags] {         elasticsearch {             hosts => ["https://192.168.97.32:9200"]             cacert => '/opt/logstash/config/certs/http_ca.crt'             ssl => true             user => "logstash_internal"             password => "154321"             index => "%{[fields][log_type]}-%{+YYYY.MM.dd}"         }     } } | 
결론
logstash OUTPUT 파이프라인 설정이 되면 logstash 와 filebeat 을 재시작 하고 nginx 로그를 넣게 되면 이제 Elasticsearch 에 nginx-access-날짜 로 인덱스가 생성되면서 데이터가 적재된다.

