728x90

추석 연휴 동안 서버 3대중 한대가 죽어서 2대로 테스트 데이터를 받고있었습니다. 

오늘 회사 서버를 살려서 연결 해 보니, 새로 생성된 데이터의 샤드가 제대로 분배가 되지 않는것을 파악했습니다.

 

그 해결책으로 밑에 구문을 보시면 될거같습니다.

 

 

장애 대응으로 한참 샤드 복구와 샤드 할당 작업 등이 수행 중인 바쁜 상황에서 새 인덱스가 생성 될 때를 조심해야 한다. 

엘라스틱서치에 새 샤드가 할당될 때 엘라스틱서치는 해당 노드에 총 몇개의 샤드가 있는지 체크한 뒤 적은 수의 샤드를 들고 있는 노드에 새 샤드를 할당한다. 

 

문제는 장애 복구 작업 중 방금 재시작된 노드는 샤드 복구가 제대로 끝난 상태가 아니라는 것이다. 이 노드는 겉으로 보기에는 적은 샤드를 들고 있는 것으로 판정된다. 이때 새 인덱스가 생성되면 이 인덱스의 모든 샤드를 한 노드가 몰아서 할당받는다. 그렇게 해야 전체적인 샤드 수의 균형이 맞다고 엘라스틱서치가 판단하기 때문이다. 

여기에 데이터까지 들어오면 원래는 수십 대의 노드가 분산해서 색인해야 할 데이터를 한 노드가 전부 받아서 처리하다가 다시 죽는 문제가 발생한다. 

cluster.routing.allocation.balance.shard 설정은 샤드 할당의 균형을 맞추는 경향성을 지정한다. 이 값이 낮을수록 균형을 신경 쓰지 않는다. 기본값은 0.45다. 장애 복구 작업 도중 새 인덱스가 생성될 것으로 예상되면 이 값을 임시로 0으로 낮춘다.

 

# 가중치: 노드마다 샤드 개수 밸런싱 < 인덱스마다 샤드 개수 밸런싱
cluster.routing.allocation.balance.shard: 0
cluster.routing.allocation.balance.index: 1
cluster.routing.allocation.balance.threshold: 1.0

쿼리
PUT _cluster/settings
{
  "transient": {
    "cluster.routing.allocation.balance.shard": 0.0,
    "cluster.routing.allocation.balance.index": 1.0
  }
}

공식 문서 :  https://www.elastic.co/guide/en/elasticsearch/reference/7.2/shards-allocation.html

 

Cluster level shard allocation | Elasticsearch Guide [7.2] | Elastic

Regardless of the result of the balancing algorithm, rebalancing might not be allowed due to forced awareness or allocation filtering.

www.elastic.co

비상 상황 처리가 끝나면 이 값을 다시 원래대로 돌려야한다. 계속 0으로 놓고 운영을 하면 특정 노드에 특정 샤드의 배분이 우선된다. 균형을 고려하지 않고 순서대로 단순하게 샤드를 배정하기 때문에 시간이 지날수록 어떤 노드는 특정한 샤드를 많이 할당받고, 어떤 노드는 샤드를 잘 할당 받지 않는 현상이 심화된다. 

 

당장 큰 문제는 생기지 않겠지만 인덱스당 샤드의 수 대비 노드의 수가 크면 클수록, 인덱스의 수가 많을수록, 인덱스마다 데이터의 성질이 다를수록 쏠림 경항이 심해지며 점점 문제의 소지가 된다.

 

이미 특정 노드에 샤드가 몰린 경우 수동으로 reroute API를 호출해서 다른 노드에 샤드를 옮길 수 있다. 다만 이 reroute 작업이 수행되는 동안은 클러스터 부하가 커지기 때문에 전후 상황을 봐서 수행해야 한다. reroute API 는 다음과 같이 호출한다. commands에  하나 이상의 명령을 지정할 수도 있다. 

POST _cluster/rerout
{
	"commands":[
    {
      "move":{
        "index": "test-index-5",
        "shard" : 0,
        "from_node": "es02",
        "to_node": "es01"
      }
    }
  ]
}

 

