본문 바로가기

데이터 과학/데이터 과학 실무

빠르게 사용해보는 Elasticsearch

이번에 문서 검색 기능을 개발할 일이 생겼는데 단순 단어매칭 보다는 고도화된 검색엔진이 필요하여 Elasticsearch를 알아보게 되었다.

Elasticsearch란?

Elasticsearch는 정형, 비정형 데이터 등의 모든 type의 데이터를 위한 검색 및 분석엔진이다. 대용량의 데이터를 다룰 수 있도록 분산형 feature들이 발달되어 있으며 REST API를 통해 데이터 조회, 입력, 삭제를 처리한다.

Installation

당분간은 개발용으로만 사용할 예정이지만 추후 서비스 가능성을 고려해 docker로 설치하려 한다. 설치 명령어는 Elasticsearch의 quick start 를 참고하였다.

docker network create elastic
docker pull docker.elastic.co/elasticsearch/elasticsearch:7.13.3

번거롭지만 docker container가 사용할 데이터 파일의 경로를 정해주기 위해 아래의 docker-compose.yml을 작성하여 실행을 하였다. (SSD 용량이 거의 다 차서 HDD에 데이터를 저장할 목적)

version: '2.2'
services:
  es01:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.13.4
    container_name: es01
    environment:
      - node.name=es01
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=es02,es03
      - cluster.initial_master_nodes=es01,es02,es03
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - data01:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
    networks:
      - elastic
  es02:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.13.4
    container_name: es02
    environment:
      - node.name=es02
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=es01,es03
      - cluster.initial_master_nodes=es01,es02,es03
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - data02:/usr/share/elasticsearch/data
    networks:
      - elastic
  es03:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.13.4
    container_name: es03
    environment:
      - node.name=es03
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=es01,es02
      - cluster.initial_master_nodes=es01,es02,es03
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - data03:/usr/share/elasticsearch/data
    networks:
      - elastic

volumes:
  data01:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /mnt/elasticsearch-data/data01
  data02:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /mnt/elasticsearch-data/data02
  data03:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /mnt/elasticsearch-data/data03

networks:
  elastic:
    driver: bridge

volumes의 device 경로는 임의로 설정할 수 있다. 용량이 넉넉한 디바이스로 지정하도록 하자.

설치가 잘 되었는지는 아래의 명령어로 확인할 수 있다.

curl -X GET http://localhost:9200/

Index 생성

설치가 제대로 되었다면 다음으로 할 일은 RDBMS의 database에 해당하는 index를 만들어야 한다. BM25 알고리즘 기반의 고도화된 검색엔진을 사용하기 위해서 similarity type을 BM25로 셋팅하여 index를 만들었다. 다른 파이썬 코드와 쉽게 연동하기 위해 필자는 아래 코드와 같이 파이썬으로 PUT request를 보냈다.

위의 명령어로 설치를 완료하였다면 elastic_url은 http://localhost:9200/ 이 된다.

import json
import requests

def create_bm25_idx(elastic_url, idx_name):
    req_url = '%s/%s' % (elastic_url, idx_name)
    data = {
        'settings': {
            'number_of_shards': 1,
            'index': {
                'similarity': {
                    'default': {
                        'type': 'BM25'
                    }
                }
            }
        }
    }
    headers = {'Content-Type': 'application/json'}
    req = requests.put(req_url,
                       data=json.dumps(data),
                       headers=headers)
    print(req.text)

Document 추가

다음은 document를 추가하는 함수를 작성할 차례이다. 위와 마찬가지로 파이썬으로 작성하였으며, 세 번째 인자로 파이썬 딕셔너리를 받아 json으로 변환 후 post 요청을 보내도록 구현하였다.

def add_doc(elastic_url, idx_name, doc):
    req_url = '%s/%s/_doc' % (elastic_url, idx_name)
    headers = {'Content-Type': 'application/json'}
    req = requests.post(req_url,
                       data=json.dumps(doc),
                       headers=headers)
    print(req.text)

Search

마지막으로 검색 함수를 작성한다. 검색 url은 index 경로 뒤에 _search를 붙여 만들 수 있다. 아래의 함수에서 attr_name은 컬럼명이고 query는 검색어이다.

def search(elastic_url, idx_name, query, attr_name):
    req_url = '%s/%s/_search?pretty' % (elastic_url, idx_name)
    data = {
        'query': {
            'match': {
                attr_name: query
            }
        }
    }
    headers = {'Content-Type': 'application/json'}
    req = requests.get(req_url,
                       data=json.dumps(data),
                       headers=headers)
    print(req.text)

검색의 추가적인 옵션은 elasticsearch 공식 문서를 참고하도록 하자.

 

인증, 암호화 적용

이 예제를 따라하면 사용자 인증기능과 https를 도입할 수 있다.

volume 셋팅은 위의 docker-compose.yml과 동일하게 적용할 수 있다.

본 블로그는 쿠팡 파트너스 활동을 포함하고 있으며, 이에 따른 일정액의 수수료를 제공받습니다.