'검색엔진'에 해당되는 글 11건

  1. 2018.05.09 [Elasticsearch] Adaptive Replica Selection 기능
  2. 2018.05.09 [Elasticsearch] GET API 에 대해서
  3. 2018.04.24 [Elasticsearch] Synonym filter position 문제 개선
  4. 2018.04.24 [Logstash] Logstash 를 이용한 CSV 파일 Import를 하려면
  5. 2018.04.19 [Elasticsearch] Synonym filter 테스트
  6. 2018.04.18 [Elasticsearch] elasticsearch-docker plugin 설치는 어떻게?
  7. 2018.04.17 [Elasticsearch] elasticsearch-docker 만들어 보기
  8. 2018.04.04 [Elasticsearch] Contribution 하기 위한 준비 작업
  9. 2016.02.18 [검색엔진역사] 초간단 요약.
  10. 2015.03.09 실무 예제로 배우는 Elasticsearch 검색엔진(활용편) (2)

[Elasticsearch] Adaptive Replica Selection 기능

Elastic/Elasticsearch 2018.05.09 17:06

6.x 에 추가된 API 중 맘에 드는 것이 있어서 글을 퍼왔습니다.


Reference)

https://www.elastic.co/guide/en/elasticsearch/reference/current/search.html


Adaptive Replica Selection

As an alternative to requests being sent to copies of the data in a round robin fashion, you may enable adaptive replica selection. This allows the coordinating node to send the request to the copy deemed "best" based on a number of criteria:


  • Response time of past requests between the coordinating node and the node containing the copy of the data
  • Time past search requests took to execute on the node containing the data
  • The queue size of the search threadpool on the node containing the data

This can be turned on by changing the dynamic cluster setting cluster.routing.use_adaptive_replica_selection from false to true:


PUT /_cluster/settings

{

    "transient": {

        "cluster.routing.use_adaptive_replica_selection": true

    }

}



위에 기술 되어 있는 것 처럼,

- 기본적으로는 Round Robin 방식으로 동작 합니다.

- 하지만 3가지 기준을 가지고 이 기능은 동작을 합니다.

1. Coordinating node 와 Data node 간의 응답 시간

2. Data node 에서 수행된 시간

3. Data node 의 threadpool queue 크기


이 중에서 가장 좋은 기준을 바탕으로 동작 하게 되는 것입니다.


Trackback 0 : Comment 0

[Elasticsearch] GET API 에 대해서

Elastic/Elasticsearch 2018.05.09 14:22

평소 Get API 에 대해서 설명을 할 때 document id 로 lookup 을 하기 때문에 모든 shard 로 request 를 보내지 않아 빠르다고 설명을 했습니다.

이 과정에서 놓치기 쉬운 부분이 있는데 기본적으로 Get API 는 realtime  동작을 합니다.

즉, refresh 동작과 상관 없이 기본적으로 최신 정보를 가져오게 되는 것입니다.


다시 말해, 내부적으로 refresh 동작을 수행한다는 의미가 됩니다.

그래서 Get API 에서는 realtime 이라는 변수를 통해서 realtime Get 을 사용 할 것인지 말 것인지를 정의 할 수 있습니다.


아래는 위 설명을 이해 하는데 도움을 주기 위해서 소스 코드의 일부를 발췌 하였습니다.


[GetRequest.java]

private boolean refresh = false;

boolean realtime = true;
/**
* Should a refresh be executed before this get operation causing the operation to
* return the latest value. Note, heavy get should not set this to {@code true}. Defaults
* to {@code false}.
*/
public GetRequest refresh(boolean refresh) {
this.refresh = refresh;
return this;
}

public boolean refresh() {
return this.refresh;
}

public boolean realtime() {
return this.realtime;
}

@Override
public GetRequest realtime(boolean realtime) {
this.realtime = realtime;
return this;
}


[RestGetAction.java]

@Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
final boolean includeTypeName = request.paramAsBoolean("include_type_name", true);
final String type = request.param("type");
if (includeTypeName == false && MapperService.SINGLE_MAPPING_NAME.equals(type) == false) {
throw new IllegalArgumentException("You may only use the [include_type_name=false] option with the get APIs with the " +
"[{index}/_doc/{id}] endpoint.");
}
final GetRequest getRequest = new GetRequest(request.param("index"), type, request.param("id"));
getRequest.refresh(request.paramAsBoolean("refresh", getRequest.refresh()));
getRequest.routing(request.param("routing"));
getRequest.preference(request.param("preference"));
getRequest.realtime(request.paramAsBoolean("realtime", getRequest.realtime()));
if (request.param("fields") != null) {
throw new IllegalArgumentException("the parameter [fields] is no longer supported, " +
"please use [stored_fields] to retrieve stored fields or [_source] to load the field from _source");
}
final String fieldsParam = request.param("stored_fields");
if (fieldsParam != null) {
final String[] fields = Strings.splitStringByCommaToArray(fieldsParam);
if (fields != null) {
getRequest.storedFields(fields);
}
}

getRequest.version(RestActions.parseVersion(request));
getRequest.versionType(VersionType.fromString(request.param("version_type"), getRequest.versionType()));

getRequest.fetchSourceContext(FetchSourceContext.parseFromRestRequest(request));

