Posts 우분투에 ELK(Elasticsearch, Logstash, Kibana) 구축해보기
Post
Cancel

우분투에 ELK(Elasticsearch, Logstash, Kibana) 구축해보기

개요

  • 우분투 환경에 ELK를 구축하여 후암동의 평균 공동주택 공시가격을 구해본다.

환경 설명

  • Windows 10 pro(실제환경)에서 docker를 이용하여 우분투로 가상환경을 구축한다.
  • 실제환경의 ~/Document/elasticsearch 디렉토리와 가상환경 /home/elasticsearch를 연결한다.
  • Elasticsearch, Logstash, Kibana는 하나의 가상환경에 설치 및 실행한다.

우분투 환경 구축

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    # 실제환경의 ~/Document/elasticsearch에서 실행
    > docker run --name realty_lab -v %cd%:/home/elasticsearch -p 5601:5601 --memory=4g  -it -d ubuntu:20.04
    
    # 컨테이너 쉘로 접속
    > docker exec -it realty_lab bash
    
    # 가상환경 안에서 apt 저장소를 카카오 미러 서버로 변경
    $ cp /etc/apt/sources.list sources.list_backup
    $ sed -i -e 's/archive.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list
    $ sed -i -e 's/security.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list
    
    # 의존성 패키지 업데이트
    $ apt-get update && apt-get upgrade -y
    
    # wget, curl, vim, net-tools 설치
    $ apt-get install wget curl vim net-tools -y
    
    # elasticsearch라는 이름의 유저 생성
    $ adduser elasticsearch
    
    # elasticsearch로 로그인
    $ su elasticsearch
    

실험 데이터 다운로드

  • 개요
    • 국토교통부 공동주택 공시가격 csv 파일을 받아 실험한다(다운로드 링크).
    • 단순 wget으로는 받기 어려우므로 브라우저를 켜고 직접 다운로드한다.
    • 다운로드 한 zip 파일을 ~/Document/elasticsearch에 압축풀기한다.
    • 파일 인코딩이 euc-kr이므로 utf-8로 변환 후 사용한다.
    • 1GB가 넘는 큰 텍스트 파일이다. 따라서 후암동 주소만 분리하여 실험한다.
  • 데이터 정제
    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      
      # 파일명을 MULTI_OWNER_HSE_PRICE.csv로 변환
      $ mv [공동주택 공시가격 unzip 파일명] MULTI_OWNER_HSE_PRICES.csv
      
      # 파일 인코딩을 utf8로 변환
      $ iconv -f cp949 utf-8 MULTI_OWNER_HSE_PRICE.csv MULTI_OWNER_HSE_PRICE_iconv.csv
      
      # 확인
      $ head MULTI_OWNER_HSE_PRICE_iconv.csv
      
      # 이상 없으면 원본 지우고 utf8만 남김
      $ rm MULTI_OWNER_HSE_PRICE.csv
      $ mv MULTI_OWNER_HSE_PRICE_iconv.csv MULTI_OWNER_HSE_PRICE.csv
      
  • 시스템 언어 설정
    • 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
      
      # 유저가 elasticsearch이라면 관리자 권한 획득
      $ exit
      
      # 참고(https://ti.bqbro.com/21)
      # 기본 시스템 언어로는 쉘에서 한글 입력을 할 수 없다.
      # 따라서 기본 시스템 언어를 utf8로 변경해준다.
      $ apt-get install language-pack-ko
      $ apt-get install language-pack-ko-base
      
      $ echo "LANG=\"ko_KR.UTF-8\"" >> /etc/environment
      $ echo "LANG=\"ko_KR.EUC-KR\"" >> /etc/environment
      $ echo "LANGUAGE=\"ko_KR:ko:en_GB:en\"" >> /etc/environment
      
      $ echo "LANG=\"ko_KR.UTF-8\"" >> /etc/default/locale
      $ echo "LANG=\"ko_KR.EUC-KR\"" >> /etc/default/locale
      $ echo "LANGUAGE=\"ko_KR:ko:en_GB:en\"" >> /etc/default/locale
      
      $ echo "LANG=\"ko_KR.UTF-8\"" >> /etc/profile
      
      # 우분투 쉘에서 이탈하여 컨테이너 재시작
      $ exit
      > docker stop realty_lab
      > docker start realty_lab
      
      # 우분투 쉘에 재접근
      > docker exec -it realty_lab bash
      
  • 후암동 주소만 분리
    • 1
      2
      3
      4
      5
      6
      
      # elasticsearch 로그인
      $ su elasticsearch
      $ cd
      
      # 후암동 주소만 분리
      $ cat MULTI_OWNER_HSE_PRICES.csv | grep "후암동" > test_data.csv
      

