Blog

2016.07.12 - 번역 - And the big one said "Rollover" — Managing Elasticsearch time-based indices efficiently ...

drscg 2019. 1. 7. 09:00

Anybody who uses Elasticsearch for indexing time-based data such as log events is accustomed to the index-per-day pattern: use an index name derived from the timestamp of the logging event rounded to the nearest day, and new indices pop into existence as soon as they are required. The definition of the new index can be controlled ahead of time using index templates.

log event처럼 시간 기반의 data를 index하기 위해 Elasticsearch를 사용하는 이들은 날짜별 index(log event의 timestamp를 가장 가까운 날짜로 반올림한 값을 index 이름으로 사용)에 익숙하다. 새로운 index는 필요할 때마다 생성된다. 새로운 index의 정의는 index templates 을 사용하여 미리 제어할 수 있다.

This is an easy pattern to understand and implement, but it glosses over some of the complexities of index management such as the following:

이는 이해하기 쉽고 구현하기 쉬운 pattern으로, 다음과 같은 index 관리의 복잡성에 대해 설명한다.

  • To achieve high ingest rates, you want to spread the shards from your active index over as many nodes as possible.
    높은 index 속도를 위해서, 활성 index의 shard를 가능한 많은 node에 분산해야 한다.
  • For optimal search and low resource usage you want as few shards as possible, but not shards that are so big that they become unwieldy.
    최적의 search와 낮은 resource 활용을 위해서, 가능한 한 적은 수의 shard가 좋지만, 너무 커서 불안정해지는 shard는 좋지 않다.
  • An index per day makes it easy to expire old data, but how many shards do you need for one day?
    일별 index는 오래된 data를 없애기가 쉽다 그렇다면, 하루에 몇 개의 shard가 필요한가?
  • Is every day the same, or do you end up with too many shards one day and not enough the next?
    매일 똑같나? 아니면 어떤 날은 많은 shard가 필요하고 그 다음에는 충분하지 않나?

In this blog post I’m going to introduce the new Rollover Pattern, and the APIs which support it, which is a simpler, more efficient way of managing time-based indices.

이 게시물에서는 새로운 Rollover Pattern 과 시간 기반 index를 보다 간편하고 효율적으로 관리하는 API를 소개하려 한다. 

Rollover Pattern

The Rollover Pattern works as follows:

Rollover Pattern은 다음과 같이 동작한다.

  • There is one alias which is used for indexing and which points to the active index.
    index에 사용되고, 활성 index를 가리키는 하나의 alias가 있다.
  • Another alias points to active and inactive indices, and is used for searching.
    다른 alias는 활성 및 비활성 index를 가리키며, search에 사용된다.
  • The active index can have as many shards as you have hot nodes, to take advantage of the indexing resources of all your expensive hardware.
    활성 index는, 모든 비싼 hardware의 index resource를 활용하기 위하여, hot node만큼 많은 shard를 가질 수 있다.
  • When the active index is too full or too old, it is rolled over: a new index is created and the indexing alias switches atomically from the old index to the new.
    활성 index가 너무 꽉 차거나 너무 오래된 경우, roll over 된다. 새로운 index가 생성된고 index alias는 오래된 index에서 새로운 index로 자동 전환된다.
  • The old index is moved to a cold node and is shrunk down to one shard, which can also be force-merged and compressed.
    오래된 index는 cold node로 이동하여, 하나의 shard로 축소(shrink)되며, 강제 merge 되고 압축될 수도 있다.

Getting started

Let’s assume we have a cluster with 10 hot nodes and a pool of cold nodes. Ideally, our active index (the one receiving all the writes) should have one shard on each hot node in order to split the indexing load over as many machines as possible.

10개의 hot node와 cold node pool을 가진 cluster가 있다고 가정해 보자. 이상적으로, 활성 index(모든 쓰기를 수신하는 index)는 index load를 가능한 한 많은 machine에 분할하기 위하여, 각 hot node에 하나의 shard를 가져야 한다.

We want to have one replica of each primary shard to ensure that we can tolerate the loss of a node without losing any data. This means that our active index should have 5 primary shards, giving us a total of 10 shards (or one per hot node). We could also use 10 primary shards (a total of 20 including replicas) and have two shards on each node.

data의 손실없이 node를 잃을 수 있도록 허용하려면, 각 primary shard의 replica가 있어야 한다. 즉, 활성 index는 5개의 primary shard가 있어야 하며, 총 10개의 shard(또는 hot node당 1개씩)가 제공된다. 또한, 10개의 primary shard(replica를 포함하여 총 20개)를 사용할 수 있으며, 각 node당 2개의 shard를 가진다.

First, we’ll create an index template for our active index:

먼저, 활성 index를 위한 index template 을 생성한다.

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": {} } }

