본문 바로가기

4.개발 및 운영 환경

CentOS에서 ELK 설치 및 설정

ELK는 Elasticsearch, Logstash, Kibana를 말하며, 로그를 수집하고 저장하며 분석할 수 있는 환경을 제공한다. 모두 오픈소스로 다양한 기능은 제공되지는 않지만, 기본적인 분석으로는 충분할 것으로 본다. 아직 필자도 이제야 사용하고 있는 중이라서 기본적인 내용으로만 정리했다.

작성자: http://ospace.tistory.com/,2018.08.12 (ospace114@empal.com)

들어가기

개발을 하면서 로그를 생성되어 디스크에 쌓이게 된다. 이런 로그를 분석하고 싶다면, 해당 시스템에 들어가서 필요한 로그파일(이전 저장된)을 찾아서 텍스트를 검색하거나, 날짜 등을 사용해서 원하는 로그를 찾게 된다. 이런 로그가 하나의 서버에만 저장되었다면 간단하지만, 여러 서버에 분산되거나, 다른 로그들과 같이 분석해야한다면 매우 힘들거나 번거로운 작업이 된다.
이런 로그들을 한 시스템에 모아서 구글 검색하듯이 필요한 내용을 찾고 비교한다면 훨씬 작업이 편리해질거라고 생각된다. 이런 환경을 구축하는게 ELK의 역할이다. 물론 ELK 말고도 다른 뛰어난 비슷한 오픈소스들이 있다. 이런 오픈 소스들의 비교는 여기서는 다루지 않고 단지 ELK 환경 구축만 다룰 것이다.
다음 환경은 CentOS이지만 대부분의 리눅스 환경이 비슷하기 때문에 어렵지는 않다. 그래도 Redhat 기반의 배포버전이라면 거의 똑같기 때문에 바로 적용이 가능하다.

아파치 설치

먼저 로그 생성을 위한 아파치 서버를 설치해보자. 아파치 서버 로그를 읽어서 전송하기 위한 목적이다. 단순히 로그 생성용이라고 보면 된다.

sudo yum install httpd

그리고, 서비스를 실행한다.

sudo service httpd start

sudo service httpd status

제대로 실행되는지는 브라우저로 접속해서 확인해본다.

tail -f /var/log/access_log

제대로 로그가 출력되면 제대로 동작 중이다.

Java 설치 및 Repo 설정

다음으로 ELK 설치전에 Java를 설치해야 한다. 만약 이미 자바를 설치했다면 굳이 다시 설치할 필요는 없다. 단지, 버전이 1.8 이상으로 설치되어 있어야 실행하는데 문제가 없다.
특별한 경우가 아니라면 OpenJDK를 설치하는걸 권장한다. 만약 Oracle의 JDK를 설치하는 경우는 수동으로 하기 때문에 해야할 일이 많다. 그리고 환경설정이 제대로 되지 않을 경우 실행에 문제가 발생한다.
여기서는 OpenJDK를 설치하는 방법을 다루겠다.

sudo yum -y install java-1.8.0-openjdk java-1.8.0-openjdk-devel

다음으로는 ELK 패키지를 설치하기 위한 저장소를 설정하겠다. 해당 내용은 yum를 위한 저장소 구성이다.

sudo vi /etc/yum.repos.d/elastic.repo

설정에 들어갈 내용이다.

[elastic-5.x]
name=Elastic repository for 5.x packages
baseurl=https://artifacts.elastic.co/packages/5.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md

그리고 yum 저장소 DB를 업데이트 하면 된다.

sudo yum check-update

Logstash 설치

logstash를 로그를 읽어서 elasticsearch로 전달하는 역할을 한다. 필요하면 로그를 분해하거나 추가 및 가공을 할 수 있다.
다음으로 logstash 설치이다.

sudo yum -y install logstash

설치가 되었다면, 간단하게 동작하는지 확인해보자.

sudo /usr/share/logstash/bin/logstash -e 'input { stdin { } } output { stdout {} }'

실행되었다면 임의로 텍스트를 입력해보자.

foo
{
      "@version" => "1",
          "host" => "insight",
    "@timestamp" => 2018-08-12T07:22:04.207Z,
       "message" => "foo"
}

