ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ES hot-warm, Rollout, Shrink 알아보기
    개발하면서/타인글보면서 2019. 10. 27. 00:52
    반응형

    회사에서 ES도 다루고 있는데 초반에는 별다른 관리 없이도 문제가 없었는데 인입되는 양이 점점 많아지면서

    인덱스 관리가 필요했고, 그때 알아본 키워드가 hot-warm architecture, rollover, shrink 였습니다.

    급한 불을 끈 후 위 링크는 에버노트 깊은 곳에 저장하고 있다가... 올해 더 많은 데이터 인입이 계획 되어있어서,

    아... 학습 좀 하고 적용해야겠다는 생각에 리서치를 시작했습니다.

    ES 공식 블로그 문서를 읽고 직역 비스므리한 정리를 하였습니다.

     

    ES 공식 블로그 - Hot Warm Architecture

    https://www.elastic.co/kr/blog/hot-warm-architecture-in-elasticsearch-5-x?fbclid=IwAR0gajLECYQLc9JSqYICrbfaxwcikUr52OgyVqAgb3MfoWiJWhzcoXgotdk

     

    Elasticsearch Hot Warm Architecture

    A recommendation for using Elasticsearch 5.x for larger time-data analytics: indices & a tiered architecture with 3 different types of nodes, called “Hot-Warm”.

    www.elastic.co

     

    Master nodes

    master node는 es 클러스터 관리 및 상태만 담당하여 전반적으로 안정성을 유지합니다.

    데이터 저장 또는 인덱싱, 검색 같은 비싼 연산은 하지 않으므로 data node 보다는 CPU, Memory를 낮게 줘도 됩니다.

    How nodes

    최신 인덱스의 인덱싱과 검색을 담당하여 CPU, IO를 많이 사용하므로 hot node 서버는 높은 사양과 SSD를 추천합니다.

    HA를 위해 최소 3개의 hot node 띄우는 것을 추천하고 여러분의 최신 데이터 양에 맞춰 개수를 조절하면 됩니다.

    Warm nodes

    검색 query가 빈번하지 않지만 양이 많은 read-only indice에 를 담당합니다.

    warm node는 read-only 이므로 SSD 보다는 크기가 큰 디스크(대개 spinning disk)를 추천합니다.

    데이터가 많은 경우 성능을 유지하기 위에 warm node를 추가가 필요하다는 경고를 보여줄 수 있으니

    이때 warm node를 추가해주면 좋습니다.

     

    and as before,

    with the caveat that larger amounts of data may require additional nodes to meet performance requirements

     

    How-to

    elasticsearch.yml에 node.attr.box_type: hot tag 추가 혹은

    ./bin/elasticsearch -Enode.attr.box_type=hot 로 es를 시작하면 hot node가 되고

    elasticsearch.yml에 node.attr.box_type: warm tag 추가 혹은

    ./bin/elasticsearch -Enode.attr.box_type=warm 로 es를 시작하면 warm node가 됩니다.

    box_type 속성은 ES에 할당된 인덱스에 적용됩니다.

     

    logstash나 filebeat에서는

    indice의

    settings: {

        "index.routing.allocation.require.box_type": "hot" or "warm"

    }

    으로 세팅하면 인덱스 생성 시 해당 box_type을 따라가고

    index template을 이용해서도 box_type을 지정할 수 있습니다.

     

    forcemerge

    _forcemerge는 segment 개수를 줄여서 memory, disk 공간, file handler를 절약하는 것뿐만 아니라

    best_compression codec으로 새롭게 인덱싱을 한다는 부수적인 효과도 있습니다.

    strong box일 때(box_type이 hot을 말하는 듯?) forcemerge를 하게 되면 I/O 성능이 안 좋아지면서 실시간 로그 인덱싱 속도에

    영향을 끼칠 수 있으므로 좋은 생각은 아닙니다.

    하지만 read-only 이면서 검색이 빈번하지 않는 medium box(box_type이 warm을 말하는 듯)는

    안전하게 segment 머지를 할 수 있습니다.

    그러므로 인덱스 데이터가 warm node로 옮겨지면 _forcemerge를 사용하면 좋습니다.

     

    지금까지는 사용자가 직접 box_type을 바꾸고 _forcemerge를 해주는 걸 알아봤는데 이걸 자동으로 해줄 수도 있습니다.

    바로 Curator

     

     

    ※ master node 설명할 때 split-brain에 대한 얘기가 나오는데 알아보면 재밌을 듯
    the discovery.zen.minimum_master_nodes setting at 2, which prevents the possibility of a "split-brain" scenario???

    https://medium.com/@Alibaba_Cloud/elasticsearch-distributed-consistency-principles-analysis-1-node-b512e2b839f8

    https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes-7.0.html?fbclid=IwAR0TJkKmPcJYqB6Q7ggtCFz4yAWNrSxtvcbT_nKlQKduJqUxwQDiAN89KcE#breaking_70_discovery_changes


    ES 공식 블로그 - Rollover

    https://www.elastic.co/blog/managing-time-based-indices-efficiently

     

    And the big one said "Rollover" — Managing Elasticsearch time-based indices efficiently

    Introducing the new Rollover Pattern, and the APIs which support it, which is a simpler, more efficient way of managing time-based indices in Elasticsearch.

    www.elastic.co

    시간 기반의 데이터를 인덱싱 할 때는 인덱스 명을 일자별로 구분해서 하는 패턴이 익숙할 겁니다.

    날짜가 바뀌면 index template의 세팅에 맞춰 새로운 인덱스가 생성이 됩니다.

    ex: test-index-YYYY.MM.dd

     

    날짜 기반의 인덱스 생성 패턴은 이해하기도 쉽고 구현도 쉽지만 다음과 같은 인덱스 관리에 복잡성은 다룰 수 없습니다.

     

    but it glosses over some of the complexities of index management such as the following:
    to avoid considering something, such as an embarrassing mistake, to make it seem not important, and to quickly continue talking about something else:

    https://dictionary.cambridge.org/dictionary/english/gloss-over-sth

     

    • 높은 인입량을 처리하기 위해선 가능한 많은 노드에 shard를 뿌려줘야 합니다.
    • 검색과 리소스 사용량을 최적화하기 위해 가능한 적은 shard를 만들고 싶은데
      그렇다고 다루기 어려울 정도로 큰 한 개의 shard를 만들고 싶지도 않습니다.
    • 일자별 인덱스는 오래된 데이터를 삭제하기 용이합니다. 하지만 얼마나 많은 shard가 필요한지는 알지 못합니다.
    • 매일 동일한 shard인가요? 아니면 shard가 너무 많다거나 부족하지는 않았나요?

    그래서 나온 게 Rollover Pattern이고, 단순하지만 효과적으로 관리가 가능한 API를 제공합니다.

    Rollover Pattern은  다음과 같이 동작합니다.

    • 실제 인덱싱이 발생하는 active index 가리키는 하나의 alias가 있고,
      검색을 위해 active/inactive index를 가리키는 또 하나의 alias가 있습니다.
    • active index는 hot node가 가용 가능한 shard 개수만큼 가질 수 있습니다.
    • active index가 너무 커지거나 너무 오래된 경우, rollover 발동!!
      새로운 인덱스가 만들어지고 1번의 alias는 새롭게 만들어진 인덱스를 atomic 하게 변경합니다.
    • 오래된 인덱스는 cold node로 옮겨져 하나의 shard로 축소(shrink)되어 forcemerge 합니다.

    Getting started

    10대의 hot node가 있고 어느 정도의 cold node가 있다고 가정해봅시다.

    각 primary shard 당 하나의 replica를 갖게 함으로써 특정 노드 장애 시에도 사용에 문제없게 할 수 있습니다.

    이 뜻은 active index가 5개 primary shard로 세팅됐다면 replica 포함 10개 shard가 필요하다는 것이고,

    hot node 당 한 개의 shard를 갖고 있는걸 뜻합니다.

     

    active index template

    box_type은 hot이고 total_shards_per_node는 shard를 노드에 골고루 뿌려줄 때 도움 주는 옵션입니다.

    특정 노드가 실패할 경우에도 shard이 가능하도록 1이 아닌 2로 세팅하였습니다.

    active-logs-* pattern의 검색을 위해 alias는 search-logs로 지정하였습니다.

    PUT _template/active-logs
    {
     "template": "active-logs-*",
     "settings": {
       "number_of_shards":   5,
       "number_of_replicas": 1,
       "routing.allocation.include.box_type": "hot",
       "routing.allocation.total_shards_per_node": 2
      },
     "aliases": {
       "search-logs": {}
      }
    }

    box_type은 cold이고 deflate 압축을 통해 저장 공간을 아끼는 옵션입니다.
    replicas를 0으로 한 이유는 나중에 설명하겠습니다.

    PUT _template/inactive-logs
    {
     "template": "inactive-logs-*",
     "settings": {
       "number_of_shards":   1,
       "number_of_replicas": 0,
       "routing.allocation.include.box_type": "cold",
       "codec": "best_compression"
      }
    }

     

    active-logs-1 인덱스를 만들고
    active index 이므로 alias를 active-logs로 지정하고
    _bulk API를 이용하여 active index인 active-logs(alias)에 5개 문서를 인덱싱 합니다.

    PUT active-logs-1
    PUT active-logs-1/_alias/active-logs    
    POST active-logs/log/_bulk
    { "create": {}}
    { "text": "Some log message", "@timestamp": "2016-07-01T01:00:00Z" }
    { "create": {}}
    { "text": "Some log message", "@timestamp": "2016-07-02T01:00:00Z" }
    { "create": {}}
    { "text": "Some log message", "@timestamp": "2016-07-03T01:00:00Z" }
    { "create": {}}
    { "text": "Some log message", "@timestamp": "2016-07-04T01:00:00Z" }
    { "create": {}}
    { "text": "Some log message", "@timestamp": "2016-07-05T01:00:00Z" }


    Rolling over the index

    active index가 아주 오래되었거나 크기가 많이 커지면 빈 인덱스를 만들고 교체가 필요한데 rollover API는 이를 가능하게 해 줍니다.

    그런데 크기가 얼마나 커야 많이 크다고 할 수 있을까요?

    여러분이 소유한 인프라나, 검색 용도, 기대하는 성능, 그리고 shard 복구 시 기다릴 수 있는 시간 등 처한 상황에 따라 다릅니다.

    실제로 shard의 크기를 각기 다르게 한 후 테스트를 진행해 여러분에게 맞는 개수를 알아내야 합니다.

     

    In practice, you can try out different shard sizes and see what works for you.

     

    그리고 shrink는 하나의 인덱스를 한 shard로 모으는 동작입니다.
    하나의 shard는 최대 2,147,483,519 개의 문서를 가질 수 있는데 한 인덱스의 문서 개수가 이보다 많다면
    둘 이상의 shard로 shrink는 동작할 것입니다.

     

    시간 단위로 rolling을 하면 (test-index-YYYY.MM.dd) 찾으려는 로그가 어느 인덱스에 저장되어있는지 알기 쉽습니다.
    하지만 문서 개수로 할 때 더 큰 장점이 있습니다.

    바로 모든 shard의 크기가 얼추 비슷해서 노드들의 밸런스가 무너지지 않는다는 점이죠.

    rollover API는 주기적으로 해당 인덱스가 max_docs, max_age에 도달했는지 체크합니다.

    아래 요청은 active-logs(alias)가 7일이 지났거나, 문서 개수가 5개가 넘은 경우 새로운 인덱스를 만들라는 얘기입니다.

    ※ index template의 settings과 mapping 값 중 변경하고 싶다면 body에 추가해주면 됩니다.

    POST active-logs/_rollover
    {
     "conditions": {
       "max_age":   "7d",
       "max_docs":  5
      }
    }

    Response

    {
      "old_index": "active-logs-1",
      "new_index": "active-logs-2",
      "rolled_over": true,
      "dry_run": false,
      "conditions":
        {
          "[max_docs: 5]": true,
          "[max_age: 7d]": false
        }
    }

     

    Why don't we support a max_size constraint?

    max_doc처럼 왜 max_size는 없는지에 대해 설명하겠습니다.

    shard의 크기는 정확한 측정이 어렵습니다.
    왜냐하면 인덱스 merge 중에는 임시 파일이 생겨 shard의 크기가 계속 증가하다가 merge가 완료되면 이 값은 바로 줄어듭니다.

    예를 들어 5개의 primary shard인  인덱스가 있고 각 shard 크기가 5GB라고 가정했을 때
    merge가 실행되면 일시적으로 크기가 25GB 더 증가됩니다. 
    하지만 문서 개수는 예측 가능합니다.

     

    https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-rollover-index.html API 문서에는 max_size가 있네요
    임시 저장 공간을 어떻게 파악하는지 궁금하네요.

    Shrinking the index

    더 이상 active-logs-1에는 쓰기 연산은 일어나지 않을 것이고 우리는 cold node로 옮기고,

    한 개의 shard를 가진 inactive-logs-1라는 새로운 인덱스로 shrink 할 수 있습니다.

     

    shrinking 하기 전 반드시 해야 할 게 있습니다.

    • 해당 인덱스를 read-only로 만들기
    • 모든 shard의 데이터를 하나의 노드로 복사하기(노드 선택 가능)
    PUT active-logs-1/_settings
    {
     "index.blocks.write": true,
     "index.routing.allocation.require._name": "some_node_name"

    }

     

    allocation 설정은 각 shard의 적어도 하나의 사본이 "some_node_name" 노드로 이동시킵니다.
    모든 shard가 아닙니다.! (replica shard는 primary shard와 동일한 노드에 있을 수 없습니다)

     

    shard의 primary 혹은 replica 중 적어도 하나는 이동됩니다.

    위 작업이 완료되었다면 (cluster health APi를 통해 확인) shrink 명령을 실행합니다.

    POST active-logs-1/_shrink/inactive-logs-1

    여러분의 파일 시스템이 하드 링크를 지원한다면 즉시 실행될 것이고, 지원하지 않는다면
    모든 segment 파일을 한 곳으로 카피할 때까지 기다려합니다.

     

    recovery API 혹은 cluster API를 이용하여 shrink의 진행상황을 모니터링할 수 있습니다.

    GET _cluster/health/inactive-logs-1?wait_for_status=yellow

    shrink 완료된 것을 확인했다면 search-logs alias에서 active-logs-1은 빼고 inactive-logs-1은 추가해주면 됩니다.

    POST _aliases
    {
      "actions": [
      {
        "remove": {
          "index": "active-logs-1",
          "alias": "search-logs"
        }
      },
      {
        "add": {
          "index": "inactive-logs-1",
          "alias": "search-logs"
        }
      }
      ]
    }

     

    Saving space

    한 개의 shard로 줄었지만 segment 갯수는 기존과 동일합니다. (best_compression이 실행 안된 상태)

    한개의 shard를 한개의 segment 파일로 줄이기 위해서는 _forcemerge를 실행하면 됩니다.

    POST inactive-logs-1/_forcemerge?max_num_segments=1

     

    이때 ES에서는 하나에 shard에 존재하고 있던 다수의 segment 파일을 한 개의 새로운 segment로 만드는데
    이때 best_compression이 적용되면서 deflate 압축을 사용합니다.

    _forcemerge는 primary와 replica 모두 만들 필요가 없으므로

    위에서 언급했던 inactive-logs index template은 replica를 0으로 했습니다.

    There is no point in running a force-merge on both the primary and replica shard, which is why our template for inactive log indices set the number_of_replicas to 0.

    _forcemerge가 끝났다면 replica를 1로 늘려줍니다.

    PUT inactive-logs-1/_settings { "number_of_replicas": 1 }

     

    cluster health API를 이용해서 replica까지 완료된 게 확인됐다면 기존 active index 였던 active-logs-1은 삭제해줍니다.

    DELETE active-logs-1

    Deleting old indices

    날짜별 인덱스 패턴과는 다르게 rollover 패턴은 해당 인덱스에 어떤 날짜가 저장되었는지 쉽게 알 수 없습니다.

    쉽게 알수 있는 field stats API가 있으니 이를 이용하면 됩니다.

    @timestamp 필드에서 2016/07/03보다 작은 데이터가 존재하는 인덱스를 리턴하는 요청입니다. 리턴되는 인덱스를 지워주면 되겠죠?

    GET search-logs/_field_stats?level=indices
    {
      "fields": ["@timestamp"],
      "index_constraints": {
        "@timestamp": {
          "max_value": {
            "lt": "2016/07/03",
            "format": "yyyy/MM/dd"
          }
        }
      }
    }

    공식문서 정리는 끝이 났습니다.

    hot node는 색인이 발생하고 검색이 빈번한 인덱스

    warm/cold node는 색인은 발생하지 않고 검색이 근근히 일어나는 인덱스
        https://www.elastic.co/blog/implementing-hot-warm-cold-in-elasticsearch-with-index-lifecycle-management

        보면 warm은 위에서 얘기한 rollover, shrink, _forcemerge가 일어나는 node, cold는 복구를 위한 node라고 합니다.

    rollover pattern 는 time pattern이 해결할수 없는 부분을 위해 나온 문서 개수, retention으로 인덱스를 관리하는 방법

    shrink는 한 인덱스에 있는 모든 shard를 한개의 shard로 옮기는 작업, segment 파일 개수는 그대로임

    _forcemergesegment 파일을 새로 쓰면서 머지하고 best_compression이 실제 일어나는 곳

     

    hot-warm은 이해하기 쉬운데 rollover는 복잡해서 한번 더 정리해보겠습니다.

     

    1. hot node에 유지할 인덱스를 active index라고 하고 alias 생성(A)

    2. cold(warm?) node에 유지할 인덱스를 inactive index라고 하고 이것도 alias 생성(B)

    3. 검색할 때 사용되는 alias 생성(C), 최초에는 active index와 동일

    4. (A) alias에 rollover 적용, rollover가 발동되면 기존(active-logs-1) active index를 write block 및 특정 노드에 몰아두기

    5. active-logs-1 -> inaictve-logs-1 shrink 실행

    6. 검색할 때 사용되는 alias 수정 (acitve-logs-1 제거, inactive-logs-1 추가)

    7. inactive-logs-1 _forcemerge 실행 및 replica 0에서 1로 세팅

    8. rollover pattern은 인덱스에 어떤 데이터가 있는지 파악하기 힘들지만 field stat API를 이용하여 조회 및 삭제

     

    field stat API는 없어졌는데(5.6) 아래 두 API 중에 하나 쓰면 되지 않을까.... 생각해봅니다 ㅎㅎ;;

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

    https://www.elastic.co/guide/en/elasticsearch/reference/7.4/indices-stats.html

    반응형

    댓글

Designed by Tistory.