직업 REST API를 호출하는 방법 외에도 cerebro 같은 관리 도구를 사용하면 UI상에서 샤드 분포를 눈으로 확인하며 쉽고 편리하게 샤드를 옮길 수 있다.

728x90
728x90

인덱스의 샤드 개수(number_of_shards) 는 한 번 지정하면 reindex 등의 특별한 작업을 수행하지 않는 한 변경할 수 없다.

그런데 샤드 개수를 어떻게 지정하느냐에 따라 엘라스틱서치 클러스터 전체의 성능이 크게 달라진다. 중요한 설정임에도 불구하고 샤드 개수를 정확히 어떤 값으로 지정하면 되는지 딱 정해주는 알기 쉽고 명확한 기준은 알려진 바가 없다. 이 문제는 굉장히 어려운 문제다. 이번 절에서는 샤드 개수를 어떻게 지정할지 그 전략을 알아보도록 하겠습니다. 

 

샤드 크기와 개수 조정

클러스터에 샤드 숫자가 너무 많아지면 클러스터 성능이 눈에 띄게 떨어지게 됩니다. 샤드 하나당 루씬 인덱스가 하나씩 더 뜨며 힙을 차지 한다. 주 샤드를 하나 더 띄울 때마다 복제본 샤드도 늘어나는 것을 고려하면 무작정 number_of_shards를 늘릴 수는 없다. 그렇다고 샤드 숫자를 적게 지정하면 샤드 하나당 크기가 커진다. 

 

샤드 하나의 크기가 너무 커지는것도 심각한 문제이고, 샤드 크기가 지나치게 크면 재기동이나 장애 상황 등에서 샤드 복구, 복제본 샤드 생성에 너무 많은 시간이 소요된다. 이렇게 되면 문제 상황에서 클러스터 안정성이 크게 떨어진다. 장애로 인해 재기동한 노드가 샤드 복구를 하는 과정 자체가 너무 무거워서 복구 도중 다시 죽는 일도 생길 수 있다. 전체 적인 서비스 성능도 쉽게 체감될 정도로 감소한다.

결국 밸런스가 중요합니다.

그렇다면 어떤 기준으로 어떻게 밸런스를 잡아야 할까요?

 

조금 더 중요한 원칙은 샤드 하나의 크기를 일정 기준 이하로 유지해야한다는 것입니다. 

전체 샤드의 수를 체크 하는 것은 그 다음입니다. 

샤드의 크기를 기준 크기 이하로 유지하는 선에서 전체 샤드 개수를 최대한 줄이는 방향으로 접근 해야합니다. 그렇다면 '너무 크다'의 기준은 얼마나 되어야 할까요?? ( 엘라스틱 블로그 참조 : 샤드 당 20GB~ 40GB 크기가 적절하다고 말하고있음), 그러나 실제 운영 경험상으로는 샤드 하나당 크기가 20GB 만 되어도 다양한 상황에서 느리고 무겁다라고 느껴집니다. 

블로그에서 말한 크기의 샤드 또한 버틸 수 있을지 모르지만, 수 GB 내외 수준에서 조정하는 것을 추천드립니다. 

샤드의 크기는 cat shards API 를 이용해서 확인 할 수 있습니다. 

GET _cat/shards?v&s=store:desc

또한 블로그에서 힙 1GB 당 20개 이하의 샤드를 들고있는 것이 적절하다고 설명하고 약 32GB 힙 기준으로 노드당 640 샤드 이하다.

샤드 개수 확인 API

GET _cat/health?v
GET _cat/allocation?v

힙 1GB 당 샤드 20개 기준은 빡빡하기는 하나 이 기준을 지키는 것을 어느 정도 이상적인 목표로 삼으며 밸런스를 조정하면 좋다. 

그러나 이 기준만으로 샤드 개수를 결정할 수는 없다. 샤드 개수 결정에 고려하면 좋은것을 더 알아보도록 하자.

