es에서 노드들을 어떻게 발견할까?
discovery.seed_hosts에 array값으로 initial nodes list를 주기는 하지만
그 이후에 노드가 추가, 삭제되는 경우는 어떻게될까?

# Discovery :  discovery.seed_hosts
discovery는 cluster formation module이 클러스터를 형성하는 다른 노드들을 찾는 과정을 의미한다.
(cluster formation module이 있고 역할에 따라 나뉘는데 그 중에 하나가 discovery임.)
이 과정은 es노드를 시작할때 또는 노드에서 마스터 노드가 실패했다고 판단될때 실행되는데,
마스터 노드가 발견되거나 새로 선출될때까지 계속해서 실행된다.

이 과정은 seed hosts 리스트에 의해 시작되고,
프로세스는 두 단계로 동작하는데
* 첫째 각 노드는 연결된 노드를 식별하고, 마스터 자격이 있는지 확인하여 seed 주소를 검사함
* 둘째 성공하면 원격 노드와 알려진 master-eligible 노드 목록을 모두 공유하고 원격 노드는 피어와 차례로 응답.
그런 다음 노드는 방금 발견한 새 노드들을 검사하고, 피어를 요청하는 등의 작업을 수행.
노드가 마스터 자격이 없는 경우 선택된 마스터 노드를 감지할때까지 이 감지 프로세스가 계속됨.

결론은 새로운 노드를 추가할때에 master-eligible nodes를 포함하는 클러스터의 모든 리스트를 
discover.seed_hosts에 기입해주고, 띄우면 master-eligible nodes에서 discovery하는 듯.
원래 있던 노드의 discovery.seed_hosts까지 수정해서 재시작해줄필요 없음.

# cluster.initial_master_nodes
최초 bootstrap당시의 master-eligible-nodes 목록임.
새 노드를 기존 클러스터에 추가할때는 master-eligible nodes를 포함하여 모두 discovery.seed_hosts에만 넣어주면되고,
initial_master_nodes 셋팅 해 줄 필요없음.
대신, inital_master_nodes에 설정된 master-eligible-nodes들 중에서 과반수 이상이 살아있어야 클러스터가 정상적으로 실행됨.

* discovery에 대하여.
https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-discovery-hosts-providers.html

 

Discovery | Elasticsearch Reference [7.6] | Elastic

Discovery is the process by which the cluster formation module finds other nodes with which to form a cluster. This process runs when you start an Elasticsearch node or when a node believes the master node failed and continues until the master node is foun

www.elastic.co

* cluster.initial_master_nodes에 대하여. discovery에 사용되는 여러 셋팅값들.
https://www.elastic.co/guide/en/elasticsearch/reference/current/discovery-settings.html#unicast.hosts

 

Important discovery and cluster formation settings | Elasticsearch Reference [7.6] | Elastic

Important discovery and cluster formation settingsedit There are two important discovery and cluster formation settings that should be configured before going to production so that nodes in the cluster can discover each other and elect a master node. disco

www.elastic.co

 

$ yarn add @elastic/elasticsearch

github가면 예제랑 사용방법이 다 나와있음

## hot thread api
* cpu사용일이 높을때 hot threads api를 통해 특정 프로세스가 블락되어 문제를 일으키고 있는지 확인 할 수 있음.
* _nodes/hot_threads
* response 내용
   * 첫줄 : 노드의 신원 정보(스레드 정보가 어느 cpu에 속하는지에 관한 기초적 정보)
   * 둘째줄 : 'search'스레드에 xx%사용중이라고 나옴. search, merge, index와같은 행동이 기술됨.
   * 마지막줄 : es가 같은 스택트레이스를 가지고 이는 스레드가 최근 몇밀리초내에 뜬 10개 스냅샷 중 10개에 있다는 것을 의미.
   * 블락된 스레드 확인 : 블락 사용율, waiting상태에 있는 스레드들에 대한 대기 사용율.