Indices created from this template will be allocated to nodes tagged with box_type: hot, and the total_shards_per_node setting will help to ensure that the shards are spread across as many hot nodes as possible. I’ve set it to 2 instead of 1, so that we can still allocate shards if a node fails.

이 template으로 생성된 index는 box_type: hot 으로 tag되어 node에 할당된다. 그리고, total_shards_per_node 설정을 톻해 shard가 가능한 한 많은 node에 분산되도록 할 것이다. node 장애시에도 shard를 할당할 수 있도록, 이 값을 1 대신 2 로 설정했다.

We will use the active-logs alias to index into the current active index, and the search-logs alias to search across all log indices.

현재 활성 index에 index하도록 active-logs alias를, 모든 log index를 search하기 위하여, search-logs alias를 사용할 것이다.

Here is the template which will be used by our inactive indices:

아래가 비활성 index에 사용될 template이다.

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" } }

Archived indices should be allocated to cold nodes and should use deflate compression to save space. I’ll explain why I’ve set replicas to 0 later.

보관된 index는 cold node애 활당되어야 하고, 공간을 절약하기 위하여 deflate 압축을 사용해야 한다. replicas 를 0 으로 설정한 이유는 나중에 설명하겠다.

Now we can create our first active index:

이제 첫번째 활성 index를 만들 수 있다.

PUT active-logs-1
PUT active-logs-1/_alias/active-logs        

The -1 pattern in the name is recognised as a counter by the rollover API. We will use the active-logs alias to index into the current active index, and the search-logs alias to search across all log indices.

이름의 -1 pattern은 rollover API에 의해 counter로 인식된다. 현재 활성 index에 index하기 위하여 active-logs alias를, 모든 log index를 search 하기 위하여 search-logs alias를 사용한다.

Indexing events

When we created the active-logs-1 index, we also created the active-logs alias. From this point on, we should index using only the alias, and our documents will be sent to the current active index:

active-logs-1 index를 생성할 때, active-logs alias도 생성했다. 지금부터는 alias만을 사용하여 index해야 하며, document는 현재 활성 index로 전송된다.

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

At some stage, the active index is going to become too big or too old and you will want to replace it with a new empty index. The rollover API allows you to specify just how big or how old an index is allowed to be before it is rolled over.

어떤 시점에, 활성 index가 너무 크거나 오래되어, 이를 새로운 빈 index로 변경하려 한다. rollover API API를 사용하면, roll over되기 전에, index의 크기나 기간을 지정할 수 있다.

How big is too big? As always, it depends. It depends on the hardware you have, the types of searches you perform, the performance you expect to see, how long you are willing to wait for a shard to recover, etc etc. In practice, you can try out different shard sizes and see what works for you. To start, choose some arbitrary number like 100 million or 1 billion. You can adjust this number up or down depending on search performance, data retention periods, and available space.

얼마 커야 너무 큰 것일까? 항상 그렇지만, 경우에 따라 다르다. hardware, searcj 형태, 기대하는 성능, shard가 복구되기를 기다리는 시간 등에 따라 다르다. 실제로, 다양한 크기의 shard를 시험해 보고, 그 효과를 확인할 수 있다. 시작하려면, 1억, 10억 같은 임의의 숫자를 선택하자. search 성능, data 보존 기간, 가용 공간 등에 따라 이 숫자를 조정할 수 있다.

There is a hard limit on the number of documents that a single shard can contain: 2,147,483,519. If you plan to shrink your active index down to a single shard, you must have fewer than 2.1 billion documents in your active index. If you have more documents than can fit into a single shard, you can shrink your index to more than one shard, as long as the target number of shards is a factor of the original, e.g. 6 → 3, or 6 → 2.

하나의 shard가 가질수 있는 document의 수는 2,147,483,519로 제한된다. 활성 index를 단일 shard로 측소하려는 경우, 활성 index에는 21억개 미만의 document를 가져야 한다. 하나의 shard에 들어갈 수 있는 것보다 더 많은 document가 있다면 index를 하나 이상의 shard(대상 shard의 수는 원래 index의 요소가 중요하다. 예를 들면, 6 → 3, 6 → 2)로 축소(shrink)해야 한다.

Rolling an index over by age may be convenient as it allows you to parcel up your logs by hour, day, week, etc, but it is usually more efficient to base the rollover decision on the number of documents in the index. One of the benefits of size-based rollover is that all of your shards are of approximately the same weight, which makes them easier to balance.

시간, 일, 주 등을 기준으로 log를 정리할 수 있으므로, 기간별로 index를 rolling하면 편리하지만, 일반적으로 index document 수를 rollover 결정의 기준으로 하는 것이 더 효율적이다. size 기반 rollover의 이점 중 하나는 모든 shard의 크기가 거으 같기 때문에, 쉽게 균형을 맞출 수 있다는 것이다.

