Life with AI/Research with AI

[Ceph Storage] CRUSH 알고리즘과 CRUSH Map

타임-세이버 2023. 2. 23. 00:07
반응형

[Ceph Storage] CRUSH 알고리즘과 CRUSH Map

Ceph는 오픈소스 분산 스토리지 시스템으로, 객체, 블록, 파일 시스템 등 다양한 형태의 데이터를 저장하고 관리할 수 있습니다. Ceph의 중요한 구성 요소 중 하나는 CRUSH 알고리즘과 CRUSH Map입니다. 이번에는 Ceph의 CRUSH Map에 대해 자세히 살펴보겠습니다.

CRUSH 알고리즘

CRUSH 알고리즘은 Ceph에서 사용하는 분산 데이터 복제 및 위치 결정 알고리즘입니다. CRUSH 알고리즘은 데이터를 저장하는 OSD (Object Storage Device)의 위치를 결정하는 데 사용됩니다. 이를 통해 데이터를 안정적으로 분산 저장하고, 데이터의 안전성 및 가용성을 보장합니다. CRUSH 알고리즘은 단순한 해시 알고리즘보다 효율적이며, 데이터 복제 및 복구에 있어서도 우수한 성능을 보입니다.

CRUSH 알고리즘은 데이터를 저장할 수 있는 OSD의 위치를 결정하기 위해, 일련의 규칙과 매핑을 사용합니다. 이러한 규칙과 매핑은 CRUSH Map을 통해 정의됩니다.

CRUSH Map

CRUSH Map은 Ceph 클러스터의 물리적인 노드 및 OSD의 계층 구조와 관련된 정보를 담고 있는 데이터 구조입니다. CRUSH Map은 OSD의 위치 결정 및 데이터 복제, 데이터 이동 등에 사용됩니다. CRUSH Map은 JSON 형식으로 정의되며, 일반적으로는 "ceph osd crush dump" 명령어를 사용하여 확인할 수 있습니다.

CRUSH Map은 다음과 같은 구성 요소로 이루어져 있습니다.

Bucket

Bucket은 OSD 그룹화를 위한 가상 노드입니다. Bucket은 다른 Bucket 또는 OSD를 자식으로 가질 수 있습니다. Bucket은 다양한 형태로 정의될 수 있으며, 다음과 같은 유형이 있습니다.

  • Root Bucket: 전체 클러스터를 대표하는 가상 노드입니다.
  • Failure Domain Bucket: 장애 도메인을 정의하는 가상 노드입니다. 예를 들어, 랙, 샤드 등의 물리적인 장애 도메인을 나타낼 수 있습니다.
  • Device Class Bucket: 장치 유형을 정의하는 가상 노드입니다. 예를 들어, SSD, HDD 등의 장치 유형을 나타낼 수 있습니다.

Bucket은 다음과 같이 JSON 형식으로 정의됩니다.

 

{
    "id": <bucket id>,
    "type": <bucket type>,
    "name": <bucket name>,
    "weight": <bucket weight>,
    "alg": <bucket 알고리즘>,
    "hash": <bucket 해시 알고리즘>,
    "items": [
        <child bucket or OSD>,
        ...
    ]
}
  • id: Bucket의 고유 식별자입니다. 모든 Bucket과 OSD는 고유한 ID를 가집니다.
  • type: Bucket의 종류를 나타냅니다. 예를 들어, root, host, rack 등이 있습니다.
  • name: Bucket의 이름입니다.
  • weight: Bucket의 가중치입니다. Bucket의 크기에 비례합니다.
  • alg: Bucket 내의 OSD 배치 알고리즘입니다. 예를 들어, straw2, uniform 등이 있습니다.
  • hash: Bucket 내의 OSD 선택을 위한 해시 알고리즘입니다. 예를 들어, rjenkins1, sha1 등이 있습니다.
  • items: Bucket이 가지고 있는 자식 Bucket 또는 OSD의 목록입니다.

OSD

OSD는 Object Storage Device의 약자로, 데이터를 저장하고 관리하는 단위입니다. OSD는 다음과 같이 JSON 형식으로 정의됩니다.

{
    "id": <OSD id>,
    "weight": <OSD weight>,
    "crush_location": {
        "<bucket type>": "<location>",
        ...
    }
}
  • id: OSD의 고유 식별자입니다. 모든 OSD는 고유한 ID를 가집니다.
  • weight: OSD의 가중치입니다. OSD의 크기에 비례합니다.
  • crush_location: OSD가 속한 Bucket의 위치 정보입니다. Bucket type과 위치를 쌍으로 저장합니다.