모든 노드가 충분히 일을 하고 있는지...?

ES 의 샤드는 이름 자체가 의미하듯 샤딩, 즉 분산처리를 위해 생긴 개념이다. 

전체 샤드 개수를 줄이는 것만 생각하다 보면 분산처리의 강점을 충분히 살리지 못할 수도 있다. 특정인덱스의 주 샤드와 복제본 샤드가 모든 노드에 고르게 퍼지도록 설정하는 것도 중요한 요소다. 노드 대수가 n대라면 number_of_shards를 n의 배수로 지정해 모든 노드가 작업을 고르게 분산받도록 설정하는 방법 등의 사용된다. 특히 서비스 중요도가 높은 인덱스나 성능을 타이트하게 조정해야 하는 인덱스라면 이 부분을 신경 써야 한다. 

 

서비스상 빈번하게 호출되지 않느 ㄴ인덱스나 크기가 너무 작은 인덱스의 샤드를 모든 노드에 배치 시킬 필요는 없다. 

n의 배수에도 크게 집착하지 않아도 된다. 어차피 추후 선형적 확장을 위해 추가 서버가 투입되면 이 숫자가 달라지게 된다. 또한 한 엘라스틱서치 클러스터에서 활발하게 작업 중이 ㄴ인덱스의 수가 이미 충분히 많다면 단일 인덱스에 대해 모든 노드가 일을 하고 있는지를 과도하게 신경 쓸 필요가 없을 수도 있다. 한 인덱스의 작업에 참혀하지 않는 노드이더라도 다른 인덱스의 작업에 리소스를 사용할 것이기 때문이다. 서비스 중요도와 함께 샤드의 크기, 개수 등의 요소를 함께 적절히 고려한 중용이 필요하다. 

 

미래에 데이터가 커질 것을 고려

지금은 데이터가 초당 n개 씩 들어오지만 한두 달 사이에 초당 10n 개의 데이터가 인입될 수 도 있다. 서비스 요건상 데이터의 성질이 변경되어 새로운 필드가 많이 추가되거나 데이터 크기 자체가 커질 수 있다. 이런 운영상의 이슈로 인해 미래에 커질 수 있는 데이터 사이즈를 어느 정도 고려해서 설정해야 한다. 

 

인덱스 이름에 시간값을 넣어 정기적으로 신규 인덱스를 생성하는 성질의 데이터는 템플릿에 설정된 number_of_shards 값을 봐 가면서 변경하면 이런 문제를 어느 정도 유연하게 대처할 수 있다. 

그러나 단일 인덱스에 서비스 데이터를 담고 있다면 그렇게 하기는 어렵다. 이런 경우라면 미레에 커질 데이터 사이즈를 넉넉히 예상해서 설정해 두는 것이 좋다. 그렇더라도 버틸 수 없는 상태 까지 오면 결국 reindex를 할 수 밖에 없다. reindex를 위해서 미리 alias 를 설정해 두는 것을 잊지 않아야한다. 

 

테스트 수행

앞선 요소들은 단순하게 일률적으로 적용될 만한 것은 아미녀 서비스 환경마다 상황이 다르다. 즉 엘라스틱 블로그에 실힌 힙 1gb당 샤드 2-개라는 가이드 또한 데이터 특성을 전혀 고려하지 않는 숫자입니다. 실제로는 인덱스에 어떤 성격의 데이터가 얼마나 있는지에 따라 양상이 매우 다릅니다. 여러 통제된 조건에서 테스트를 해 보고 결정하는 것이 좋습니다. 

특히 성능에 민감한 인덱스라면 테스트가 더더욱 중요합니다. 

가능하다면 실제 서비스 조건과 유사한 조건으로 테스트를 수행해 보고 그 결과를 참고해 개수를 지정하도록 하자.

물론 문제를 많이 일으키는 인덱스는 데이터 양과 특성이 자주 바뀌는 인덱스인 경우가 많다. 

다양한 요소를 잘 고려해서 종합적으로 결정해야 합니다. 

 

728x90

+ Recent posts