The rollover API would be called regularly by a cron job to check whether the max_docs or max_age constraints have been breached. As soon as at least one constraint has been breached, the index is rolled over. Since we’ve only indexed 5 documents in the example above, we’ll specify a max_docs value of 5, and (for completeness), a max_age of one week:

rollover API는 cron job에 의해 정기적으로 호출되어, max_docs 나 s max_age 조건을 만족하는가를 확인한다. 적어도 하나의 조건을 만족하면, index는 roll over된다. 위의 예에서 5개의 document만 index했기 때문에, max_docs 를 5 로, max_age 를 일주일로 지정한다.

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

This request tells Elasticsearch to rollover the index pointed to by the active-logs alias if that index either was created at least seven days ago, or contains at least 5 documents. The response looks like this:

이 request는 index가 적어도 7일 이전에 생성되었거나 최소한 5개의 document를 가진 경우, active-logs 가 가리키는 index를 rollover한다. 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 } }

The active-logs-1 index has been rolled over to the active-logs-2 index because the max_docs: 5 constraint was met. This means that a new index called active-logs-2 has been created, based on the active-logstemplate, and the active-logs alias has been switched from active-logs-1 to active-logs-2.

max_docs: 5 조건이 충족되었으므로, active-logs-1 index는 active-logs-2 index로 roll over된다. 즉, active-logs template을 기반으로 한 active-logs-2 라는 새로운 index가 생성되었다는 것을 의미한다. 그리고, active-logs alias는 active-logs-1 에서 active-logs-2 로 전환된다.

By the way, if you want to override any values from the index template such as settings or mappings, you can just pass them in to the _rollover request body like you would with the create index API.

template에서 settings 이나 mappings 같은 값을 재정의하려면, create index API 에서와 마찬가지로 _rollover request body에 그 값을 전달하면 된다. 

Why don't we support a max_size constraint?

Given that the intention is to produce evenly sized shards, why don't we support a max_size constraint in addition to max_docs?  The answer is that shard size is a less reliable measure because ongoing merges can produce significant temporary growth in shard size which disappears as soon as a merge is completed.  Five primary shards, each in the process of merging to one 5GB shard, would temporarily increase the index size by 25GB!  The doc count, on the other hand grows predictably.

동일한 크기의 shard를 생성하는 것이 목적이라면, max_docs 외에 max_size 조건을 지원하는 것이 어떨까? 그 답은 shard의 크기는 신뢰도가 떨어진다. 왜냐하면, 지속적인 merge는 일시적으로 상당한 크기의 shard를 생성할 수 있다. 이 shard는 merge가 완료되면 사라진다. 하나에 5GB인 shard를 merge하는 process 각각에서, 5개의 primary shard는 일시적으로 index 크기를 25GB까지 증가시킨다. 반면에, document count는 예측가능하게 증가한다.

Shrinking the index

Now that active-logs-1 is no longer being used for writes, we can move it off to the cold nodes and shrink it down to a single shard in a new index called `inactive-logs-1`. Before shrinking, we have to:

이제, 는 더 이상 쓰기에 사용되지 않으므로, cold node로 이동하고, `inactive-logs-1` 라는 새로운 index에서 단일 shard로 축소(shrink)할 수 있다. 축소되기 전에, 우리는 다음과 같이 해야 한다.

  • Make the index read-only.
    index를 읽기 전용으로 만든다.
  • Move one copy of all shards to a single node. You can choose whichever node you like, probably whichever cold node has the most available space.
    모든 shard의 복사본 하나를 단일 node로 옮긴다. 원하는 node, 가용 공간이 가장 많은 cold node를 선택할 수 있다.

This can be achieved with the following request:

다음 request를 통해, 이 작업을 할 수 있다.

PUT active-logs-1/_settings { "index.blocks.write": true, "index.routing.allocation.require._name": "some_node_name" }

The allocation setting ensures that at least one copy of each shard will be moved to the node with name some_node_name. It won’t move ALL the shards — a replica shard can’t be allocated to the same node as its primary — but it will ensure that at least one primary or replica of each shard will move.

allocation 설정을 사용하면, 각 shard의 복사본 중 최소한 하나가 some_node_name 이라는 이름을 가진 node로 이동한다. 모든 shard가 이동하지는 않는다(replica shard는 그것의 primary와 동일한 node에 항당될 수 없다). 그러나 각 shard의 primary나 replica shard 중 최소한 하나가 이동할 것이다.

Once the index has finished relocating (use the cluster health API to check), issue the following request to shrink the index:

reallocating이 완료되면(cluster health API 로 확인하자), index를 축소(shrink)하기 위해 다음과 같은 request를 실행하자.

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