return channel -> client.get(getRequest, new RestToXContentListener<GetResponse>(channel) {
@Override
protected RestStatus getStatus(final GetResponse response) {
return response.isExists() ? OK : NOT_FOUND;
}
});
}


[TransportGetAction.java]

@Override
protected void asyncShardOperation(GetRequest request, ShardId shardId, ActionListener<GetResponse> listener) throws IOException {
IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex());
IndexShard indexShard = indexService.getShard(shardId.id());
if (request.realtime()) { // we are not tied to a refresh cycle here anyway
listener.onResponse(shardOperation(request, shardId));
} else {
indexShard.awaitShardSearchActive(b -> {
try {
super.asyncShardOperation(request, shardId, listener);
} catch (Exception ex) {
listener.onFailure(ex);
}
});
}
}

@Override
protected GetResponse shardOperation(GetRequest request, ShardId shardId) {
IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex());
IndexShard indexShard = indexService.getShard(shardId.id());

if (request.refresh() && !request.realtime()) {
indexShard.refresh("refresh_flag_get");
}

GetResult result = indexShard.getService().get(request.type(), request.id(), request.storedFields(),
request.realtime(), request.version(), request.versionType(), request.fetchSourceContext());
return new GetResponse(result);
}

보시면 아시겠지만 refresh 와 realtime 의 동작에는 차이가 있습니다.

이해 하기 쉽게 정리 하면,

- refresh 는 get 수행 전에 relevant shard 를 대상으로 refresh 동작을 먼저 하기 때문에 성능 저하가 있을 수 있습니다.

- realtime 은 수행 시점에 refresh 동작을 수행 하게 됩니다. (이 경우 refresh searcher 의 대상은 internal searcher 가 됩니다.)

- 여기서 translog 에서 데이터를 읽어야 하는 경우가 있는데 이 경우는 update API 에서 사용 되게 됩니다.


[InternalEngine.java]

@Override
public GetResult get(Get get, BiFunction<String, SearcherScope, Searcher> searcherFactory) throws EngineException {
assert Objects.equals(get.uid().field(), IdFieldMapper.NAME) : get.uid().field();
try (ReleasableLock ignored = readLock.acquire()) {
ensureOpen();
SearcherScope scope;
if (get.realtime()) {
VersionValue versionValue = null;
try (Releasable ignore = versionMap.acquireLock(get.uid().bytes())) {
// we need to lock here to access the version map to do this truly in RT
versionValue = getVersionFromMap(get.uid().bytes());
}
if (versionValue != null) {
if (versionValue.isDelete()) {
return GetResult.NOT_EXISTS;
}
if (get.versionType().isVersionConflictForReads(versionValue.version, get.version())) {
throw new VersionConflictEngineException(shardId, get.type(), get.id(),
get.versionType().explainConflictForReads(versionValue.version, get.version()));
}
if (get.isReadFromTranslog()) {
// this is only used for updates - API _GET calls will always read form a reader for consistency
// the update call doesn't need the consistency since it's source only + _parent but parent can go away in 7.0
if (versionValue.getLocation() != null) {
try {
Translog.Operation operation = translog.readOperation(versionValue.getLocation());
if (operation != null) {
// in the case of a already pruned translog generation we might get null here - yet very unlikely
TranslogLeafReader reader = new TranslogLeafReader((Translog.Index) operation, engineConfig
.getIndexSettings().getIndexVersionCreated());
return new GetResult(new Searcher("realtime_get", new IndexSearcher(reader)),
new VersionsAndSeqNoResolver.DocIdAndVersion(0, ((Translog.Index) operation).version(), reader, 0));
}
} catch (IOException e) {
maybeFailEngine("realtime_get", e); // lets check if the translog has failed with a tragic event
throw new EngineException(shardId, "failed to read operation from translog", e);
}
} else {
trackTranslogLocation.set(true);
}
}
refresh("realtime_get", SearcherScope.INTERNAL);
}
scope = SearcherScope.INTERNAL;
} else {
// we expose what has been externally expose in a point in time snapshot via an explicit refresh
scope = SearcherScope.EXTERNAL;
}

// no version, get the version from the index, we know that we refresh on flush
return getFromSearcher(get, searcherFactory, scope);
}
}


[코드의 이해]

RestGetAction -> TransportGetAction (TransportSingleShardAction) -> IndexShard ->

ShardGetService -> InternalEngine (Engine) -> SearcherManager(ReferenceManager)


Trackback 0 : Comment 0

[Elasticsearch] Synonym filter position 문제 개선

Elastic/Elasticsearch 2018.04.24 15:52

이미 synonym filter 테스트 관련 글을 공유 했었습니다.

- [Elasticsearch] Synonym filter 테스트


이 테스트에서 발생한 문제는 동의어에 대한 position 정보가 잘 못되는 것입니다.

테스트 환경은 기본 Elasticsearch 6.x 에서 진행 되었습니다. 


아래는 SynonymFilter 코드 내 주석 입니다.


[SynonymFilter.java]

 Matches single or multi word synonyms in a token stream.

 This token stream cannot properly handle position

 increments != 1, ie, you should place this filter before

 filtering out stop words.


그리고 아래는 동의어 처리 시 문제가 발생 하는 부분의 코드 입니다.


[SynonymMap.java]

 /** Sugar: analyzes the text with the analyzer and

 *  separates by {@link SynonymMap#WORD_SEPARATOR}.

 *  reuse and its chars must not be null. */

