top bar

글 목록

레이블이 logstash인 게시물을 표시합니다. 모든 게시물 표시
레이블이 logstash인 게시물을 표시합니다. 모든 게시물 표시

2017년 7월 28일 금요일

[Elasticsearch] 'Docker Compose' 로 ELK 스택 구성


 이번엔 Docker를 이용해 ELK 스택을 구성해 보겠다. Docker는 LXC 기술중 하나로서, 이식성이 뛰어난 격리된 프로세스 컨테이너를 통해 어플리케이션을 구동하는 방식이다. Docker 관련 문서들은 차고 넘치니, 따로 정리하지는 않겠다.

Docker Compose는 이러한 Docker 컨테이너 여러개를 조합(Compose)하여 설정 할 수 있게 해주고, 따라서 다수의 컨테이너 설정을 하나의 파일에서 가능하게 해 준다. 또한 설정된 컨테이너를 한번에, 또는 각각 하나씩 구동 할 수도 있다.

하지만 단 하나의 컨테이너를 구동할때도 Docker Compose가 유용하다.
가령, 아래의 tomcat 컨테이너를 띄우는 Docker 명령이 있다고 하자.

$ docker run -it --rm -p 8888:8080 tomcat:8.0

 위의 설정과 동일한 컨테이너를 Docker Compose로는 아래와 같이 설정하여 구동할 수 있다.

version: '2'
services:
  tomcat:
    image: tomcat:8.0
    ports:
      - 8888:8080

뭔가 텍스트가 더 많이 들어 간 것 같지만 기분 탓일 거다. 일단 깔끔하게 YAML 형식을 지원하는 Docker Compose 설정이 보기가(가독성이) 더 좋아 보인다. 위의 기본 docker 명령 예제에서 입력한 옵션은 많지 않지만, 훨씬 더 복잡한 옵션을 입력하고 컨테이너를 구동한다고 했을때 옵션을 입력하기도, 확인하기도 어려울 것이다. 따라서, 복잡한 하나 또는 여러개의 컨테이너 설정, 그리고 그 컨테이너들간의 관계를 설정 해야할 때는 당연히 뒤도 안보고 Docker Compose를 사용해야 한다.



docker-compose.yml 작성



ELK를 위한 docker-compose.yml 파일의 기본 골격은 아래와 같다.

version: '2'
services:
 elasticsearch:
  .
  .
  .

 logstash:
  .
  .
  .

 kibana:
  .
  .
  .

하나씩 살펴보자.

1) Elasticsearch
elasticsearch:
  image: docker.elastic.co/elasticsearch/elasticsearch:5.4.0
  container_name: elasticsearch
  volumes:
   - ./elasticsearch/config/elasticsearch.yml:/usr/share
/elasticsearch/config/elasticsearch.yml
   - ./elasticsearch/data:/usr/share/elasticsearch/data
  environment:
   ES_JAVA_OPTS: "-Xmx2048m -Xms2048m"
  ports:
   - 9200:9200
   - 9300:9300

'volumes' 설정을 통해서 외부의 파일시스템과 컨테이너 내부의 파일시스템을 마운트한다. [외부디렉토리]: [내부디렉토리] 와 같은 형식으로 마운트할 수 있는데, 위에서 보는것과 같이 파일 자체(elasticsearch.yml)도 마운트 가능하다.

'environment'로 elasticsearch 인스턴스의 JVM 옵션을 줄 수 있다. 'ports' 또한 컨테이너 외/내부의 포트를 설정하여 해당 포트를 통한 통신을 가능하게 해 준다. 별거 없다. ㅇㅇ

2) Logstash
 logstash:
  image: docker.elastic.co/logstash/logstash:5.4.0
  container_name: logstash
  command: logstash -f /usr/share/logstash/pipeline/logstash.conf
  volumes:
   - ./logstash/config/:/usr/share/logstash/config/
   - ./logstash/pipeline/:/usr/share/logstash/pipeline/
  environment:
   LS_JAVA_OPTS: "-Xmx2048m -Xms2048m"
  ports:
   - 10080:10080
  depends_on:
   - elasticsearch

elasticsearch 설정과 흡사하다. 단, 'command' 설정으로 컨테이너를 구동할때 'logstash -f ... ' 명령어로 logstash 를 실행하도록 한다. 물론 '-f' 옵션 다음의 오는 경로는 컨테이너 내부 파일 경로이다.