엘라스틱서치 환경 구축

  • 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
    
    # 엘라스틱서치 다운로드(최신버전 링크: https://www.elastic.co/kr/downloads/elasticsearch)
    # 여기서는 7.14.1 버전 사용
    $ wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.14.1-linux-x86_64.tar.gz
    
    # 압축 해제
    $ tar -xvzf elasticsearch*.gz
    
    # 압축 파일 삭제
    $ rm elastic*.gz
    
    # nori_tokenizer 설치
    $ ./elasticsearch-7.14.1/bin/elasticsearch-plugin install analysis-nori
    
    # elasticsearch 데몬으로 실행
    $ ./elasticsearch-7.14.1/bin/elasticsearch -d
    
    # 실행확인(한 5분 후에 로드가 완료된다.)
    $ curl localhost:9200
    # 결과
    {
      "name" : "b2ca57c11320",
      "cluster_name" : "elasticsearch",
      "cluster_uuid" : "b7508aRxQFSGTG_kRN2AwA",
      "version" : {
        "number" : "7.14.1",
        "build_flavor" : "default",
        "build_type" : "tar",
        "build_hash" : "66b55ebfa59c92c15db3f69a335d500018b3331e",
        "build_date" : "2021-08-26T09:01:05.390870785Z",
        "build_snapshot" : false,
        "lucene_version" : "8.9.0",
        "minimum_wire_compatibility_version" : "6.8.0",
        "minimum_index_compatibility_version" : "6.0.0-beta1"
      },
      "tagline" : "You Know, for Search"
    }
    

로그스태시 환경 구축

  • 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
    
    # 로그스태시 OSS Only 다운로드(최신버전 링크: https://www.elastic.co/kr/downloads/logstash-oss)
    # 여기서는 7.14.1 버전 사용
    $ wget https://artifacts.elastic.co/downloads/logstash/logstash-oss-7.14.1-linux-x86_64.tar.gz
    
    # 압축 해제
    $ tar -xvzf logstash*.gz
    
    # 압축 파일 삭제
    $ rm logstash*.gz
    
    # jdk를 받아야한다. 아래는 elasticsearch와 logstash에서 지원하는 jdk 버전 목록.
    # https://www.elastic.co/kr/support/matrix#matrix_jvm
    # elasticsearch와 logstash를 같은 환경에 설치하였다면 두 버전 모두를 고려한 jdk를 받아야한다.
    # openjdk 11을 받으면 에러 없이 처리된다.
      
    # 유저가 elasticsearch이라면 관리자 권한 획득
    $ exit
    
    # openjdk 설치
    $ apt-get install openjdk-11-jdk -y
    
    # openjdk 설치 확인
    $ java -version
    $ javac -version
    
    # ~/.bashrc 마지막 줄에 자바홈 추가
    echo "# JAVA HOME directory setup" >> ~/.bashrc
    echo "export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64" >> ~/.bashrc
    echo "export PATH=\"$PATH:$JAVA_HOME/bin\"" >> ~/.bashrc
      
    # 환경 변수 변경 내역 반영(Reload ~/.bashrc)
    $ source ~/.bashrc
    
    # Logstash input plugin 설치
    $ ./logstash-7.14.1/bin/logstash-plugin install logstash-input-jdbc
    