public CharsRef analyze(String text, CharsRefBuilder reuse) throws IOException {

  try (TokenStream ts = analyzer.tokenStream("", text)) {

    CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class);

    PositionIncrementAttribute posIncAtt = ts.addAttribute(PositionIncrementAttribute.class);

    ts.reset();

    reuse.clear();

    while (ts.incrementToken()) {

      int length = termAtt.length();

      if (length == 0) {

        throw new IllegalArgumentException("term: " + text + " analyzed to a zero-length token");

      }

      if (posIncAtt.getPositionIncrement() != 1) {

        throw new IllegalArgumentException("term: " + text + " analyzed to a token (" + termAtt +

                                           ") with position increment != 1 (got: " + posIncAtt.getPositionIncrement() + ")");

      }

      reuse.grow(reuse.length() + length + 1); /* current + word + separator */

      int end = reuse.length();

      if (reuse.length() > 0) {

        reuse.setCharAt(end++, SynonymMap.WORD_SEPARATOR);

        reuse.setLength(reuse.length() + 1);

      }

      System.arraycopy(termAtt.buffer(), 0, reuse.chars(), end, length);

      reuse.setLength(reuse.length() + length);

    }

    ts.end();

  }

  if (reuse.length() == 0) {

    throw new IllegalArgumentException("term: " + text + " was completely eliminated by analyzer");

  }

  return reuse.get();

}


기본적으로 동의어 처리에 대한 문제는 이미 lucene 레벨에서 개선이 되었습니다.

관련 참고 링크는 아래와 같습니다.


[Reference links]

https://issues.apache.org/jira/browse/LUCENE-6664

http://blog.mikemccandless.com/2012/04/lucenes-tokenstreams-are-actually.html


이와 같이 개선된 synonym filter 를 elasticsearch 에서는 아래와 같이 사용 할 수 있습니다.


[Reference links]

https://www.elastic.co/guide/en/elasticsearch/reference/master/analysis-synonym-graph-tokenfilter.html



[Create index]

PUT /syngtest

{

  "settings": {

    "index.number_of_shards": 1,

    "index.number_of_replicas": 0,

    "index": {

      "analysis": {

        "analyzer": {

          "arirang_custom": {

            "tokenizer": "arirang_tokenizer",

            "filter": [

              "lowercase",

              "trim",

              "arirang_filter",

              "custom_synonym"

            ]

          }

        },

        "filter": {

          "custom_synonym": {

            "type": "synonym_graph",

            "synonyms": [

              "henry,헨리,앙리",

              "신해철,마왕"

            ]

          }

        }

      }

    }

  }

}


[Request analyze]

GET /syngtest/_analyze

{

  "tokenizer": "arirang_tokenizer",

  "filter": [

    "lowercase",

    "trim",

    "arirang_filter",

    "custom_synonym"

  ],

  "text": "신해철은 henry"

}


[Analyzed result]

{

  "tokens": [

    {

      "token": "마왕",

      "start_offset": 0,

      "end_offset": 3,

      "type": "SYNONYM",

      "position": 0

    },

    {

      "token": "신해철",

      "start_offset": 0,

      "end_offset": 3,

      "type": "korean",

      "position": 0

    },

    {

      "token": "헨리",

      "start_offset": 5,

      "end_offset": 10,

      "type": "SYNONYM",

      "position": 1

    },

    {

      "token": "앙리",

      "start_offset": 5,

      "end_offset": 10,

      "type": "SYNONYM",

      "position": 1

    },

    {

      "token": "henry",

      "start_offset": 5,

      "end_offset": 10,

      "type": "word",

      "position": 1

    }

  ]

}


특별히 코드를 수정 하거나 하지 않고 문제가 해결 된 것을 확인 하실 수 있습니다.

왜 해결 되었는지는 위 synonym graph filter 에 대해서 문서를 보시면 되겠습니다.


Trackback 0 : Comment 0

[Logstash] Logstash 를 이용한 CSV 파일 Import를 하려면

Elastic/Logstash 2018.04.24 11:14

Elastic 사의 공식 문서를 보시면 쉽게 하실 수 있습니다.


기본 flow 는 아래와 같습니다.


CSV -> logstash input file -> Logstash filter csv -> logstash output elasticsearch


각각에 필요한 참조문서는

[Logstash Input File]

https://www.elastic.co/guide/en/logstash/current/plugins-inputs-file.html


[Logstash Filter CSV]

https://www.elastic.co/guide/en/logstash/current/plugins-filters-csv.html


[Logstash Output Elasticsearch]

https://www.elastic.co/guide/en/logstash/current/plugins-outputs-elasticsearch.html


[Elasticsearch Indices Templates]

https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-templates.html


Template 이 필요한 이유는 csv 파일 데이터에 대한 dynamic mapping 시 의도치 않은 데이터에 대한 형변환 오류를 방지 하기 위함 입니다.

사전에 꼭 정의 하셔서 reindexing 하는 일이 없도록 주의 하시면 좋을 것 같습니다.


Trackback 0 : Comment 0

[Elasticsearch] Synonym filter 테스트

Elastic/Elasticsearch 2018.04.19 16:19

커뮤니티에 질문 주신 내용이 있어서 바쁘지만 테스트 결과 공유 드립니다.