Rule

Rule은 데이터의 복제 규칙을 정의하는 구성 요소입니다. Rule은 다음과 같이 JSON 형식으로 정의됩니다.

 

{
    "rule_id": <rule id>,
    "rule_name": "<rule name>",
    "ruleset": <ruleset id>,
    "type": <replication type>,
    "min_size": <minimum number of replicas>,
    "max_size": <maximum number of replicas>,
    "steps": [
        ...
    ]
}
  • rule_id: Rule의 고유 식별자입니다.
  • rule_name: Rule의 이름입니다.
  • ruleset: Rule이 적용되는 ruleset의 ID입니다.
  • type: 데이터의 복제 방식을 나타냅니다. 예를 들어, replicated, erasure coded 등이 있습니다.
  • min_size: 최소 복제 수입니다.
  • max_size: 최대 복제 수입니다.
  • steps: 데이터의 위치 결정을 위한 매핑 단계입니다. 각 매핑 단계는 Bucket ID, Bucket type, Bucket 내 OSD 배치 알고리즘 등의 정보를 포함합니다.

예시

다음은 Ceph의 CRUSH Map 예시입니다.

{
    "nodes": [
        {
            "id": -1,
            "name": "default",
            "type": "root",
            "children": [
                {
                    "id": -2,
                    "name": "zone1",
                    "type": "zone",
                    "children": [
                        {
                            "id": -3,
                            "name": "host1",
                            "type": "host",
                            "children": [
                                {
                                    "id": 0,
                                    "name": "osd.0",
                                    "weight": 1.0
                                },
                                {
                                    "id": 1,
                                    "name": "osd.1",
                                    "weight": 1.0
                                }
                            ]
                        },
                        {
                            "id": -4,
                            "name": "host2",
                            "type": "host",
                            "children": [
                                {
                                    "id": 2,
                                    "name": "osd.2",
                                    "weight": 1.0
                                },
                                {
                                    "id": 3,
                                    "name": "osd.3",
                                    "weight": 1.0
                                }
                            ]
                        }
                    ]
                },
                {
                    "id": -5,
                    "name": "zone2",
                    "type": "zone",
                    "children": [
                        {
                            "id": -6,
                            "name": "host3",
                            "type": "host",
                            "children": [
                                {
                                    "id": 4,
                                    "name": "osd.4",
                                    "weight": 1.0
                                },
                                {
                                    "id": 5,
                                    "name": "osd.5",
                                    "weight": 1.0
                                }
                            ]
                        },
                        {
                            "id": -7,
                            "name": "host4",
                            "type": "host",
                            "children": [
                                {
                                    "id": 6,
                                    "name": "osd.6",
                                    "weight": 1.0
                                },
                                {
                                    "id": 7,
                                    "name": "osd.7",
                                    "weight": 1.0
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ],
    "straw_calc_version": 1
}

위의 예시에서는 root Bucket이 하나 있으며, 그 하위에 두 개의 zone Bucket이 있습니다. 각 zone Bucket은 다시 두 개의 host Bucket을 가지고 있으며, 각 host Bucket은 두 개의 OSD를 가지고 있습니다.

이 CRUSH Map에서는 straw2 알고리즘을 사용하여 OSD를 배치합니다. 이 알고리즘은 OSD의 가중치와 Bucket의 가중치를 고려하여 OSD를 선택합니다. 각 OSD는 여러 Bucket에 속할 수 있으며, 이를 통해 데이터를 분산하여 저장할 수 있습니다.

또한, 위의 예시에서는 Rule도 정의되어 있습니다. 다음은 예시의 Rule입니다.

{
	"rules": [{
		"rule_id": 0,
		"rule_name": "replicated_rule",
		"ruleset": 0,
		"type": "replicated",
		"min_size": 1,
		"max_size": 10,
		"steps": [{
				"op": "take",
				"item": -1,
				"item_name": "root",
				"type": "osd",
				"chooseleaf_type": "rack",
				"chooseleaf_hash": "rjenkins1",
				"required_features": [
					"rack",
					"host"
				]
			},
			{
				"op": "emit"
			},
			{
				"op": "chooseleaf_firstn",
				"num": 2,
				"type": "osd",
				"chooseleaf_type": "indep",
				"chooseleaf_hash": "rjenkins1"
			},
			{
				"op": "emit"
			}
		]
	}],
	"types": [{
		"type_id": 0,
		"name": "osd"
	}],
	"buckets": [{
		"id": -1,
		"name": "root",
		"type_id": 0,
		"type_name": "osd",
		"children": [{
				"id": -2,
				"name": "zone1",
				"type_id": 0,
				"type_name": "osd",
				"children": [{
						"id": -3,
						"name": "host1",
						"type_id": 0,
						"type_name": "osd",
						"children": [{
								"id": 0,
								"name": "osd.0",
								"type_id": 0,
								"type_name": "osd",
								"weight": 1
							},
							{
								"id": 1,
								"name": "osd.1",
								"type_id": 0,
								"type_name": "osd",
								"weight": 1
							}
						]
					},
					{
						"id": -4,
						"name": "host2",
						"type_id": 0,
						"type_name": "osd",
						"children": [{
								"id": 2,
								"name": "osd.2",
								"type_id": 0,
								"type_name": "osd",
								"weight": 1
							},
							{
								"id": 3,
								"name": "osd.3",
								"type_id": 0,
								"type_name": "osd",
								"weight": 1
							}
						]
					}
				]
			},
			{
				"id": -5,
				"name": "zone2",
				"type_id": 0,
				"type_name": "osd",
				"children": [{
						"id": -6,
						"name": "host3",
						"type_id": 0,
						"type_name": "osd",
						"children": [{
								"id": 4,
								"name": "osd.4",
								"type_id": 0,
								"type_name": "osd",
								"weight": 1
							},
							{
								"id": 5,
								"name": "osd.5",
								"type_id": 0,
								"type_name": "osd",
								"weight": 1
							}
						]
					},
					{
						"id": -7,
						"name": "host4",
						"type_id": 0,
						"type_name": "osd",
						"children": [{
								"id": 6,
								"name": "osd.6",
								"type_id": 0,
								"type_name": "osd",
								"weight": 1
							},
							{
								"id": 7,
								"name": "osd.7",
								"type_id": 0,
								"type_name": "osd",
								"weight": 1
							}
						]
					}
				]
			}
		]
	}]
}

위의 Rule은 "replicated" 타입으로 정의되어 있으며, 2개의 OSD에 데이터를 복제하는 규칙입니다. 이 규칙은 ruleset 0에서 사용되며, 이 ruleset은 CRUSH Map에서 정의된 Bucket 계층 구조에서 데이터를 배치하는 데 사용됩니다. 따라서 이 Rule은 위에서 정의한 CRUSH Map의 Bucket 계층 구조와 관련이 있습니다.

Rule은 다음과 같은 세 가지 단계로 구성됩니다.

  1. take 단계: 데이터를 배치할 OSD를 선택합니다. 이 단계에서는 root Bucket에서 시작하여 각 Bucket의 하위 Bucket 중에서 데이터를 저장할 OSD를 선택합니다. 위 예시에서는 zone1, zone2, host1, host2, host3, host4 중에서 OSD를 선택할 수 있습니다.
  2. emit 단계: 데이터를 저장할 OSD를 선택한 후, 해당 OSD의 위치를 결정합니다. 이 단계에서는 선택된 OSD가 속한 Bucket 계층 구조에서 OSD의 위치를 결정합니다. 위 예시에서는 zone1, zone2, host1, host2, host3, host4 Bucket 중에서 선택된 OSD가 속한 Bucket을 결정합니다.
  3. chooseleaf 단계: 데이터를 저장할 OSD의 위치가 결정되면, 데이터를 저장할 OSD를 선택합니다. 이 단계에서는 선택된 OSD 중에서 가장 적합한 OSD를 선택합니다. 위 예시에서는 straw2 알고리즘을 사용하여 가장 적합한 OSD를 선택합니다.

위의 예시에서는 Rule이 하나 있지만, Ceph에서는 여러 Rule을 정의할 수 있습니다. 예를 들어, replication factor이 3인 Rule을 정의하여 3개의 OSD에 데이터를 복제할 수도 있습니다.

 

CRUSH Map은 Ceph에서 데이터를 분산하여 저장하는 데 필수적인 요소입니다. 이를 통해 Ceph는 데이터를 안정적으로 보관하면서도 데이터 복제 및 데이터 복구를 쉽게 수행할 수 있습니다. 또한 CRUSH Map을 조정하여 클러스터의 확장성을 개선할 수 있습니다. 따라서 Ceph를 사용하는 데 있어서 CRUSH Map을 이해하고 활용하는 것은 매우 중요합니다.

반응형