맨처음에 있는 "foo"가 입력값이고 다음에 중괄호 부분이 출력되는 값이다. 여기서는 logstash에 대한 자세한 문법은 소개하지 않겠다. 해당 내용은 구글링을 통해서 확인해보면 훌륭한 내용들이 많이 있다. 여기까지 왔다면 logstash은 정상적으로 동작중임을 확인할 수 있다.
에제 앞에서 설치한 아파치의 로그파일을 읽어오는 부분을 확인해보자. 이제는 logstash에 아파치 로그 파일을 읽어오기 위한 환경설정 파일을 생성해보자.

sudo vi /etc/logstash/conf.d/apache.conf

아래 내용을 입력해보자.

input {
        file {
                path => "/var/log/httpd/access_log"
                start_position => beginning
        }
}

filter {
        grok {
                match => { "message" => "%{COMBINEDAPACHELOG}" }
        }
}

output {
        stdout {}
}

아직은 테스트용이라서 아파치 로그가 제대로 작동하는지 확인하기 위한 목적이다. 아직까지 output에는 stdout으로 콘솔 출력하도록 되어 있다. 실제 테스트를 해보자.

sudo /usr/share/logstash/bin/logstash --path.settings=/etc/logstash/

브라우저로 접속 테스트를 하면 쉘 콘솔에 아래와 같이 출력되면 정상이다.

(중략)
{
        "request" => "/noindex/css/fonts/Light/OpenSans-Light.ttf",
          "agent" => "\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36\"",
           "auth" => "-",
          "ident" => "-",
           "verb" => "GET",
        "message" => "192.168.35.148 - - [06/Aug/2018:03:01:10 +0900] \"GET /noindex/css/fonts/Light/OpenSans-Light.ttf HTTP/1.1\" 404 240 \"http://192.168.35.249/noindex/css/open-sans.css\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36\"",
           "path" => "/var/log/httpd/access_log",
       "referrer" => "\"http://192.168.35.249/noindex/css/open-sans.css\"",
     "@timestamp" => 2018-08-05T18:01:11.406Z,
       "response" => "404",
          "bytes" => "240",
       "clientip" => "192.168.35.148",
       "@version" => "1",
           "host" => "insight",
    "httpversion" => "1.1",
      "timestamp" => "06/Aug/2018:03:01:10 +0900"
}

여기까지 되었다면 elasticsearch로 로그 전송하기 위한 설정으로 변경하면 된다. apache.conf에서 마지막의 output 항목을 아래와 같이 수정하면 된다.

output {
    elasticsearch {
        hosts => "localhost:9200"
        index => "apache"
    }
}

마지막으로 서비스를 실행하면 된다.

sudo service logstash start

Elasticsearch 설치

elasticsearch를 설치해보자.

sudo yum install elasticsearch

기본 환경설정을 해보자.

sudo vi /etc/elasticsearch/elasticsearch.yml

아래 설정 내용을 수정하자.

# 자신 클러스터 이름
cluster.name: es-demo
# 자신이 바인딩할 네트워크 주소
network.host: 0.0.0.0
# 클러스터 구성 시 노드들이 서로 찾을 때 discovery router 로 사용할 노드 목록, 마스터노드 주소
discovery.zen.ping.unicast.hosts:
  - 192.168.35.1:9300
# JVM 메모리 swapping lock 여부
bootstrap.memory_lock: true    

다음으로 elasticsearch 서비스를 실행하기 위한 서비스 환경 설정을 수정하자.

sudo systemctl edit Elasticsearch

아래 내용은 메모리 관련 설정이다.

[Service]
LimitMEMLOCK=infinity

시스템 설정을 로딩하고 서비스를 실행해보자.

sudo systemctl daemon-reload

sudo systemctl restart elasticsearch

서비스가 정상적으로 동작하는지 확인해보자.

curl -i http://localhost:9200/

지금까지 해서 elasticsearch 설치 및 환경 구성을 하였다.

Kibana 설치

마지막으로 kibana를 설치해보자.

sudo yum install kibana

