[OpenSearch의 인덱스와 샤드 관리] Shard allocation과 Rerouting

Hyunjun Kim·2025년 7월 31일
0

Data_Engineering

목록 보기
110/153

index는 여러 개의 shard로 구성되어 있는데, 각 shard 마다 복제본을 다른 노드에 위치시켜서 어떤 물리적인 노드가 죽었을 때 자동으로 데이터 유실 없이 복구가 되고 여러 개의 복제본과 여러 개의 샤드에 대해서 shard라우팅 과정을 통해 여러군데 나눠져 있는 데이터를 찾을 수 있다.

shard 자동으로 할당되는 예제를 배웠었는데
우리가 어떻게 수동으로 변경할 수 있는지, shard 변경될 때 어떤 동작들이 일어나고
그와 관련되어서 우리가 신경써야되는 건 뭔지 알아보자.

4.1 샤드 할당(Shard allocation)과 재배치(Rerouting)

노드에 샤드를 할당하는 과정을 샤드 할당이라고 한다. 클러스터 매니저는 어떤 샤드를 어떤 노드에 할당하고, 언제 노드 간에 샤드 이동이 필요한지 결정한다. 샤드 할당은 다음과 같은 상황에서 발생한다.

  • 처음 인덱스를 생성했을 때
  • 인덱스의 레플리카 수를 변경했을 때
  • 클러스터에 노드가 추가되거나 노드가 클러스터를 이탈했을 때
    • 클러스터를 구성하는 노드의 수의 변경이 일어났을 때
  • 노드 간에 샤드 리밸런싱(Rebalancing)이 필요할 때
    • 예를 들어, 특정 노드에 샤드가 많이 배치되었을 때 자동으로 샤드 리밸런싱이 수행된다.
    • 특정노드 편중, skew