'depends_on' 은 'elasticsearch' 컨테이너가 시작한 후에 logstash 컨테이너를 구동하도록 하는 설정이다.

3) Kibana
 kibana:
  image: docker.elastic.co/kibana/kibana:5.4.0
  container_name: kibana
  volumes:
   - ./kibana/config/:/usr/share/kibana/config
  ports:
   - 5601:5601
  depends_on:
   - logstash

설명할 건 다 했으니 생략.



docker-compose 구동



'docker-compose.yml' 파일이 위치하고 있는 디렉토리에서,
아래와 같은 명령을 통해 구동 한다.

$ docker-compose up -d

로그를 보고 싶다면 아래 명령어를 사용한다.

$ docker-compose logs -f --tail="100" [service_name]

설정파일의 전체 내용은 아래에 있다.

https://gist.github.com/JuPyoHong/17362d9e0b64256b627dc063b00cc357


2015년 9월 3일 목요일

[Elasticsearch] Logstash를 이용한 bulk 데이터 인덱싱

들어가며..



이전 포스트에서 Logstash와 Elasticsearch를 연동하여 인덱싱하는 방법을 살펴보았다. 참고

오늘은 bulk format의 데이터를 Logstash를 이용해 인덱싱하는 방법을 정리하겠다. 하지만 기본적으로 Logstash는 bulk format을 처리하지 못한다. 왜 그런지 아래 예제를 보며 살펴보겠다.

일단 로그스태쉬 configuration은 아래와 같이 작성한다.
input {
        file {
                codec => json
                path => "/home/asuraiv/logstash_data/*.json"
        }
}

output {
        elasticsearch {
                host => "localhost"
                index => "school"
                index_type => "students"
                protocol => "http"
                template_name => "school"
        }
        stdout {
                codec => rubydebug {
                        metadata => true
                }
        }
}
input plugin은 'file'을 사용하였다. 'path' 값으로 설정된 경로안에 json 확장자 파일이 저장되면 Logstash의 file input plugin이 자동으로 그 파일을 읽어 output으로 보내준다.

아래와 같은 bulk format을 저장하여 인덱싱을 시도해보자
{ "index" : { "_index" : "school", "_type" : "students", "_id" : "1" }}
{ "address" : "newyork", "name" : "james", "class" : "A" }
하지만 결과는 아래와 같다
{
         "index" => {
        "_index" => "school",
         "_type" => "students",
           "_id" => "1"
    },
      "@version" => "1",
    "@timestamp" => "2015-09-03T08:03:14.064Z",
          "host" => "cweb02.ami",
          "path" => "/home1/irteam/logstash_data/data.json",
     "@metadata" => {
        "retry_count" => 0
    }
}
{
       "address" => "los engeles",
          "name" => "kim",
         "class" => "C",
      "@version" => "1",
    "@timestamp" => "2015-09-03T08:03:14.065Z",
          "host" => "cweb02.ami",
          "path" => "/home1/irteam/logstash_data/data.json",
     "@metadata" => {
        "retry_count" => 0
    }
}
원래 bulk format은 첫번째줄이 meta정보, 두번째줄이 source 데이터이다. 따라서 2줄이 하나의 event로 인식되야 하는것인데 지금은 각 라인이 서로 다른 event로 인식되어 따로따로 인덱싱이 되었다.

bulk format에서 첫째줄 메타정보에 인덱싱을 수행할 인덱스, 타입, id값등을 설정할 수 있지만 위와같은 결과가 나오면 이 메타정보가 아무 소용이 없다. 두번째 줄의 데이터가 인덱싱이 된것도 Logstash configuration에 'index'와 'index_type'이 명시되어있기 때문에 정상적으로 인덱싱이 된것이다.

따라서 bulk format을 정상적으로 인지하면서, 메타정보에 맞게 유동적으로 index, type, id등을 설정하여 인덱싱을 하려면 어떻게 해야할까?

es_bulk codec



input 단계에서 bulk format을 파싱하여 메타데이터와 source데이터를 하나의 이벤트로 묶어주는 codec이 있다.