인덱스 설정 파일 작성 및 인덱스 생성

  • 개요
    • 공동주택 공시가격 구조에 맞게 인덱스를 설정해준다.
    • 한국어 파싱을 위해 tokenizer로 nori_tokenizer를 사용한다.
    • fulltext search를 해야하는 컬럼만 type을 text로 지정해준다.
    • 타입에 대한 자세한 내용은 아래 링크를 참고한다.
      • https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html
  • 인덱스 파일 작성
    • 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
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      
      # multi_owner_hse_price_index.json
      {
        "settings": {
          "analysis": {
            "analyzer": {
              "my_analyzer": {
                "type": "custom",
                "tokenizer": "nori_tokenizer",
                "filter": "lowercase"
              }
            }
          }
        },
        "mappings": {
          "properties": {
            "기준연도": {
              "type": "short"
            },
            "기준월": {
              "type": "byte"
            },
            "법정동코드": {
              "type": "integer"
            },
            "도로명주소": {
              "type": "text"
            },
            "시도": {
              "type": "text"
            },
            "시군구": {
              "type": "keyword"
            },
            "읍면": {
              "type": "keyword"
            },
            "동리": {
              "type": "keyword"
            },
            "특수지코드": {
              "type": "interger"
            },
            "본번": {
              "type": "integer"
            },
            "부번": {
              "type": "integer"
            },
            "특수지명": {
              "type": "keyword"
            },
            "단지명": {
              "type": "text"
            },
            "동명": {
              "type": "text"
            },
            "호명": {
              "type": "text"
            },
            "전용면적": {
              "type": "double"
            },
            "공시가격": {
              "type": "double"
            },
            "단지코드": {
              "type": "keyword"
            },
            "동코드": {
              "type": "keyword"
            },
            "호코드": {
              "type": "keyword"
            }
          }
        }
      }
      
  • elasticsearch에 realty라는 이름의 인덱스 생성
    • 1
      
      $ curl -XPUT http://localhost:9200/realty -d @multi_owner_hse_price_index.json -H 'Content-Type: application/json'
      
  • 생성 확인
    • 1
      2
      3
      
      $ curl localhost:9200/realty
      # 결과
      {"realty":{"aliases":{},"mappings":{"properties":{"@timestamp":{"type":"date"},"@version":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"host":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"message":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"path":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"공시가격":{"type":"double"},"기준연도":{"type":"short"},"기준월":{"type":"byte"},"단지 명":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"단 지명":{"type":"text"},"단지코드":{"type":"keyword"},"도로명주소":{"type":"text"},"동리":{"type":"keyword"},"동명":{"type":"text"},"동코드":{"type":"keyword"},"법정동코드":{"type":"integer"},"본번":{"type":"integer"},"부번":{"type":"integer"},"시군구":{"type":"keyword"},"시도":{"type":"text"},"읍면":{"type":"keyword"},"전용면적":{"type":"double"},"특수지명":{"type":"keyword"},"특수지코드":{"type":"integer"},"호명":{"type":"text"},"호코드":{"type":"keyword"}}},"settings":{"index":{"routing":{"allocation":{"include":{"_tier_preference":"data_content"}}},"number_of_shards":"1","provided_name":"realty","creation_date":"1631061617189","analysis":{"analyzer":{"my_analyzer":{"filter":"lowercase","type":"custom","tokenizer":"nori_tokenizer"}}},"number_of_replicas":"1","uuid":"dyj6mpZIRmqxdgqsIz6QvQ","version":{"created":"7140199"}}}}}
      

로그스태시 실행

  • 로그스태시 config 파일 작성
    • 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
      
      # logstash.conf
      input {
        file {
          path => "/home/elasticsearch/test_data.csv"
          start_position => beginning
          sincedb_path => "/dev/null"
        }
      
      }
      
      filter {
        csv {
          columns => [
            "기준연도",
            "기준월",
            "법정동코드",
            "도로명주소",
            "시도",
            "시군구",
            "읍면",
            "동리",
            "특수지코드",
            "본번",
            "부번",
            "특수지명",
            "단지명",
            "동명",
            "호명",
            "전용면적",
            "공시가격",
            "단지코드",
            "동코드",
            "호코드"
          ]
          separator => ","
        }
      }
      
      output {
        stdout {
          codec => rubydebug
        }
      
        elasticsearch {
          action => "index"
          hosts => ["localhost:9200"]
          index => "realty"
        }
      }
      
  • 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
      
      $ ./logstash-7.14.1/bin/logstash -f logstash.conf
      # 결과
      ...
      {
             "시군구" => "용산구",
              "시도" => "서울특별시",
              "부번" => "0",
            "공시가격" => "910000000",
             "동코드" => "3",
            "path" => "/home/elasticsearch/test_data.csv",
              "읍면" => "",
            "host" => "b2ca57c11320",
            "기준연도" => "2020",
        "@version" => "1",
            "단지코드" => "256703",
              "동명" => "103",
             "호코드" => "9",
              "동리" => "후암동",
              "본번" => "458",
      "@timestamp" => 2021-10-04T07:43:54.413Z,
             "단지명" => "브라운스톤남산",
            "전용면적" => "166.56",
           "법정동코드" => "1117010100",
             "기준월" => "1",
           "특수지코드" => "0",
         "message" => "\"2020\",\"1\",\"1117010100\",\"서울특별시 용산구 후암로 65\",\"서울특별시\",\"용산구\",\"\",\" 후암동\",\"0\",\"458\",\"0\",\"\",\"브라운스톤남산\",\"103\",\"301\",\"166.56\",\"910000000\",\"256703\",\"3\",\"9\"",
            "특수지명" => "",
           "도로명주소" => "서울특별시 용산구 후암로 65",
              "호명" => "301"
      }
      ...
      