커뮤니티에 질문 올려 주신 분이 계셔서 직접 테스트 진행 했습니다.

제가 position 관련 에러 수정을 위한 테스트 시간이 없어서 인명 사전 등록 방법으로 처리 했는데요.

수정이 필요 하시면 KoreanFilter 코드를 수정 하시면 됩니다.


설치 및 테스트 Elasticsearch Version)

6.0.0


Arirang plugin 설치)

$ bin/elasticsearch-plugin install https://github.com/HowookJeong/elasticsearch-analysis-arirang/releases/download/6.0.0/elasticsearch-analysis-arirang-6.0.0.zip


Index 삭제)

DELETE syntest


{

  "error": {

    "root_cause": [

      {

        "type": "index_not_found_exception",

        "reason": "no such index",

        "resource.type": "index_or_alias",

        "resource.id": "syntest",

        "index_uuid": "_na_",

        "index": "syntest"

      }

    ],

    "type": "index_not_found_exception",

    "reason": "no such index",

    "resource.type": "index_or_alias",

    "resource.id": "syntest",

    "index_uuid": "_na_",

    "index": "syntest"

  },

  "status": 404

}


Index 생성)

PUT /syntest

{

  "settings": {

    "index.number_of_shards": 1,

    "index.number_of_replicas": 0,

    "index": {

      "analysis": {

        "analyzer": {

          "arirang_custom": {

            "tokenizer": "arirang_tokenizer",

            "filter": [

              "lowercase",

              "trim",

              "custom_synonym",

              "arirang_filter"

            ]

          }

        },

        "filter": {

          "custom_synonym": {

            "type": "synonym",

            "synonyms": [

              "henry,헨리,앙리",

              "신해철,마왕"

            ]

          }

        }

      }

    }

  }

}


{

  "acknowledged": true,

  "shards_acknowledged": true,

  "index": "syntest"

}


Analyze 실행)

GET /syntest/_analyze

{

  "tokenizer": "arirang_tokenizer",

  "filter": [

    "lowercase",

    "trim",

    "custom_synonym",

    "arirang_filter"

  ],

  "text": "신해철"

}


{

  "tokens": [

    {

      "token": "신해철",

      "start_offset": 0,

      "end_offset": 3,

      "type": "korean",

      "position": 0

    },

    {

      "token": "신해",

      "start_offset": 0,

      "end_offset": 2,

      "type": "korean",

      "position": 0

    },

    {

      "token": "해철",

      "start_offset": 1,

      "end_offset": 3,

      "type": "korean",

      "position": 1

    },

    {

      "token": "마왕",

      "start_offset": 0,

      "end_offset": 3,

      "type": "SYNONYM",

      "position": 1

    }

  ]

}


동의어 처리가 되지 않은 이유)

GET /syntest/_analyze

{

  "tokenizer": "arirang_tokenizer",

  "text": "신해철은 henry"

}


{

  "tokens": [

    {

      "token": "신해철은",

      "start_offset": 0,

      "end_offset": 4,

      "type": "korean",

      "position": 0

    },

    {

      "token": "henry",

      "start_offset": 5,

      "end_offset": 10,

      "type": "word",

      "position": 1

    }

  ]

}


tokenizer 에서 추출한 토큰은 위와 같이 두개 입니다.

아래는 filter 를 적용한 내용입니다.


GET /syntest/_analyze

{

  "tokenizer": "arirang_tokenizer",

  "filter": [

    "lowercase",

    "trim",

    "custom_synonym",

    "arirang_filter"

  ],

  "text": "신해철은 henry"

}


{

  "tokens": [

    {

      "token": "신해철",

      "start_offset": 0,

      "end_offset": 3,

      "type": "korean",

      "position": 0

    },

    {

      "token": "신해철은",

      "start_offset": 0,

      "end_offset": 4,

      "type": "korean",

      "position": 0

    },

    {

      "token": "신해",

      "start_offset": 0,

      "end_offset": 2,

      "type": "korean",

      "position": 0

    },

    {

      "token": "해철",

      "start_offset": 1,

      "end_offset": 3,

      "type": "korean",

      "position": 1

    },

    {

      "token": "철은",

      "start_offset": 2,

      "end_offset": 4,

      "type": "korean",

      "position": 2

    },

    {

      "token": "henry",

      "start_offset": 5,

      "end_offset": 10,

      "type": "word",

      "position": 3

    },

    {

      "token": "헨리",

      "start_offset": 5,

      "end_offset": 10,

      "type": "SYNONYM",

      "position": 3

    },

    {

      "token": "앙리",

      "start_offset": 5,

      "end_offset": 10,

      "type": "SYNONYM",

      "position": 3

    }

  ]

}


위에 추출된 term 목록을 보면 "마왕" 이라는 동의어가 추가 되지 않은것을 볼 수 있습니다.

이것은 두 가지 방법으로 해결이 가능 합니다.

사전에 "신해철" 이라는 인명 사전 정보를 등록 하시면 됩니다.

기본적으로 tokenzier 과정 후 filter 처리가 되면서 사전에 등록된 정보로 term 구성이 되기 때문에 사전에 누락 된 경우는 일반적으로 KoreanFilter 에 의해서 bigram 처리가 됩니다.

다른 한 가지는 position 정보가 구성 시 오류가 나지 않도록 코드를 수정 하는 것입니다.