## thread pool
* cpu와 memory의 효율적 사용을 위해 각 노드는 스레드풀을 관리하고있음.
* 요청종류에 따라 bulk/index/search thread pool이 있음.
threadpool.bulk.type:fixed
threadpool.bulk.size
threadpool.bulk.queue_size: 스레드수를 제어 (기본으로 cpu core * 5)


## 메모리
* heap크기, 필드와 필터 캐시.
* 집계와 필터에 메모리 사용.
* 필드데이터를 메모리에 올림으로써 필드데이터 접근 효율을 높임.
* 쿼리에 해당하는 문서만 올리는게 아니라 색인에 있는 모든 문서에 대한 값들을 로딩해서 쿼리가 엄청 빨라지는 것임.
* jvm ram의 50프로정도는 남겨
   * 루씬이 빈번하게 사용하는 파일 시스템 캐시를 위한 메모리 부족을 막기위해.
* 운영체제에 메모리가 부족하면 메모리페이지를 디스크로 내리는 스와핑이 발생할 수 있음. 급격한 성능저하로 이어지기때문에 스와핑은 끄도록.


## 필터와 필드 캐시
* 필터 캐시 : 필터와 쿼리 작업의 결과를 메모리내에 저장, 필터가 적용된 최초 쿼리 결과를 필터 캐시에 저장, 색인 수준으로는 사용 권장하지 않고. 노드수준의 사용을 권장 (LRU)
* 필트데이터 캐시 제한은 없음. 필드 데이터 서킷 프레이커 값에 도달까지 커질 수 있음.
* 케시 제거되는 것도 주의가 필요함. 비용이 큰 작업이고, 필드데이터가 너무 작게 설정되어있는건 아닌지 확인해볼필요가 있음.


## 운영체제 캐시
* 루씬 세그먼트는 불변 파일이고, os의 파일시스템 캐시를 적극 활용함.
  * 불변 파일이라느 것은 루씬에 의해 한번만 쓰이고, 여러번 읽힌다는 것을 의미.
* 루씬의 불변파일은 캐시 친화적. 기반이 되는 os에서 핫한 세그먼트에 빠른 접근이 가능하게 메모리에 상주시키도록 설계됨
node.tag정보를 가지고 라우팅하여 특정색인을 성능좋은 장비로 위치시킬수있음.


## 플러그인
* 사이트 플러그인, 코드 플러그인이 있음.
* 사이트 플러그인은 추가적인 기능제공은 아니고, 단순히 es가 서비스하는 웹페이지를 제공하는 것. ex) head plugin
* 코드 플러그인은 es가 실행하는 jvm코드를 포함하는 플러그인으로. jar파일. 
   * es가 인터페이스 제공을 위해 서비스 할 수 있는 기본 html, 이미지 그리고 자바스크립트 파일 포함 가능함.
   * ex) marvel plugin

엘라스틱서치에서 샤드는 refresh, flush, optimize API 과정을 통해 관리된다.
엘라스틱서치의 위의 내용은 모두 루씬 내용인데 서로 용어가 다르기 때문에 잘 정리해 둘 필요가 있다.

루씬 엘라스틱서치
flush refresh
commit flush
merge optimize API

루씬에서는 데이터가 in-memory buffer 기반으로 처리된다.
데이터변경사항이 들어오면 segment를 생성하고, 시스템 캐시에 캐시된 후에, 디스크 동기화가 이루어짐.

1. 루씬에서의 flush = 엘라스틱서치의 refresh.
- segment 생성시 커널 시스템 캐시에 세그먼트가 캐시되어 읽기가 가능해진다.
- 루씬의 ReOpen() 함수를 이용해 IndexSearcher에서 읽을 수 있는 상태.
- 일정주기마다 업데이트 된 문서가 ReOpen() 함수로 처리.

- es 클러스터에 존재하는 모든 샤드에서는 기본적으로 1초마다 한번씩 refresh작업이 수행된다.
- 인덱스를 새로고침한다는 의미인데, refresh가되면 새로 추가한 데이터의 검색이 가능해진다.
- 대량 인덱스 시에는 -1로 비활성화해두면 인덱싱할때 이점이 있다.