Logstash 설치 폴더의 'bin' 에서 아래와 같은 명령어로 설치가 가능하다 (Logstash 1.4+)
$ ./plugin install logstash-codec-es_bulk
그리고 configuration 파일의 input 설정부분에 아래와 같이 작성한다
input {
        file {
                path => "/home/asuraiv/logstash_data/*.json"
                codec => es_bulk {
                }
        }
}
위의 하이라이트된 설정을 추가한다. es_bulk 코덱안에는 별다른 설정이 없는 그냥 비어있는 중괄호 { } 이다.

이 코덱을 적용하면 bulk format의 메타데이터 부분은 '@metadata' 로 넣어주고, filter나 output plugin에서 "{[@metadata][_index]}" 와 같은 방식으로 접근할 수 있다. 따라서, output pulgin을 아래와 같이 작성하면 bulk format에 메타데이터에 따라 유동적으로 인덱싱할 인덱스, 타입, id를 지정할 수 있다
elasticsearch {
        host => "localhost"
        index => "%{[@metadata][_index]}"
        index_type => "%{[@metadata][_type]}"
        document_id => "%{[@metadata][_id]}"
        protocol => "http"
        template_name => "school"
}



2015년 8월 7일 금요일

[Elasticsearch] Logstash와 Elasticsearch의 연동

Elasticsearch output Plugin

Elasticsearch의 output 플러그인은 'node', 'transport', 'http' 의 3가지 프로토콜이 있다.
여기서는 Elasticsearch 클러스터의 9200번포트로 직접 접속하여 데이터를 전송하는 방식인 'http' 프로토콜을 통해서, Logstash와 연동해보도록 한다.


1. 기초

아래와 같은 클러스터 구조가 있다.









일단 간단하게 stdin 으로 json 데이터를 입력받고, Elasticsearch에 색인하는 작업부터 해보겠다.

logstash.conf 파일은 아래와 같이 작성한다.
input {
        stdin {
                codec => json
        }
}

output {
        elasticsearch {
                host => "localhost"
                index => "school"
                index_type => "students"
                protocol => "http"
        }
        stdout {
                codec => rubydebug { }
        }
}
위와 같이 output 설정에 elasticsearch { } 를 작성하고, 중괄호 안에 각종 설정을 세팅한다. protocol은 'http' 로 작성한다. 이 protocol을 생략하게되면 default 설정인 'node'로 세팅되는데, 이 프로토콜은 로그스태쉬를 하나의 node로 실행시켜, 색인작업을 하고자 하는 elasticsearch cluster에 합류시킨다.

일단 지금은 http 프로토콜을 사용한다 host, index, index_type 등의 설정은 localhost의 elasticsearch 서버로, shcool이라는 인덱스의 students 타입에 데이터를 색인 시키는 설정이다.

입력된 값들이 제대로 인식이 되었는지 확인하기 위해서, stdout 설정도 추가하였다.

실행시켜본다.
./logstash -f logstash.test.conf
Logstash startup completed
그리고 { "class" : "A", "name" : "john" } 를 입력해보자
{ "class" : "A", "name" : "john" } ## 입력
{ ## 여기서부터 stdout rubydebug codec에 의해 출력되는 부분.
         "class" => "A",
          "name" => "john",
      "@version" => "1",
    "@timestamp" => "2015-08-07T07:11:41.975Z",
          "host" => "cweb02.ami",
     "@metadata" => {
        "retry_count" => 0
    }
}
출력을 보니 제대로 색인이 된것 같다.
head 플러그인 화면으로 가서 확인해보자.

위 logstash.conf 파일에서 설정해 준 대로, 'school' 인덱스와 'students' 타입이 동적으로 생성되며, 색인이 성공했음을 확인 할 수 있다.


2. Template 사용

템플릿을 사용하여 미리 index와 type의 스키마를 정해놓고 데이터를 색인 할 수 있다.
아래와 같이 템플릿을 정의하자.
curl -XPUT 'localhost:9200/_template/school?pretty' -d '{
    "template" : "school",
    "settings" : { "index.refresh_interval" : "5s" },
    "mappings" : {
        "students" : {
            "properties" : {
                "class" : { "type" : "string", "store" : "yes", index : "not_analyzed" },
                "name" : { "type" : "string",  "store" : "yes" },
                "address" : { "type" : "string",  "store" : "yes" }
            }
        }
    }
}'
'students' 라는 인덱스 템플릿을 하나 생성하였다. 인덱스 템플릿에 관한 자세한 내용은 아래의 doc 문서를 확인해보자