설치가 되었다면 kibana 환경설정을 수정해보자.

sudo vi /etc/kibana/kibana.yml

아래 내용을 설정한다.

#외부에서 접속하기 위해서
server.host: 0.0.0.0
elasticsearch.url: "http://localhost:9200"

마지막으로 서비스를 실행한다.

sudo service kibana start

지금까지 이상없이 설정되었다면 브라우저로 접속하면 페이지가 제대로 보일 것이다. 페이지가 제대로 보이면 "Index pattern"에 앞에서 logstash에서 설정한 index 값인 "apache"을 입력하면 자동으로 인식하여 "Create" 버튼이 보일 것이다.

물론 사전에 한번 아파치에 접속해서 로그를 발생해서 elasticsearch에 로그를 저장하고 앞의 내용을 실행한다.


Trableshooting

서비스를 자동으로 등록

서비스 등록 및 확인 하는 방법

chkconfig --list [서비스명]

chkconfig [서비스명] on

chkconfig --list [서비스명]

모든 서비스 등록하는 방법

chkconfig httpd on

chkconfig logstash on

chkconfig elasticsearch on

chkconfig kibana on

방화벽으로 인한 문제

간혹 방화벽으로 인해 접속되지 않은 경우가 발생한다. 간단한 방법은 방화벽을 중지하면 된다.

sudo service firewalld stop

이는 임시방편이기 때문에 테스트로 임시로 적용하는 목적으로 사용하고, 실제로는 방화벽에 등록해서 사용해야 한다.

sudo firewall-cmd --zone=pubic --permanent --add-port=80/tcp

sudo firewall-cmd --zone=pubic --permanent --add-port=5601/tcp

또는 직접 파일을 수정할 수도 있다.

sudo vi /etc/firewalld/zones/public.xml

해당 zone 영역에 아래 내용을 추가하면 된다.

파일 접근 권한 문제

logstash에서 로그 파일을 읽어 들일때에 접근 권한이 되지 않아서 로그 파일을 읽어 오지 못한다. 앞의 아파치 서버 로그 저장위치가 "/var/log/httpd"에 있는데 해당 디렉토리의 권한을 아래와 같이 설정해야 한다. 디렉토리에 읽기 뿐만 아니라 실행 권한도 있어야 한다.

sudo chmod 755 /var/log/httpd

해당 디렉토리 접근 권한이 apache로만 되어 있기에 logstash 권한으로는 로그파일을 읽어 올 수 없다. 그렇기 때문에 apache 그룹에 logstash를 추가해야 한다.

sudo usermod -aG apache logstach

그리고 logstash 테스트를 하다가 /var/lib/logstash 서브 디렉토리 소유자가 root나 다른 계정으로 변경되면서 logstash 서비스를 실행하는데 에러가 발생한다. 이를 아래와 같이 해결해야 한다.

sudo chown logstash:logstash -R /var/lib/logstash

SpringBoot 기본 로그 분석 패턴예

다음은 SpringBoot에서 생성되는 기본 로그에 대한 패턴을 작성한 예입니다.

input {
    file {
        type => "log4j"
        path => "/path_to_log/application.log"
        start_position => beginning
        codec => multiline {
            pattern => "^%{TIMESTAMP_ISO8601} "
            negate => true
            what => "previous"
        }
    }
}
filter {
    grok {
        match => {
            "message" => "%{TIMESTAMP_ISO8601:logdate}\s+%{LOGLEVEL:loglevel}\s+%{NUMBER:process}\s+---\s+\[%{DATA:thread}\]\s+(?<classname>\S+)\s+:\s+%{GREEDYDATA:text}"
        }
    }
    date {
        match => ["logdate", "yyyy-MM-dd HH:mm:ss,SSS", "ISO8601"]
    }
}
output {
    elasticsearch {
        hosts => ["localhost:9200"]
        index => "springboot"
    }
}

%으로 시작하는 키워드는 내부 정의된 패턴입니다. 또는 직접 정규식을 작성할 수 있습니다. 내부 정의된 패턴은 아래 링크를 참고하세요.

https://github.com/logstash-plugins/logstash-patterns-core/blob/master/patterns/grok-patterns