Elasticsearch로 후암동의 평균 공동주택 공시가격 계산

  • 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
    51
    52
    53
    54
    55
    
    $ curl http://localhost:9200/realty/_search?pretty -d '{
      "size": 0,
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "공시가격"
          }
        }
      },
      "query": {
        "bool": {
          "must": [
            { 
              "match": {
                "시도": "서울특별시"
              }
            },
            { 
              "match": {
                "시군구": "용산구"
              }
            },
            {  
              "match": {
                "동리": "후암동"
              }
            }
          ]
        }
      }
    }' -H 'Content-Type: application/json'
    # 결과
    {
      "took" : 156,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 10000,
          "relation" : "gte"
        },
        "max_score" : null,
        "hits" : [ ]
      },
      "aggregations" : {
        "avg_price" : {
          "value" : 2.801156181619256E8
        }
      }
    }
    
  • 평균 공시가격은 약 280,115,618원

키바나 설치 및 실행

  • 개요
    • 위의 예시처럼 직접 elasticsearch에 쿼리를 날려 검색할 수도 있지만
      키바나를 통해 GUI로 쉽게 검색 할 수도 있다.
  • 키바나 설치 및 실행
    • 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
      
      # 새로운 쉘 연결 생성
      > docker exec -it realty_lab bash
      
      # elasticsearch로 로그인
      $ su elasticsearch
      $ cd
      
      # 키바나 설치(링크: https://www.elastic.co/kr/downloads/kibana)
      # 여기서는 7.14.1 버전
      $ wget https://artifacts.elastic.co/downloads/kibana/kibana-7.14.1-linux-x86_64.tar.gz
      
      # 압축 해제
      $ tar -xvzf kibana*.gz
      
      # 압축 파일 삭제
      $ rm kibana*.gz
      
      # 키바나 실행(한 7분 후 로드 완료)
      $ ./kibana-7.14.1-linux-x86_64/bin/kibana -H 0.0.0.0 -p 5601 -e http://localhost:9200
      # 결과
      log   [07:53:37.182] [info][plugins-service] Plugin \"metricsEntities\" is disabled.
      log   [07:53:37.353] [warning][config][deprecation] plugins.scanDirs is deprecated and is no longer used
      log   [07:53:37.355] [warning][config][deprecation] Config key [monitoring.cluster_alerts.email_notifications.email_address] will be required for email notifications to work in 8.0.\"
      log   [07:53:37.359] [warning][config][deprecation] \"xpack.reporting.roles\" is deprecated. Granting reporting privilege through a \"reporting_user\" role will not be supported starting in 8.0. Please set \"xpack.reporting.roles.enabled\" to \"false\" and grant reporting privileges to users using Kibana application privileges **Management > Security > Roles**.
      log   [07:53:37.534] [info][server][NotReady][http] http server running at http://0.0.0.0:5601
      
      # 브라우저를 켜고 localhost:5601로 접속
      # 좌상단 햄버거 메뉴 클릭 
      #  > Analytics 하위의 Discover 클릭 
      #  > 인덱스 패턴이 없다면 realty*로 인덱스 패턴 생성 
      #  > 좌측 Field fiters 하위 Available fields에서 공시가격 클릭 후 Visualize 클릭
      #  > 우측 Vertical axis의 Median of 공시가격을 클릭하여 Average로 변경함
      
  • 키바나 검색 결과

실험 소감

  • 생각보다 ELK 전체를 구축했을때 요구 성능이 높아 당황했다(메모리 최소 4gb 필요).
  • 만약 전체 데이터로 실험했다면 훨씬 많은 메모리가 필요했을 것이라고 추측한다.
  • 실제로는 엘라스틱서치가 죽는 것을 방지하기 위해 3개 이상의 노드를 홀수 개로 생성한다.
    이 경우 필요 메모리는 더욱 늘어날 것이다.
  • 엘라스틱서치는 다양한 aggregation function을 제공한다.
    검색에 대하여 더 공부하면 지금보다 신기한 계산 결과도 얻을 수 있을 것 같다.

참고

This post is licensed under CC BY 4.0 by the author.