KoreanFilter 코드를 참고 하셔서 테스트 및 수정 하시면 됩니다.


아래는 사전에 "신해철" 추가 후 실행한 방법 입니다.


GET /syntest/_analyze

{

  "tokenizer": "arirang_tokenizer",

  "filter": [

    "lowercase",

    "trim",

    "arirang_filter",

    "custom_synonym"

  ],

  "text": "신해철은 henry"

}


{

  "tokens": [

    {

      "token": "신해철",

      "start_offset": 0,

      "end_offset": 3,

      "type": "korean",

      "position": 0

    },

    {

      "token": "마왕",

      "start_offset": 0,

      "end_offset": 3,

      "type": "SYNONYM",

      "position": 0

    },

    {

      "token": "henry",

      "start_offset": 5,

      "end_offset": 10,

      "type": "word",

      "position": 1

    },

    {

      "token": "헨리",

      "start_offset": 5,

      "end_offset": 10,

      "type": "SYNONYM",

      "position": 1

    },

    {

      "token": "앙리",

      "start_offset": 5,

      "end_offset": 10,

      "type": "SYNONYM",

      "position": 1

    }

  ]

}


Trackback 0 : Comment 0

[Elasticsearch] elasticsearch-docker plugin 설치는 어떻게?

Elastic/Elasticsearch 2018.04.18 15:44

Elasticsearch docker 만들어 보기 참고하세요.


초간단 예제를 보여 드리겠습니다.

Dockerfile 에 추가해 주시면 됩니다.


# plugin 을 설치 합니다.

RUN bin/elasticsearch-plugin install --batch analysis-icu


$ docker run elasticsearch-6.2.3-ubuntu-14.04-jdk8u152 ls -al plugins/

total 12

drwxr-xr-x 1 elasticsearch elasticsearch 4096 Apr 18 06:38 .

drwxr-xr-x 1 elasticsearch elasticsearch 4096 Apr 18 06:37 ..

drwxr-xr-x 2 elasticsearch elasticsearch 4096 Apr 18 06:38 analysis-icu


참 쉽습니다.

Trackback 0 : Comment 0

[Elasticsearch] elasticsearch-docker 만들어 보기

Elastic/Elasticsearch 2018.04.17 18:14

이미 Elastic 사에서 docker 를 제공하고 있습니다.

정보는 아래 링크를 참고하세요.


https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html

https://github.com/elastic/elasticsearch-docker

https://docs.docker.com/get-started/


Docker 를 이용해서 이미지를 만들어 보고 실행 및 클러스터 구성을 어떻게 하는지 단계 별 실행 코드를 따라 하면서 배워 보도록 하겠습니다.

그냥 따라만 하시면 될 겁니다. (Maybe)


제 개발 환경은 mac 입니다.

- docker 당연히 설치가 되어 있어야 합니다.

그 이외는 음... 그냥 개발자시면 쉽게 하실 수 있을 실거예요. ^^


Step 1) 이미지를 만들기 위한 repo 를 하나 만듭니다.

$ mkdir elasticsearch-docker

$ cd elasticsearch-docker


Step 2) 이미지에 패키징 할 패키지들을 준비해 둡니다.

$ wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.2.3.tar.gz

$ download jdk-8u152-linux-x64.tar.gz


Step 3) 혹시 커스텀 설정을 하실 거라면 만들어 보세요.

$ mkdir config

$ touch elasticsearch.yml

$ touch jvm.options

$ touch log4j2.properties


# elasticsearch default config 파일로 구성을 합니다.

# 또는 변경이 필요할 경우 작성을 합니다.


Step 4) 이미지 생성을 위한 도커 파일을 만듭니다.

$ vi Dockerfile

From ubuntu:14.04

MAINTAINER HENRY JEONG sophistlv@gmail.com


ARG arg_user_home

ARG arg_es_version


ENV PATH $arg_user_home/apps/elasticsearch/bin:$arg_user_home/apps/jdk/bin:$PATH

ENV ES_HOME $arg_user_home/apps/elasticsearch

ENV JAVA_HOME $arg_user_home/apps/jdk


RUN groupadd -g 1000 elasticsearch && useradd elasticsearch -u 1000 -g 1000 -m -b $arg_user_home -d $arg_user_home -s /bin/bash

USER 1000


# 기본 디렉토리를 생성 합니다.

WORKDIR $arg_user_home

RUN mkdir apps


# 현재 작업 디렉토리를 선언 합니다.

WORKDIR $arg_user_home/apps


# 아래와 같이 다운로드를 받거나 이미 받아 놓은 파일을 추가 합니다.

#RUN wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-$arg_es_version.tar.gz

COPY elasticsearch-$arg_es_version.tar.gz .

COPY jdk-8u152-linux-x64.tar.gz .


# 압축을 해제 합니다.(자동으로 해제 됩니다.)

RUN tar -xvzf elasticsearch-$arg_es_version.tar.gz

RUN tar -xvzf jdk-8u152-linux-x64.tar.gz

RUN rm -f elasticsearch-$arg_es_version.tar.gz

RUN rm -f jdk-8u152-linux-x64.tar.gz


# 설정 파일을 추가 합니다.

COPY config/elasticsearch.yml elasticsearch-$arg_es_version/config/

COPY config/jvm.options elasticsearch-$arg_es_version/config/

COPY config/log4j2.properties elasticsearch-$arg_es_version/config/