2. 루씬에서의 commit = 엘라스틱서치의 flush.
- 물리적으로 디스크 기록을 수행하는 fsync() 함수 호출 작업이다.
- flush가 있기 때문에 매번 commit 필요가 없고, 일정 주기로 commit이 수행된다.
- 루씬에서의 flush작업은 디스크로 쓰기가 이루어지기 전이기 때문에, flush작업까지만 되고 시스템에 문제가 발생하면 데이터 유실 발생 가능성이 있다.

- es에서의 flush는 루씬의 commit 작업과 함께 새로운 translog를 시작한다.
- * translog는 루씬에는 없는 내용으로 샤드의 장애복구를 위해 재공 되는 특수한 파일이다.
- * 샤드는 자신에게 일어나는 모든 변경사항을 translog에 먼저 기록하고, 내부 루씬을 호출한다.
- * 시간이 지나면 translog 파일 크기도 증가한다. 루씬에서 commit이 이루어지면 translog에서 commit 지점까지의 내용이 삭제된다.
- * 데이터가 커널 시스템 캐시에 있다 디스크에 동기화 되지 못하고 유실될 가능성을 대비하여 transhlog를 만든 것이다.
- es에서 flush 작업은 default로 5초에 한번씩 수행되고, api를 통해 flush 주기 조절이 가능하나 추천하지 않는다.

3. 루씬의 merge = 엘라스틱서치의 optimize API
- 검색 성능을 높이기 위해 검색 대상이 되는 세그먼트를 병합하여 세그먼트 수를 줄이는 작업이다.

- 검색 대상이 되는 세그먼트 수를 줄이면, 검색 횟수를 줄일 수 있고, 검색 성능이 올라간다.
- commit작업을 동반하기 때문에 비용이 크다. 

- es에서는 forced merge API를 통해 루씬 merge 작업을 강제 수행할 수 있다.
- 파편화된 다수 세그먼트들을 병합한다.
- 강제 수행하지 않더라도, 백그라운드로 주기적으로 수행된다.

elasticsearch에서는 nested type을 사용할 수 있다.
문서안에 object array를 저장할 수 있고, 그것이 nested type이다.

es가이드에 나온 예제 처럼 user object를 array형태로 넣을 수 있다.

{
  "group" : "fans",
  "user" : [
    {
      "first" : "John",
      "last" :  "Smith",
      "score" : 90
    },
    {
      "first" : "Alice",
      "last" :  "White",
      "scroe" : 100
    }
  ]
}


내가 임의로 score라는 필드도 추가해 넣어보았다.
그러면 nested 영역에 들어가지 않는 "group"이라는 필드를 key로 score의 sum을 구하고 싶다면 어떻게 해야할까?

아래와 같이 reverse_nested 구문을 사용하면 된다.
주의 할 것은 먼저 nested한 영역부터 작성해주고 그 안에 reverse_nested, nested 밖 내용을 작성해주면 된다.

  "aggs": {
    "user": {
      "nested": {
        "path": "user"
      },
      "aggs": {
        "score": {
          "sum": {
            "field": "combined_struct.score"
          },
          "aggs": {
            "group_by_key": {
              "reverse_nested": {}, 
              "aggs": {
                "group_sum_score": {
                  "terms": {
                    "field": "group"
                  }
                }
              }
            }
          }
        }
      }
    }
  }

 

 

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

 

Nested datatype | Elasticsearch Reference [7.5] | Elastic

Because nested documents are indexed as separate documents, they can only be accessed within the scope of the nested query, the nested/reverse_nested aggregations, or nested inner hits. For instance, if a string field within a nested document has index_opt

www.elastic.co

 

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-nested-aggregation.html

 

Nested Aggregation | Elasticsearch Reference [7.5] | Elastic

A special single bucket aggregation that enables aggregating nested documents. For example, lets say we have an index of products, and each product holds the list of resellers - each having its own price for the product. The mapping could look like: PUT /p