logstash에서 직접 grok 플러그인의 로그 패턴을 테스트하기가 쉽지 않다. 이를 온라인으로 조금 쉬게 확인할 수 있는 곳이 있다.

http://grokconstructor.appspot.com/do/match

_dateparsefailure가 발생하는 경우

위의 filter에서 logdate 필드에 패턴을 보면 ",SSS"로 되어 있다. 간혹 로그의 시간정보가 ".123" 형태로 되어 있는 경우가 있다. 이때에는 ".SSS"로 변경해야 "_dateparsefailure"가 tags에서 보이지 않는다.

중복 필드 데이터 발생 현상

간혹 필드 데이터가 중복으로 발생하는 경우가 있다.

이럴 상황 중에 하나가 중복으로 로그 처리를 설정했는데 제대로 되어 있지 않은 경우이다. 예를 들어;

  • 두개의 xml 설정 파일을 두고 읽어오는 log 파일 위치를 틀리지만 내용은 동일 한 경우
  • 하나의 xml 설정에 두개의 로그파일을 읽어오고 같은 필터를 통해서 처리하는 경우

위의 경우 로그를 중복 처리하게 되어 같은 로그가 중복해서 처리되어 데이터 중복이 발생한다. (ㅡ.ㅡ;;; 맞나?)
이럴 경우 하나의 설정 파일로 묶어서 처리한다.
간혹 output 항목에 if 문의 조건이 여러개 비교하는 경우가 잘못된 형식을 쓰는 경우에도 발생한다.

input {
    file {
        type => "log_1"
        path => "/path_to_log_1/application.log"
        ...
    }
    file {
        type => "log_2"
        path => "/path_to_log_1/application.log"
        ...
    }
    file {
        type => "log_3"
        path => "/path_to_log_1/application.log"
        ...
    }
}
filter {
    ...
}
output {
    if [type]=="log_1" {
        elasticsearch {
            hosts => ["localhost:9200"]
            index => "log_a"
        }
    }

    if [type] in ["log_2", "log_3"] {
        elasticsearch {
            hosts => ["localhost:9200"]
            index => "log_b"
        }
    }
}

주의할 부분으로 if [type] == "log_1" or "log_2"를 사용하는 경우 잘못된 처리가 된다. 같은 로그가 두번 저장될 수 있다. 이를 if [type] == "log_1" or [type] == "log_2" 또는 if [type] in ["log_1", "log_2"] 를 사용한다.

결론

ELK에 대한 설치를 다루었지만, ELK가 최고의 솔루션은 아니다. 또한 설치가 번거롭고, 설정하는게 생각보다 쉽지가 않았다. 특히, 유닉스에 파일 접근 권한 및 소유권 및 서비스 동작 매커니즘을 잘 알고 있어야 설치시 발생하는 문제에 대해서 쉽게 대응할 수 있다. 그렇지 않으면 잘못된 권한이나 설정이 발생할 가능이 높다. 공짜로 사용하는데 한방으로 쉽게 설치하고 설정도 쉽지 않다. 물론 기본 설치는 과거에 비해서 아주 편해졌다. 그러나 로그 수집과 분석이 무료로 어느정도 품질이 제공되기 때문에 나름 만족하고 있다. 특히 로그를 콘솔로 찾고 분석하는 불편함이 사라졌다는게 아주 큰 장점이다. 물론 많은 기능이 포함되어 있기 때문에 단순 로그 검색 뿐만 아니라 운영 관리에 더 활용할 수 있다는 점도 장점이다. 물론 상용 제품에 비해 기능이 부족할 수도 있지만, 거장한 기능이 필요없는 소규모 환경에서 적용하기에는 적합하다.

모드 즐프하시기 바랍니다.

참조

[1] 촨동, 2018.08.12, https://handongchoi.com/2017/10/28/install-elk/

[2] olafz, grok-patterns, 2018.08.22, https://github.com/logstash-plugins/logstash-patterns-core/blob/master/patterns/grok-patterns

[3] Hans-Peter Störr, Test grok patterns, 2018.08.22, http://grokconstructor.appspot.com/do/match

반응형