https://www.elastic.co/guide/en/elasticsearch/reference/1.6/indices-templates.htm

템플릿을 생성하였으면, Logstash가 해당 템플릿을 사용하여 색인을 할 수 있도록 설정해 놓아야 한다. 바로 아래와 같이 설정한다.
elasticsearch {
                host => "localhost"
                index => "school"
                index_type => "students"
                protocol => "http"
                template_name => "school" # 바로 여기 설정
        }
}
자, 다시 Logstash를 실행시키고 { "class" : "A", "name" : "iron man", "address" : "newyork" } 와 같은 데이터를 입력한다
{ "class" : "A", "name" : "iron man", "address" : "newyork" }
{
         "class" => "A",
          "name" => "iron man",
       "address" => "newyork",
      "@version" => "1",
    "@timestamp" => "2015-08-07T07:50:33.537Z",
          "host" => "cweb02.ami",
     "@metadata" => {
        "retry_count" => 0
    }
}
템플릿대로 document가 색인되었는지 head플러그인에서 확인해보자.

아래는 템플릿을 사용하지 않았을때와 사용했을때의 인덱스 메타데이터의 차이이다.

1) 템플릿을 미적용한 경우





















Elasticsearch의 기본 idea는 realtime 으로 document를 색인하는것이다. 따라서 동적으로 인덱스와 타입이 생성된다. 위의 경우는 정해진 스키마없이 동적으로 인덱스와 타입이 생성된 경우이며 dynamic_templates 라는 기본 템플릿이 적용된다.

2) 템플릿을 적용한 경우





















위의 경우는 템플릿을 적용한 경우이다. 위에서 생성한 템플릿의 필드설정대로, 'class' 필드는 index 설정이 'not_analyzed'로 되어있다. 템플릿이 정상적으로 적용되어 데이터가 색인되었다는 증거다.

2015년 7월 21일 화요일

[Elasticsearch] Logstash 설치와 기본개념

1. 개요



Logstash는 입출력 도구이다. 다양한 종류의 로그 (System logs, webserver logs, error logs, application logs) 뿐만아니라 입력되는 모든 종류의 데이터를 가공하여 출력할 수 있다.

Elasticsearch와 궁합이 잘 맞아 지금은 Elasticsearch의 공식 패키지에 포함되어 있다고 한다. 이제 그 Logstash 설치해보고 기본적인 개념들을 살펴보도록 한다.

2. Logstash 설치와 간단예제



일단 설치해보자.

1) download
$ wget 'http://download.elastic.co/logstash/logstash/logstash-1.5.1.tar.gz'

2) 압축풀기
$ tar -xvzf logstash-1.5.1.tar.gz

끝났다(..) 정말이다.

Logstash는 위에서 언급했듯이 입출력 도구이며,
input > filter > output 의 pipeline구조로 구성되어있다.










  • input : 데이터를 받아들인다
  • filter : 데이터를 가공한다
  • output : 데이터를 출력한다

이러한 input, filter, output 설정은 직접 configuration 파일을 작성하여 설정해야하는데,
문법은 아래와 같다
input {
        .
        .
        .
}

filter {
        .
        .
        .
}

output {
        .
        .
        .
}
엄청 간단해 보인다. 하지만 어떤 기술이든 마찬가지로 기본은 쉬워보이는법,, 조금만 파고들어가면 걷잡을 수 없이 미로를 헤맬수  있다.

여튼 가장 기본적인 설정 즉, 입력은 stdin, 출력은 stdout 으로 설정하여 어떤식으로 입출력을 처리하는지 살펴보자. 일단 설정파일은 아래와 같이 매우 간단하다.
$ cat logstash.conf
input {
        stdin { }
}

output {
        stdout {
                codec => rubydebug { }
        }
}
주목할 부분은 stdout 설정에 codec을 추가했다는 것이다. rubydebug는 출력을 보기좋은 포맷으로 바꿔 준다.