www.elastic.co

 

elasticsearch에는 fielddata와 doc_values라는 것이 있고, 주요 개념이므로 이해가 필요하다.
더 근본적으로는 루씬의 개념이기 때문에 루씬에서 storedField와 docValue내용을 찾아보는 것이 좋다.

elasticsearch에서 data를 mapping할때에 keyword type과 text type이 있다.
keyword type의 경우 exact매칭에서 사용하고, text type의 경우 analyzed 매칭에 사용된다.
text type의 경우는 형태소 분석을 통해 field를 여러 terms로 나눠서 역인덱싱 과정을 거치게 되고,
keyword type은 그대로 역인덱싱 된다.
* 역인덱스란 키워드가가 어떤 문서에 포함되는지를 저장한다.

검색(search)이라는 것은 "어떤 문서가 이 키워드를 포함하는지가 궁금"하므로 역인덱스된 정보를 통해 검색이 빠른 검색이 가능하다.
그러나 sort, aggregation, accessing field value 와 같은 패턴은 "이 문서에서 이 field value값이 무엇인지"가 관심이므로 역인덱스 정보가 아닌 document를 key로, field정보를 담은 데이터 구조가 필요하다. 그 데이터 구조가 fielddata라는 것이다.

key value
doc1 a:1, b:4, c:7
doc2 a:2, b:5, c:8
doc3 a:3, b:6, c:9

 

그런데 fielddata의 경우 in-memory구조로 작동하기 때문에 많은 heap memory를 소비하게된다. 일단 field가 heap에 로딩되면 그것은 segment의 lifetime동안 남아있게된다. 따라서 비용이 높은 프로세스가된다.

text field를 사용하게 되면 fielddata 데이터 구조를 사용할 수 있는데 위의 설명과 같이 높은 비용때문에 default false로 되어있다.
필요한 경우는 fielddata=true로 옵션을 변경하여 사용하되, memory사용에 주의한다.

keyword field에서는 fileddata의 in-memory에서 동작하는 구조를 개선하여, on-disk data structure인 doc_values 사용이 가능하다.
doc_values는 아래와 같이 column-oriented fashion으로 더욱 유리하게 sort, aggregation 등을 할 수 있다.

key doc1 doc2 doc3
a 1 2 3
b 4 5 6
c 7 8 9


keyword type과 text type은 이렇게 analyzed 되냐 안되냐의 차이뿐 아니라 fielddata, doc_values와 같은 데이터 구조 사용 여부도 달라지므로 적절한 data mapping과 옵션 설정이 중요하다.

 

 

* 위의 설명은 친절한 es 가이드와 루씬내용을 따로 찾아 정리하였습니다.
* es 가이드를 상세히 읽고, 그에 따른 루씬 내용을 찾아보면 이해하기가 좋은 것 같아요.

 

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

 

fielddata | Elasticsearch Reference [7.5] | Elastic

Most fields are indexed by default, which makes them searchable. Sorting, aggregations, and accessing field values in scripts, however, requires a different access pattern from search. Search needs to answer the question "Which documents contain this term?

www.elastic.co

 

https://www.elastic.co/guide/en/elasticsearch/reference/current/doc-values.html

 

doc_values | Elasticsearch Reference [7.5] | Elastic

Most fields are indexed by default, which makes them searchable. The inverted index allows queries to look up the search term in unique sorted list of terms, and from that immediately have access to the list of documents that contain the term. Sorting, agg

www.elastic.co

 

elasticsearch.yml을 고치고 재시작하지 않아도, api를 통해 cluster setting을 수정할 수 있다.
얼마전에 cluster setting값을 수정할일이 있어 문서를 찾아보니, 

es는 진짜 가이드문서가 잘 나와있다.
https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-update-settings.html

 

Cluster update settings API | Elasticsearch Reference [7.5] | Elastic

Cluster update settings APIedit Updates cluster-wide settings. With specifications in the request body, this API call can update cluster settings. Updates to settings can be persistent, meaning they apply across restarts, or transient, where they don’t sur

