엘라스틱 서치를 사용하면서 약간 잊혀져가던 기본 개념을 다시 되새기기 위하여 엘라스틱 서치 서적을 구매 하여 읽고 내용을 정리하였습니다.
1. 클러스터와 노드의 개념
클러스터란?
여러 대의 컴퓨터와 혹은 구성 요소들을 논리적으로 결합하여 전체를 하나의 컴퓨터 혹은 하나의 구성 요소처럼 사용할 수 있게 해주는 기술입니다.
Elasticsearch에서의 클러스터
Elasticsearch 클러스터 역시 여러 개의 ElasticSearch 프로세들을 논리적으로 결합하여 하나의 ElasticSearch 프로세스처럼 사용할 수 있게 해줍니다.
Elasticsearch에서의 노드
클러스터를 구성하는 하나하나의 ElasticSearch 프로세스를 노드라고 부른다, 즉 ElasitcSearch의 클러스터란 이 노드들을 하나의 ElasticSearch 프로세스처럼 동작하게 하는 것이 클러스터입니다.
클러스터의 특징
클러스터는 보통 단일 노드로 구성하지 않습니다, 클러스터의 주 목적은 노드에 장애가 발생하였을때 ElasticSearch 에 접근할 수 없는 요청 불가 상태를 방지하기 위함이며, 여러 노드로 클러스터를 구성하였다면 하나의 노드에 장애가 발생해도 다른 노드에 요청할 수 있기에 안정적으로 서비스를 유지할 수 있습니다. 그렇기 때문에 다수의 노드로 구성하는 것이 바람직하며 이렇게 다수 노드로 구성된 클러스터는 고유의 클러스터 이름과, UUID를 가집니다.
이 고유한 두 속성을 통해 클러스터 내에 속한 노드들이 서로 동일한 클러스터 내에 있음을 인식하게 됩니다. ElasticSearch에 아래와 같은 요청을 보내면 클러스터의 이름과 UUID를 확인할 수 있습니다.
// curl localhost:9200 엘라스틱 서치 서비스가 가동중인 포트 번호로 요청을 보냅니다.
{
"name" : "elasticsearch",
"cluster_name" : "my-elasticsearch",
"cluster_uuid" : "68Dns~~~~",
"version" : {
//~~~
}
}
-
name 클러스터에 속한 노드들 중 현재 요청에 응답한 노드의 이름입니다 클러스터가 다수의 노드로 구성되어 있다면 노드별로 값이 달라집니다
-
cluster_name 클러스터의 이름입니다. 클러스터에 속한 모든 노드들은 클러스터 이름이 같습니다. 새로운 노드를 클러스터에 추가하기 위해서는 같은 클러스터 이름을 사용해야합니다.
-
cluster_uuid 클러스터의 UUID입니다. 클러스터에 속한 모든 노드들이 동일한 값을 같습니다. 이 값은 클러스터가 최초 생성될 때 자동으로 생성됩니다.
노드의 특징
노드는 클러스터를 구성하는 논리적인 ElasticSearch 프로세스 하나를 의미합니다 노드도 클러스터와 마찬가지로 각각의 고유한 노드 이름과 uuid가 있고, 역할에 따라 여러 가지 노드로 구분할 수 있습니다 노드의 역할은 아래 표와 같습니다.
노드 역할 | 설명 |
---|---|
마스터 | 클러스터 구성에서 중심이 되는 노드. 클러스터의 상태 등 메타데이터를 관리합니다. |
데이터 | 사용자의 문서를 실제로 저장하는 노드 |
인제스트 | 사용자의 문서가 저장되기 전 문서 내용을 사전 처리하는 노드 |
코디네이트 | 사용자의 요청을 데이터 노드로 전달하고, 다시 데이터 노드로부터 결과를 취합하는 노드 |
위 표와 같이 노드가 할 수 있는 역할은 총 4가지이며, 각각 하나의 역할만 할 수 있는게 아니라 한 번에 여러 개의 역할을 할 수 있습니다. 각각의 역할을 조금 더 자세히 살펴봅시다.
마스터 노드
클러스터의 메타데이터를 관리하는 역할을 하며 반드시 한 대 이상으로 구성해야합니다. 클러스터 내의 모든 노드는 현재 노드의 상태, 성능 정보, 자신이 가지고 있는 샤드의 정보를 마스터 노드에 알립니다. 마스터 노드는 이런 정보들을 수집하고 관리하면서 클러스터의 안정성을 확보하기 위해 필요한 작업들을 진행합니다.
데이터 노드
사용자가 색인한 문서를 저장하고, 검색 요청을 처리해서 결과를 돌려주는 역할을 합니다. 또한 자신이 받은 요청 중 자신이 처리할 수 있는 요청은 직접 처리하고, 다른 데이터 노드들이 처리해야 할 요청은 해당 데이터 노드에 전달합니다. 이때 어떤 데이터 노드로 요청을 전달할 것인지는 마스터 노드를 통해 받은 클러스터의 전체 상태 정보를 바탕으로 합니다.
인제스트 노드
사용자가 색인하길 원하는 문서의 내용 중 변환이 필요한 부분을 사전에 처리합니다. 데이터 노드에 저장하기 전에 특정 필드의 값을 가공해야할 경우 유용하게 동작합니다.
코디네이트 노드
실제 데이터를 저장하고 처리하지는 않지만, 사용자의 색인이나 검색 등 모든 요청을 데이터 노드에 전달하는 역할을 합니다..
클러스터를 운용하면서 꼭 유념할 부분이 있습니다. 클러스터 내에서 메타데이터를 관리하는 마스터 노드는 한 대라는 것 입니다. 헷갈릴 수 있는 부분이
- 마스터 노드 역할이 가능한 후보 노드
- 마스터 노드 역할을 하는 노드 위와 같이 두 개념이 있기 때문에 헷갈릴 수 있습니다.
만약 클러스터를 구성할 때 총 3개의 노드로 구성을 하고 3개의 노드를 모두 마스터 역할이 가능한 후보 노드 로 구성한다면, 마스터 노드 역할을 하고있는 노드에 장애가 발생하였을 때 나머지 2개의 마스터 노드 역할이 가능한 후보 노드 중 한대가 마스터 노드의 역할을 하게됩니다.
마스터 노드 변경이 가능한 이유
마스터 후보 노드들은 마스터 노드로부터 지속적으로 클러스터 운영에 필요한 데이터를 전달받기 때문에 항상 같은 메타 데이터를 유지하고 있습니다. 그래서 마스터 노드에 장애가 발생하여 마스터 노드가 변경되어도 중단 없이 서비스를 이어갈 수 있습니다.
요약
- 하나의 ElasticSearch 프로세스 단위
- 마스터, 데이터, 인제스트, 코디네이트 총 4가지 역할이 있음
- 하나의 노드가 동시에 여러 역할을 수행할 수 있음
- 마스터 노드 장애발생 시 마스터 후보 노드 중 하나의 노드가 마스터 노드로 선출됨
2. 샤드와 세그먼트
샤드란?
샤드는 인덱스에 색인되는 문서들이 저장되는 논리적인 공간을 의미합니다.
세그먼트란?
세그먼트는 샤드의 데이터들을 가지고 있는 물리적인 파일을 의미합니다.
하나의 인덱스는 다수의 샤드로 구성되고 하나의 샤드는 다수의 세그먼트로 구성됩니다. 단순 크기 순으로 본다면 인덱스 > 샤드 > 세그먼트 순입니다. 샤드는 1개 이상의 세그먼트로 구성되는데 샤드마다 세그먼트의 개수는 서로 다를 수 있습니다. 만약 5개의 샤드로 구성된 인덱스라면 실제 문서들은 각각의 샤드에 나뉘어 저장됩니다.
세그먼트가 생성되는 과정
ElasticSearch 에 데이터 입력 요청이 들어오면 문서 색인과정을 거칩니다. 그 이후에 이 색인된 문서는 메모리 버퍼 에 먼저 저장되는데 이 순간에는 문서가 검색되지 않습니다. 메모리 버퍼에 저장된 색인된 문서는 refresh 라는 과정을 거쳐야 비로소 디스크에 세그먼트 단위로 저장되고 검색이 가능하게됩니다.
세그먼트는 불변(immutable)하다
세그먼트는 기존에 기록한 데이터를 업데이트하지 않는 특성이 있습니다.
예를들어 이미 등록되어 있는 문서를 “PUT” 을 통하여 업데이트 한다 하였을 때 저희가 생각하는 것 처럼 기존 데이터에서 일부만 변경되서 업데이트 되는 것이 아닙니다.
먼저 아래의 이미지를 보겠습니다.
데이터를 업데이트하려고 시도하면 ElasticSearch는 새로운 세그먼트에 업데이트할 문서의 내용을 새롭게 쓰고, 기존의 데이터는 더 이상 쓰지 못하게 불용 처리합니다.
이러한 동작은 update 뿐 아니라 delete 도 마찬가지로 사용자가 문서를 삭제하기 위해 delete를 시도하면 바로 지우지 않고 불용 처리만 합니다.
세그먼트 병합
ElasticSearch는 최초 색인이 이루어지면 새로운 세그먼트를 생성하게되는대, 이렇게 되면 매번 신규 문서가 추가될 때 마다 세그먼트가 생겨나서 시간이 지나면 너무 많은 세그먼트들이 생성됩니다.
세그먼트가 많아지면 문서 검색시 그만큼 많은 세그먼트들이 응답을 해야하고, 불용 처리된 데이터도 실제로 지워지지 않고 가지고 있기 때문에 용량을 차지하게 됩니다.
이러한 문제점을 보완하기 위해 ElasticSearch는 백그라운드에서 세그먼트 병합을 진행합니다.
이 병합 과정에서 작은 세그먼트들이 하나의 큰 세그먼트로 합쳐지고, 불용 처리된 데이터를 실제로 디스크에서 삭제합니다.
3. 프라이머리 샤드와 레플리카 샤드
앞서 살펴본 것 과 같이 ElasticSearch 는 인덱스를 샤드로 나누고 나뉘어진 샤드에 세그먼트 단위로 문서를 저장합니다.
그렇기 때문에 샤드의 상태를 정상적으로 유지하고 장애 상황에서도 유실되지 않도록 관리하는 것은 ElasticSearch 클러스터 서비스의 연속성을 유지하기 위해 꼭 필요합니다.
이를 위하여 ElasticSearch는 프라이머리 샤드 와 레플리카 샤드 로 샤드를 나눠서 관리합니다.
프라이머리 샤드
ElasticSearch 에서 인덱스를 생성할 때 프라이머리 샤드의 개수를 필수적으로 설정해야합니다.
만약, 하나의 클러스터에 5개의 노드가 존재하고 5개의 프라이머리 샤드로 인덱스를 생성한다면, 각각의 데이터 노드에 샤드가 균등하게 배분됩니다.
그 후 사용자가 5개의 문서를 색인하면 5개의 샤드에 균등하게 저장됩니다. 각각의 샤드들은 생성 될 때 샤드 번호를 부여받고 이 번호는 0번 부터 시작됩니다.
프라이머리 샤드가 인덱스 생성 후 개수 변경이 안되는 이유
ElasticSearch가 문서를 샤드에 저장할때 샤드 번호를 지정하게 되는 알고리즘은 아래와 같습니다.
프라이머리 샤드 번호 = Hash(문서의 id) % 프라이머리 샤드 개수
문서에 할당된 id를 기준으로 ElasticSearch에서 샤드 할당 해시 함수를 이용해 몇 번째 프라이머리 샤드에 저장할지 결정합니다. 이러한 알고리즘을 사용하기 때문에 프라이머리 샤드 번호는 0번부터 사용자가 지정한 샤드 개수 -1 사이의 번호를 부여받습니다. 예를 들어, 프라이머리 샤드를 5개로 설정하였다면 프라이머리 샤드의 번혼는 0, 1, 2, 3, 4 로 5개가 생성됩니다. 바로 이 로직으로 인하여 인덱스 생성 후에는 프라이머리 샤드의 개수를 변경할 수 없습니다.
레플리카 샤드
만약 5개의 노드로 이루어진 클러스터에서 1개의 노드가 장애로 인하여 다운된다고 상상해봅시다.
해당 노드에 저장되어있는 문서들은 조회가 불가능한 상태가 될텐대 이를 방지하고자 레플리카 샤드가 존재합니다. 레플리카 샤드는 프라이머리 샤드의 복제본으로, ElasticSearch는 프라이머리 샤드에 장애가 발생하면 이 레플리카 샤드를 프라이머리 샤드로 승격 하여 정상적으로 조회가 되도록 합니다.
그렇기 때문에 한 노드에 동일한 프라이머리 샤드 번호를 복제본으로 하는 레플리카 샤드는 존재하지 않습니다. 세그먼트란? 항목에 첨부된 이미지를 참고하면 좀 더 이해가 쉽습니다.
승격하면서 사라진 레플리카 샤드와 장애로 인하여 사라진 레플리카 샤드는 살아있는 원본 프라이머리 샤드를 기반으로 다른 노드에 새롭게 생성됩니다.