This will kick off the shrink process. As long as your filesystem supports hard links, the shrink will be almost instantaneous. If your filesystem doesn’t support hard links, well, you’ll have to wait while all the segment files are copied from one index to another…

이것이 shrink process의 시작이다. filesystem이 hard link를 지원한다면, shrink는 거의 순식간이다. 지원하지 않는다면, 모든 segment file이 특정 index에서 다른 index로 복사될 때까지 기다려야 한다.

You can monitor the shrink process with the cat recovery API or with the cluster health API:

cat recovery API 이나 cluster health API 로 shrink process를 모니터링할 수 있다.

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

As soon as it is done, you can remove the search-logs alias from the old index and add it to the new:

shrink process가 끝나면, search-logs alias를 기존 index에서 제거하고, 새로운 index에 추가한다.

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

Saving space

Our index has been reduced to a single shard, but it still contains the same number of segment files as before, and the best_compression setting hasn’t kicked in yet because we haven’t made any writes. We can improve the situation by force-merging our single-shard index down to a single segment, as follows:

index가 단일 shard로 줄어들었지만, 여전히 전과 동일한 수의 segment를 가지고 있고, 쓰기가 수행되지 않아 best_compression 설정이 아직 시작되지 않았다. 다음과 같이 단일-shard index를 단일 segment로 force-merging 하여, 상황을 개선할 수 있다.

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

This request will create a single new segment to replace the multiple segments that existed before. Also, because Elasticsearch has to write a new segment, the best_compression setting will kick in and the segment will be written with deflate compression.

이 request는 기존의 여러 segment를 대체할 새로운 단일 segment를 생성한다. Elasticsearch가 새로운 segment를 작성하기 때문에, best_compression 설정이 동작하고 그 segment는 deflate 압축으로 작성된다.

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. Now that force-merge has finished, we can increase the number of replicas to gain redundancy:

primary와 replica 모두에 force-merge를 실행하는 것은 아무런 의미가 없으므로, 비활성  log index에 대한 template에서 number_of_replicas 은 0 으로 설정하였다. 이제 force-merge가 완료되면, 복제를 위해, replica의 수를 증가시킬 수 있다.

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

Once the replica has been allocated — use the cluster health API with ?wait_for_status=green to check — we can be sure that we have a redundant copy of our data and we can safely delete the active-logs-1 index:

replica가 할당되면(cluster health API에 ?wait_for_status=green 매개변수를 사용하여 확인), data의 복제본이 있는지 확인할 수 있고, active-logs-1 index를 안전하게 삭제할 수 있다.

DELETE active-logs-1

Deleting old indices

With the old index-per-day pattern, it was easy to decide which old indices could be dropped. With the rollover pattern, it isn’t quite as obvious which index contains data from which time period.

기존의 일별 index 패턴을 사용하면, 오래된 index를 결정하는 것이 쉬웟다. rollover 패턴을 사용하면 어떤 index가 어떤 기간의 data를 포함하고 있는지 분명하지 않다

Fortunately, the field stats API makes this easy to determine. We just need to look for all indices where the highest value in the @timestamp field is older than our cutoff:

다행히도, field stats API 를 사용하면 쉽게 확인할 수 있다. filed의 값 중 가장 큰 값이 기준값보다 오래된 모든 index를 찾으면 된다.

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

Any indices returned by the above request can be deleted.

위의 request에서 반환된 모든 index는 삭제될 수 있다.

Future enhancements

With the rollovershrinkforce-merge, and field-stats APIs, we have provided you with the primitives to manage time-based indices efficiently.

rollovershrinkforce-mergefield-stats API를 통해, 시간 기반의 index를 효율적으로 관리할 수 있는 기반 기술을 제공한다. 

Still, there are a number of steps which could be automated to make life simpler. These steps are not easy to automate within Elasticsearch because we need to be able to inform somebody when things don’t go as planned. This is the role of a utility or application built on top of Elasticsearch.

여전히 더 간단하게 자동화라 수 있는 많은 단계들이 있다. 이 단계들은 Elasticsearch 내에서 자동화하기가 쉽지 않다. 작업이 계획대로 되지 않았을 경우 누군가에게 아릴 수 있어야 한다. 이는 Elasticsearch를 기반으로 만들어진 utility나 application의 역활이다.

Expect to see a work flow based on the above provided by the Curator index management tool and a nice UI in X-Pack which can take advantage of the scheduling and notification features to provide a simple, reliable, index management tool.

Curator index management tool 과 X-Pack 의 멋진 UI에서 위의 내용을 기반으로 한 work flow를 볼 수 있을 것이다. 여기에서는 간단하고 안정적인 index 관리 tool을 제공하기 위해 scheduling과 알림 기능을 이용할 수 있다.

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