www.elastic.co

 

get  /_cluster/settings?include_defaults=true 으로 살펴보면
cluster setting 값들이 나온다.

setting값의 영역이  'persistent', 'transient', 'default' 로 나뉘는데,
default는 우리가 수정할 수 있는건 아니다.

default에 나와있는 설정값중에 수정할 부분을 persistent나 transient에 수정하면된다. 
persistent에 수정을 하면 클러스터를 재시작해도 설정이 유지된다.
transient에 수정을 하면 클러스터가 재시작되면 설정이 사라지고, 원복된다.

즉, es cluster에서 설정값이 transient->persistent->defaults 순의 우선순위를 갖게되는 것이다.

 

1. 예를 들어, node를 재시작할때 샤드 route를 피하기 위해 아래와 같이 잠시 설정을 none으로 바꿀때는 transient에 변경. 

PUT /_cluster/settings
{
	"transient": {
		"cluster.routing.allocation.enable": "all"
	}
}

 

2. node를 재시작해도 설정을 유지하고 싶으면 persistent에 변경.

PUT /_cluster/settings
{
	"persistent": {
		"search.default_allow_partial_results": "false"
	}
}

 

3. default 영역은 설정파악할때 참고용.
나는 계속 default영역에 바꾸려고.. 이렇게 삽질을 ㅠ.ㅠ
json에서 "default": { } 여기에 바로 update하는거 아니고, 참고용이고
transient, persistent 에 업뎃하세요~

{
	"default" : {
    	"search": {
        	xxx_xxx_xx : "false"
        }
	}
}

 

현재 memory 128기가, 코어40개의 node 5개를 운영중인데, memory는 몇기가로 설정하면 좋을까?
es가이드를 보면 30기가로 설정하는 것을 추천한다.

자바 명령어로 손쉽게 30기가로 설정하는 것이 좋다는 것을 알 수 있다.
31기가, 30기가, 32기가로 설정했을때 보면 30기가일때가 compressedzero base가 됨.

1.

java -Xmx31G -XX:+PrintFlagsFinal -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode 2>/dev/null | grep Compressed | grep Oops

> heap address: 0x00007fc918200000, size: 31744 MB, Compressed Oops mode: Non-zero based:0x00007fc9181ff000, Oop shift amount: 3 bool PrintCompressedOopsMode := true {diagnostic} bool UseCompressedOops := true {lp64_product}

2.

java -Xmx30G -XX:+PrintFlagsFinal -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode
2>/dev/null | grep Compressed | grep Oops

 

> heap address: 0x0000000080000000, size: 30720 MB, Compressed Oops mode: Zero based, Oop shift amount: 3 bool PrintCompressedOopsMode := true {diagnostic} bool UseCompressedOops := true {lp64_product}

3.

java -Xmx32G -XX:+PrintFlagsFinal -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode 2>/dev/null | grep Compressed | grep Oops

 

> bool PrintCompressedOopsMode := true {diagnostic}
bool UseCompressedOops = false {lp64_product}

 

30g로 옵션을 변경한 후에는 재시작을 해줘야하는데, 이미 인덱스가 존재하고 있었으므로 _cluster/settings api로 allocation.enable=none으로 변경해준 뒤에 재시작하고, 다시 all로 변경해주자.

PUT _cluster/settings 
{ 
	"transient": { 
    	"cluster.routing.allocation.enable": "none"
        } 
} 

# after node restart.

PUT _cluster/settings
{ 
	"transient": {
    	"cluster.routing.allocation.enable": "all"
        }
}

 

설정값을 none으로 변경뒤에 node를 일단 내리면 해당 node에 있던 primary shard가 다른 node에 있던 replica shard와 교환 하게 되고, 교환된 replica shard는 unassigend 상태가 된다. 그리고 node를 올리고 설정값을 all로 변경되면 unassigend replica shard가 그  node에 할당된다. 

 

 

+ Recent posts