클러스터 설정에서 cluster.routing.allocation을 변경해서 샤드 할당 방식을 제어할 수 있다. cluster.routing.allocation 설정이 궁금하다면 공식 문서를 참고하면 된다. (https://opensearch.org/docs/latest/api-reference/cluster-settings/)

4.1.1 초기 샤드 할당

Elasticvue를 활용해서 test1 인덱스를 생성해보자. Elasticvue에서는 샤드 개수를 지정하지 않을 경우 기본적으로 프라이머리 샤드 1개, 프라이머리 샤드 당 레플리카 샤드 1개가 생성된다.

test1 인덱스는 데이터 노드 2개에 걸쳐 저장됐고, 프라이머리 샤드와 레플리카 샤드 쌍은 서로 다른 데이터 노드에 배치된 것을 확인할 수 있다.

4.1.2 인덱스와 샤드 상태 확인

프라이머리 샤드 5개, 프라이머리 샤드 당 레플리카 샤드 5개로 test2 인덱스를 생성해보자. 총 30개의 샤드가 생성될 것이다.

프라이머리 샤드와 레플리카 샤드 쌍이 서로 다른 데이터 노드에 배치된 것을 확인할 수 있다.

그런데 unassigned는 무엇일까? 샤드의 상태는 다음과 같이 크게 4가지로 구분할 수 있다.

  1. unassigned: 샤드가 어떤 노드에도 할당되지 않은 상태
  2. initializing: 샤드를 노드 메모리에 적재중인 상태 (메모리에 모두 적재된 것이 아니므로, 샤드 사용 불가능)
  3. started: 샤드가 메모리에 모두 적재되어 사용 가능한 상태 (프라이머리 샤드가 started 상태가 되어야 레플리카 샤드 할당 가능)
  4. relocating: 샤드가 다른 노드로 재배치중인 상태

샤드의 상태에 따라 인덱스의 상태 또한 결정된다. Elasticvue의 INDICES 탭에서 Health 컬럼을 보면 인덱스의 상태를 확인할 수 있다.

  • red: 한 개 이상의 프라이머리 샤드가 unassigned 된 경우 (인덱스 읽기/쓰기 모두 불가능)
  • yellow: 프라이머리 샤드는 모두 정상적으로 노드에 할당되었지만, 한 개 이상의 레플리카 샤드가 unassinged 된 경우 (인덱스 읽기/쓰기 모두 가능하지만, 장애 발생시 인덱스를 사용하지 못할 수 있음)
  • green: 프라이머리 샤드와 레플리카 샤드 모두 정상적으로 노드에 할당된 경우

test2 인덱스의 경우 프라이머리 샤드는 모두 정상적으로 노드에 할당되었기 때문에 yellow 상태인 것을 확인할 수 있다.

4.1.3 샤드 할당에 대한 정보 확인

test2 인덱스의 레플리카 샤드 일부가 정상적으로 노드에 할당되지 못한 이유는 무엇일까? GET /_cluster/allocation/explain API로 샤드가 할당되지 않은 이유를 알 수 있다. Elasticvue REST 클라이언트를 활용해서 다음 API를 호출해보자.

$ curl -XGET $OPENSEARCH_REST_API/_cluster/allocation/explain?pretty=true

explanation을 보면 a copy of this shard is already allocated to this node [[test2][3], node[OpwmNmeiQSCd19yKmZfKfQ], [R], s[STARTED], a[id=_FKc6P1oRuKDZqO6Bpccyw]]라고 설명하고 있다. 즉 프라이머리 샤드의 레플리카 샤드가 노드에 이미 존재하는 경우, 동일한 노드에 또 다른 레플리카 샤드를 할당할 수 없다는 뜻이다. OpenSearch가 프라이머리 샤드와 동일한 노드에 레플리카 샤드를 할당하지 않고, 동일한 노드에 동일한 프라이머리 샤드의 복제본을 두 개 이상 할당하지 않기 때문이다.

4.1.4 unassigned 샤드 문제 해결

4.1.3과 같은 이유로 발생한 unassigned 샤드 문제는 두 가지 방법으로 해결할 수 있다.

  1. 인덱스의 레플리카 수를 줄여서 unassigned 샤드 제거
  2. 클러스터에 노드를 추가해서 unassigned 샤드를 새로운 노드로 재배치

먼저 인덱스의 레플리카 수를 줄여서 문제를 해결해보자. 다음 요청으로 test2 인덱스의 레플리카 샤드 개수를 프라이머리 샤드 당 1개로 변경할 수 있다.

$ curl -XPUT "$OPENSEARCH_REST_API/test2/_settings?pretty=true" \
	-H "Content-Type: application/json" \
	-d '
{
    "number_of_replicas": 1
}
'

unassigned 샤드가 사라지고, 인덱스의 상태도 green으로 변경된 것을 확인할 수 있다.

이번에는 클러스터에 데이터 노드를 추가해서 문제를 해결해보자. 먼저 test2 인덱스의 레플리카 샤드 개수를 프라이머리 샤드 당 2개로 늘려서 문제 상황을 재현해보자.

$ curl -XPUT "$OPENSEARCH_REST_API/test2/_settings?pretty=true" \
	-H "Content-Type: application/json" \
	-d '
{
    "number_of_replicas": 2
}
'

총 5개의 레플리카 샤드가 unassigned 상태인 것을 확인할 수 있다.

AMI를 활용해서 EC2 인스턴스를 1개 더 생성하고, OpenSearch config를 다음과 같이 수정해주자.

plugins.security.disabled: true
cluster.name: opensearch-cluster
node.name: opensearch-d3
node.roles: [ data, ingest ]
network.host: 0.0.0.0
discovery.seed_hosts: ["<CLUSTER_MANAGER_NODE_PUBLIC_IP>"]

노드를 재시작하고 Elasticvue를 확인해보면 클러스터에 opensearch-d3 노드가 추가된 것을 확인할 수 있다.

unassigned 상태였던 모든 레플리카 샤드가 opensearch-d3 노드에 자동으로 재배치되면서 인덱스 상태도 green으로 정상화되었다.

4.1.5 수동 샤드 재배치

앞서 본 것처럼 기본적으로 OpenSearch 클러스터는 샤드 할당과 재배치를 자동으로 수행한다. 수동으로 샤드를 재배치하고 싶다면 reroute API를 사용하면 된다. 다음 요청으로 test1 인덱스의 프라이머리 샤드를 opensearch-d2 노드에서 opensearch-d3 노드로 재배치해보자.

$ curl -XPOST "$OPENSEARCH_REST_API/_cluster/reroute?pretty=true" \
	-H "Content-Type: application/json" \
	-d '
{
	"commands": [
		{
			"move": {
				"index": "test1",
				"shard": 0,
				"from_node": "opensearch-d2",
				"to_node": "opensearch-d3"
			}
		}
	]
}
'

응답 본문의 state.routing_table.indices.test1.shards.0을 보면 프라이머리 샤드가 relocating 상태로 바뀐 것을 확인할 수 있다.

[
	{
		"state": "RELOCATING",
		"primary": true,
		"node": "OpwmNmeiQSCd19yKmZfKfQ",
		"relocating_node": "m0-xttC6SK6V7e34R5IOSw",
		"shard": 0,
		"index": "test1",
		"expected_shard_size_in_bytes": 4327,
		"allocation_id": {
			"id": "y0nEcSbfSiGlXKPclHu7Hg",
			"relocation_id": "pvTwn9XzRhyJpOt_x6SiZQ"
		}
	},
	{
		"state": "STARTED",
		"primary": false,
		"node": "2usKiujOSGSH5JwEYRFGrA",
		"relocating_node": null,
		"shard": 0,
		"index": "test1",
		"allocation_id": {
			"id": "cxX3hEamRsGfFF95zhHoEg"
		}
	}
]

성공적으로 샤드가 재배치될 경우 샤드 상태는 started로 바뀐다.

다음과 같이 Elasticvue에서 GUI로 샤드를 재배치할 수도 있다.

4.1.6 레플리카 샤드 승격(Shard promotion)

의도적이든 아니든 어떤 이유로든 노드가 클러스터를 이탈하게 되면, 클러스터 매니저는 다음과 같은 과정을 수행한다.

  1. 레플리카 샤드를 프라이머리 샤드로 승격시켜서 이탈한 노드에 있던 프라이머리 샤드를 대체하도록 한다.
  2. 새로운 레플리카 샤드를 할당해서 부족한 레플리카 샤드 개수를 채운다. (충분한 노드가 있다는 가정)
  3. 샤드를 리밸런싱해서 모든 노드에 고르게 분포되도록 한다.

위와 같은 과정 덕분에 노드가 클러스터를 이탈하더라도 모든 샤드를 최대한 빨리 복제 및 복구하여 클러스터의 데이터 손실을 막을 수 있다. 그러나 2~3에 해당하는 샤드 셔플(Shard shuffle)은 클러스터에 큰 부하를 주는 작업이므로, 이탈한 노드가 바로 클러스터에 복귀하는 상황을 가정해서 일정 시간 샤드 셔플을 미루는 것이 좋다. index.unassigned.node_left.delayed_timeout을 설정해서 샤드 셔플을 미룰 수 있으며, 기본 설정은 1m이다. delayed_timeout은 레플리카 샤드를 프라이머리 샤드로 승격하는 것에는 영향을 주지 않는다.

다음과 같은 상태에서 opensearch-d2 노드를 멈춰보자. EC2 인스턴스를 stop하거나, $ sudo systemctl stop opensearch.service를 실행하면 된다.

노드가 이탈한 즉시 opensearch-d3의 레플리카 샤드 일부가 프라이머리 샤드로 승격된 것을 확인할 수 있다. delayed_timeout 설정 때문에 unassigned 레플리카 샤드는 아직 재배치되지 않았다.

기본 delayed_timeout 설정대로 약 1분 정도 기다리면 test1 인덱스의 unassigned 레플리카 샤드가 opensearch-d3로 재배치된 것을 확인할 수 있다. test2 인덱스의 unassigned 레플리카 샤드는 노드 부족으로 인해 재배치되지 못했다.

opensearch-d2 노드를 클러스터에 복귀시켜보자. test2 인덱스의 unassigned 레플리카 샤드가 opensearch-d2 노드에 재배치된 것을 확인할 수 있다.

이번에는 opensearch-d3 노드를 멈추고, 1분 안에 클러스터에 복귀시켜보자. test1 인덱스의 unassigned 레플리카 샤드가 opensearch-d3 노드에 재배치된 것을 확인할 수 있다. opensearch-d3 노드에는 이미 test1 인덱스의 0번 샤드 정보가 디스크에 저장되어 있기 때문에 opensearch-d2 노드보다 더 빠르게 레플리카 샤드를 재배치할 수 있다.

profile
Data Analytics Engineer 가 되

0개의 댓글