# symlink 를 설정 합니다.

RUN ln -s elasticsearch-$arg_es_version elasticsearch

RUN ln -s jdk1.8.0_152 jdk


# WORKDIR 을 변경 합니다.

WORKDIR elasticsearch-$arg_es_version

RUN set -ex && for path in data logs config/scripts; do \

        mkdir -p "$path"; \

        chown -R elasticsearch:elasticsearch "$path"; \

    done


# 데몬을 실행 합니다.

#CMD bin/elasticsearch


EXPOSE 9200 9201 9300 9301


Step 5) 이미지를 생성 합니다.

$ docker build -t elasticsearch-6.2.3-ubuntu-14.04-jdk8u152 . --build-arg arg_user_home=/home/es --build-arg arg_es_version=6.2.3

Sending build context to Docker daemon  218.9MB

Step 1/26 : From ubuntu:14.04

---> 67759a80360c

Step 2/26 : MAINTAINER HENRY JEONG sophistlv@gmail.com

---> Using cache

---> be0de8404ce2

Step 3/26 : ARG arg_user_home

---> Using cache

---> 9cad1b4a18c5

Step 4/26 : ARG arg_es_version

---> Using cache

---> 693e1b4ae411

Step 5/26 : ENV PATH $arg_user_home/apps/elasticsearch/bin:$arg_user_home/apps/jdk/bin:$PATH

---> Using cache

---> 5a249008ab07

Step 6/26 : ENV ES_HOME $arg_user_home/apps/elasticsearch

---> Using cache

---> 095bc65a6dfb

Step 7/26 : ENV JAVA_HOME $arg_user_home/apps/jdk

---> Using cache

---> cb02bd60ea16

Step 8/26 : RUN groupadd -g 1000 elasticsearch && useradd elasticsearch -u 1000 -g 1000 -m -b $arg_user_home -d $arg_user_home -s /bin/bash

---> Running in 64ebd0c6b8cf

Removing intermediate container 64ebd0c6b8cf

---> 49759958cddd

Step 9/26 : USER 1000

---> Running in 849c7bde5f44

Removing intermediate container 849c7bde5f44

---> e0f04944d08c

Step 10/26 : WORKDIR $arg_user_home

Removing intermediate container a7e04ccfe107

---> c5b262000886

Step 11/26 : RUN mkdir apps

---> Running in 0272f8c59b01

Removing intermediate container 0272f8c59b01

---> 5e860b3ae335

Step 12/26 : WORKDIR $arg_user_home/apps

Removing intermediate container 093bbfc52201

---> 541e34eb3f99

Step 13/26 : COPY elasticsearch-$arg_es_version.tar.gz .

---> b7f35e466950

Step 14/26 : COPY jdk-8u152-linux-x64.tar.gz .

---> d47f3d93cfd6

Step 15/26 : RUN tar -xvzf elasticsearch-$arg_es_version.tar.gz

---> Running in ec1b02ec10b4

...중략...

Removing intermediate container ec1b02ec10b4

---> 79d8bec8ee89

Step 16/26 : RUN tar -xvzf jdk-8u152-linux-x64.tar.gz

---> Running in c8ae086e2868

Removing intermediate container b94059a73a2c

---> 6fa8928d2367

...중략...

Step 17/26 : RUN rm -f elasticsearch-$arg_es_version.tar.gz

---> Running in beec4cdec4eb

Removing intermediate container beec4cdec4eb

---> 0c30468566ea

Step 18/26 : RUN rm -f jdk-8u152-linux-x64.tar.gz

---> Running in 998cd2eb3d33

Removing intermediate container 998cd2eb3d33

---> 8e1d588a3bbc

Step 19/26 : COPY config/elasticsearch.yml elasticsearch-$arg_es_version/config/

---> b2faa21e1692

Step 20/26 : COPY config/jvm.options elasticsearch-$arg_es_version/config/

---> 36c3ce020763

Step 21/26 : COPY config/log4j2.properties elasticsearch-$arg_es_version/config/

---> 48314fe634e1

Step 22/26 : RUN ln -s elasticsearch-$arg_es_version elasticsearch

---> Running in 2f7610ab8ce4

Removing intermediate container 2f7610ab8ce4

---> 539eb5fce438

Step 23/26 : RUN ln -s jdk1.8.0_152 jdk

---> Running in 1f4585206800

Removing intermediate container 1f4585206800

---> f61c467a833b

Step 24/26 : WORKDIR elasticsearch-$arg_es_version

Removing intermediate container 786de237b577

---> a92587ddf7bf

Step 25/26 : RUN set -ex && for path in data logs config/scripts; do         mkdir -p "$path";         chown -R elasticsearch:elasticsearch "$path";     done

---> Running in dfe771b1d02d

+ mkdir -p data

+ chown -R elasticsearch:elasticsearch data

+ mkdir -p logs

+ chown -R elasticsearch:elasticsearch logs

+ mkdir -p config/scripts

+ chown -R elasticsearch:elasticsearch config/scripts

Removing intermediate container dfe771b1d02d

---> 90193d0f5dcc

Step 26/26 : EXPOSE 9200 9201 9300 9301

---> Running in b14404086bc4

Removing intermediate container b14404086bc4

---> a2c69d28a015

Successfully built a2c69d28a015