이제 logstash 압축을 해제한 디렉토리의 /bin 디렉토리로 이동하여 아래와같은 명령어로 conf 파일과 함께 실행 시킨다.
$ ./logstash -f [configuration file path]/logstash.conf
실행이 되었으면 stdin 으로 'Hello world'를 입력해보자
$ ./logstash -f logstash.conf
Logstash startup completed
Hello world!
{
       "message" => "Hello world!",
      "@version" => "1",
    "@timestamp" => "2015-07-21T11:47:37.140Z",
          "host" => "cweb02.ami"
}
표준 입력으로 Hello world! 라는 문자열을 입력했고, 표준 출력으로 출력이되며 rubydebug 코덱으로 인해 json 형식으로 출력된다. 이때 message, @version, @timestamp 등의 필드는 Logstash에 내장 되어있는 필드라고 볼 수 있다.

input, output 플러그인의 종류는 매우 많다. 물론 데이터의 출력과 동시에 Elasticsearch에 색인작업을 해주는 elasticsearch output plugin도 존재한다.
아래의 문서들을 참고하자!

  • input plugins : https://www.elastic.co/guide/en/logstash/current/input-plugins.html
  • output plugins : https://www.elastic.co/guide/en/logstash/current/output-plugins.html
  • codec plugins : https://www.elastic.co/guide/en/logstash/current/codec-plugins.html

filter 설정을 적용하면, 입력으로 들어온 데이터를 가공하는 작업을 수행한다. 예를들어 json 형식으로 데이터가 입력되었다면 필드를 삭제, 추가하거나 필드의 값을 조작할 수 있다. 여기서는 json 데이터를 가공해주는 json filter를 적용해보자.

아래와 같이 conf 파일을 수정한다
input {
        stdin { }
}

filter {
        json {
                source => "message"
                add_field => { "new_field1" => "New field value!"  }
                add_field => { "new_field2" => "My name is %{name}" }
                remove_field => [ "useless_field" ]
        }
}

output {
        stdout {
                codec => rubydebug { }
        }
}
filter로서 'json' filter를 추가했다. source 설정은 가공할 데이터가 들어있는 field를 가리킨다. add_field는 말그대로 field를 추가하는 것으로서, static한 고정값을 가지고있는 field를 추가할 수도있고, 기존 source의 데이터의 필드값을 '%{field이름}' 을 사용해 접근할 수 있다.

이제 이러한 설정을 저장하고, 실행한 뒤 표준 입력으로 json 포맷을 던져보자.
$ ./logstash -f logstash.simple.conf
Logstash startup completed
{ "name" : "timmy", "address" : "boston", "useless_field" : "garbage value" }
{
       "message" => "{ \"name\" : \"timmy\", \"address\" : \"boston\", \"useless_field\" : \"garbage value\" }",
      "@version" => "1",
    "@timestamp" => "2015-07-21T12:03:38.577Z",
          "host" => "cweb02.ami",
          "name" => "timmy",
       "address" => "boston",
    "new_field1" => "New field value!",
    "new_field2" => "My name is timmy"
}
{ "name" : "timmy", "address" : "boston", "useless_field" : "garbage value" } 와 같은 json 데이터를 표준입력을 통해 입력했다. name필드값으로 'timmy', address필드값으로 'boston'을 입력하였고, 필드 삭제 예제를 위해 'useless_field'도 추가했다.

결과는? 입력된 값 바로아래에서 볼 수 있다. 일단은 위에서 설명한것처럼 message, @version, @timestamp, host 필드는 Logstash의 내장 필드이다. 그 아래로, name과 address가 정상적으로 출력되고 있고, remove_field 설정으로 인해 'useless_field'는 삭제되었다.

그리고 add_field 설정에서 정의된 new_field1, new_field2 필드들이 새로 추가 되었으며, 주목할만한건 new_field2 의 값인데, %{name} 와 같이 기존 데이터의 필드의 값을 접근하여 새로운 필드의 값으로 저장된 것을 볼 수 있다.

짐작했겠지만 다양한 종류의 데이터를 가공하기 위해 필터의 종류도 매우 다양하다. 아래를 참고 하자.

fileter plugins : https://www.elastic.co/guide/en/logstash/current/filter-plugins.html

3. 결론



아무튼 Logstash는 input, filter, output 의 파이프라인 구조로 되어있다는 점, 더불어 input과 output의 한 부분으로 동작하며 데이터를 변화시키는 codec도 기억하면 좋을 것이다.

Elasticsearch와의 연동은 다음 포스팅으로 미루겠다.. (힘듬)