Successfully tagged elasticsearch-6.2.3-ubuntu-14.04-jdk8u152:latest


Step 6) Elasticsearch 가 사용할 네트워크를 생성 합니다.

$ docker network create es-net

$ docker network ls

NETWORK ID          NAME                DRIVER              SCOPE

ed35ea6eb949        bridge              bridge              local

a6c8b4fd7996        es-net              bridge              local

f3ab4273e30a        host                host                local

9d6aa0be922f        none                null                local


Step 7) 만들어진 이미지가 잘 되었는지 한번 명령어를 실행 시켜 봅니다.

$ docker run elasticsearch-6.2.3-ubuntu-14.04-jdk8u152 ls -al

total 248

drwxr-xr-x  1 elasticsearch elasticsearch   4096 Apr 17 07:45 .

drwxr-xr-x  1 elasticsearch elasticsearch   4096 Apr 17 07:45 ..

-rw-r--r--  1 elasticsearch elasticsearch  11358 Mar 13 10:02 LICENSE.txt

-rw-r--r--  1 elasticsearch elasticsearch 191887 Mar 13 10:07 NOTICE.txt

-rw-r--r--  1 elasticsearch elasticsearch   9268 Mar 13 10:02 README.textile

drwxr-xr-x  2 elasticsearch elasticsearch   4096 Apr 17 07:44 bin

drwxr-xr-x  1 elasticsearch elasticsearch   4096 Apr 17 07:45 config

drwxr-xr-x  2 elasticsearch elasticsearch   4096 Apr 17 07:45 data

drwxr-xr-x  2 elasticsearch elasticsearch   4096 Mar 13 10:08 lib

drwxr-xr-x  1 elasticsearch elasticsearch   4096 Mar 13 10:08 logs

drwxr-xr-x 16 elasticsearch elasticsearch   4096 Mar 13 10:08 modules

drwxr-xr-x  2 elasticsearch elasticsearch   4096 Mar 13 10:08 plugins


Step 8) 마지막으로 클러스터 구성을 테스트 합니다.

# -d 를 추가 하면 daemonize 로 동작


$ docker run -p 9200:9200 -p 9300:9300 elasticsearch-6.2.3-ubuntu-14.04-jdk8u152 bin/elasticsearch

# Single node 실행


$ docker run --memory=2g -e ES_JAVA_OPTS="-Xms512m -Xmx512m" -p 9200:9200 -p 9300:9300 -u 1000 elasticsearch-6.2.3-ubuntu-14.04-jdk8u152:latest \

bin/elasticsearch \

-Ecluster.name=cluster-docker \

-Enode.name=node-docker1 \

-Enode.master=true \

-Enode.data=true \

-Ediscovery.zen.minimum_master_nodes=1 \

-Enetwork.host=0.0.0.0 \

-Ediscovery.zen.ping.unicast.hosts=192.168.221.237:9301 \

-Ehttp.port=9200 \

-Etransport.tcp.port=9300


$ docker run --memory=2g -e ES_JAVA_OPTS="-Xms512m -Xmx512m" -p 9201:9200 -p 9301:9300 -u 1000 elasticsearch-6.2.3-ubuntu-14.04-jdk8u152:latest \

bin/elasticsearch \

-Ecluster.name=cluster-docker \

-Enode.name=node-docker2 \

-Enode.master=false \

-Enode.data=true \

-Ediscovery.zen.minimum_master_nodes=1 \

-Enetwork.host=0.0.0.0 \

-Ediscovery.zen.ping.unicast.hosts=192.168.221.237:9300 \

-Ehttp.port=9200 \

-Etransport.tcp.port=9300


별로 어려운것 없이 쉽게 할 수 있습니다.

여기서 개선 및 업그레이드는 각자 편하게 하시면 될 것 같습니다.

Trackback 0 : Comment 0

[Elasticsearch] Contribution 하기 위한 준비 작업

Elastic/Elasticsearch 2018.04.04 12:31

Elasticsearch 소스코드를 수정 하거나 디버깅을 하고 싶을 때가 있습니다.

로컬에서 빌드 부터 해야 가능하겠죠.


특별히 contributing 을 목적으로 하지는 않지만, 그래도 이왕이면 버그 수정도 하고 contribution 도 하면 좋겠죠.


아래는 이미 문서에 자세히 나와 있는 내용을 그냥 요약 정도 해본 내용입니다.

(기억을 위해 한번 더 작성해 본 내용입니다.)


Reference)

https://github.com/elastic/elasticsearch/blob/master/CONTRIBUTING.md

https://github.com/elastic/elasticsearch/blob/master/TESTING.asciidoc


사전 준비 도구)

- JDK 10 다운로드 및 설치 (Build 용)

- JDK 8 다운로드 및 설치 (Runtime 용)

- Gradle 4.3 다운로드 및 설치


JDK 10 설치 후 환경변수 설정)

$ vi .bash_profile

export JAVA_HOME=$(/usr/libexec/java_home)

export RUNTIME_JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home


Intellij 환경설정)

This can be achieved by adding the -Didea.no.launcher=true JVM option. 

IntelliJ, go to Run->Edit Configurations...->Defaults->JUnit->VM options and input -ea.

$ ./gradlew idea


Formatting 설정)

- Java indent is 4 spaces

- Line width is 140 characters

- IntelliJ: Preferences/Settings->Editor->Code Style->Java->Imports

- Class count to use import with '*' and Names count to use static import with '*'. Set their values to 99999


Elasticsearch run)

$ ./gradlew run


# 실행 시 9200과 9300 port 로 실행 중인 elasticsearch daemon 이 있으면 에러 발생 합니다.


Logging level 설정)

# path : distribution/src/config/log4j2.properties


$ vi log4j2.properties

ASIS)

rootLogger.level = info


TOBE)

rootLogger.level = debug


Create distribution)

$ ./gradlew assemble

$ ./gradlew check


Trackback 0 : Comment 0

[검색엔진역사] 초간단 요약.

ITWeb/검색일반 2016.02.18 10:20

검색엔진이 언제 부터 시작했는지 그냥 기록해 봤습니다.


1989년 팀버너리에 의해서 w3 프로젝트가 시작 되었습니다. (HTTP, HTML 정의)

1990년 FTP 서버 내 파일 검색을 위한 Archi 라는 검색엔진이 검색엔진의 시초 입니다.

1991년 팀버너리가 유즈넷에 자신의 프로젝트를 공개 하면서 활성화되기 시작했습니다.

1993년 Gopher서버에서 사용하는 베로니카라는 검색엔진이 나왔습니다.

1994년 web crawler 라는 서비스가 나왔으며 이후 우리가 아는 검색 엔진과 서비스들이 하나 둘 나오기 시작했습니다.


AOL, Lycos, Yahoo, Altavista, ......

Trackback 0 : Comment 0

실무 예제로 배우는 Elasticsearch 검색엔진(활용편)

Elastic/Elasticsearch 2015.03.09 18:43

4월에 종이책이 나옵니다.

그리고 책에 대한 부족한 부분이나 틀린 내용들에 대해서는 지속적으로 업데이트 하도록 하겠습니다.

블로그와 페북 커뮤니티를 통해서 제가 도움 드릴 수 있는 부분들은 열심히 지원도 하겠습니다.


감사합니다.


책소개

eBook & DRM-free

쉽고 빠르게 배울 수 있는 Elasticsearch 검색엔진 활용서


전작인 『실무 예제로 배우는 Elasticsearch 검색엔진 <기본편>』에서는 Elasticsearch의 기본 개념과 설치 방법, 검색서비스 구성을 다뤘고, 이번 <활용편>에서는 <기본편>에서 다루지 못한 확장 기능과 다양한 서비스의 활용 방법, Elasticsearch의 성능 최적화 방법을 소개한다.


검색엔진을 이용한 다양한 기술과의 접목과 활용, 사용자 정의 기능을 구현해서 적용할 수 있는 플러그인 구현 방법까지 Elasticsearch를 적극적으로 활용할 수 있는 방법을 보여주며, 기본적인 성능 최적화 방법과 가이드를 제공하여 대용량 트래픽의 처리와 안정성을 확보하는 데 도움을 줄 수 있도록 구성되어 있다.


이 책은 설치와 구성 등 기본적인 내용은 다루지 않으므로 Elasticsearch의 기본 내용을 알고 싶다면 『실무 예제로 배우는 Elasticsearch 검색엔진(기본편)』(한빛미디어, 2014)이 더 적합하다. 


대상 독자

  • 검색 서비스 개발에 관심 있는 기획자 또는 개발자
  • 상용 검색엔진을 오픈 소스 검색엔진으로 대체하길 원하는 서비스 관리자 또는 개발자
  • Elasticsearch의 고급 기능과 성능 최적화 등 활용 방법을 자세히 알고 싶은 개발자
맨위로

저자소개

[지은이] 정호욱

지난 13년 동안 야후코리아, NHN Technology, 삼성전자에서 커뮤니티, 소셜 검색, 광고 검색 관련 서비스를 개발해 오면서 검색엔진을 활용한 다양한 프로젝트를 수행하였다. 현재 빅 데이터 전문 기업인 그루터에서 오픈 소스 기반 검색엔진 개발자로 근무하고 있다. elasticsearch 기술에 대한 정보와 경험을 현재 개인 블로그(http://jjeong.tistory.com)를 통해 공유하고 있다.



맨위로

목차

chapter 1 검색 기능 확장 
    1.1 자동 완성 
    1.2 Percolator 
    1.3 Join 
    1.4 River 
    1.5 정리 


chapter 2 검색 데이터 분석
    2.1 Bucket Aggregation
    2.2 Metric Aggregation 
    2.3 정리 


chapter 3 Plugin
    3.1 Plugin 제작 
    3.2 REST Plugin 만들기 
    3.3 Analyzer Plugin 만들기 
    3.4 정리 


chapter 4 Hadoop 연동 
    4.1 MapReduce 연동 
    4.2 Hive 연동 
    4.3 정리 


chapter 5 ELK 연동 
    5.1 Logstash 
    5.2 Elasticsearch 
    5.3 Kibana 
    5.4 정리 


chapter 6 SQL 활용하기
    6.1 RDB 관점의 Elasticsearch 
    6.2 SQL 정의하기 
    6.3 SQL 변환하기 
    6.4 JDBC Driver 만들기 
    6.5 정리 


chapter 7 Elasticsearch 성능 최적화 
    7.1 하드웨어 관점 
    7.2 Document 관점 
    7.3 Operation 관점 
    7.4 정리 


Trackback 0 : Comments 2