Apache Ozoneに関する雑感: メタデータ
諸事情でApache Ozoneに関する情報をまとめたくなり、自分用の備忘録として記載しておきます。特に内部実装について記載します。本記事は随時更新予定です。もし間違っていたらこっそり教えてください。
Apache Ozoneとは
Apache Ozoneは、Hadoopエコシステム向けに開発された、スケーラブルで冗長性のある分散オブジェクトストアです。従来のHDFSが抱えていた、NameNodeのスケールアップ限界による小ファイル問題やオブジェクト数上限などの課題を解決するために生まれました。
主な特徴は以下の通りです。
- 高いスケーラビリティ: 数十億個のオブジェクトまで拡張可能で、小ファイルと大ファイルの両方を効率的に管理できます。
- S3互換性: S3プロトコルをサポートしており、AWS CLIなどS3互換ツールやライブラリからアクセス可能です。これにより、Hadoop以外のシステムとの連携も容易になります。
- 高可用性: 複数のメタデータ管理コンポーネント(OMとSCM)を持ち、それぞれがRAFTコンセンサスアルゴリズムに基づいて内部状態を整合させているため、高い可用性を実現しています。
- コンテナ環境対応: YARNやKubernetesなどのコンテナ環境での動作をサポートしており、クラウドネイティブな環境にも適応可能です。
- 複数のAPIサポート: S3 APIに加えて、Hadoop File System APIなど、複数のAPIを通してアクセスできます。
Ozoneは、従来のHDFSに比べて柔軟性と拡張性に優れており、ビッグデータストレージの選択肢として注目されています。特に、大量のオブジェクトを扱う場合や、S3互換性を必要とする場合に有効です。
参考資料:
- S3互換ストレージ Apache Ozoneについて - MicroAd Developers Blog
- S3互換のオブジェクトストレージ Apache Ozoneに関する情報(随時更新) - Qiita
- Apache Hadoop 最新情報 | OSSサポートのOpenStandia™【NRI】
- Apache Ozoneをやっていた一年 - Preferred Networks Research & Development
- Apache Hadoop Ozone: オブジェクトストアの概要 - Cloudera株式会社 公式ブログ
本題
HDFSではNameNodeにメタデータ管理を集約させていたので、atomicかつ
ここで名前空間の管理とは、ディレクトリ情報やファイル情報に関するメタデータの管理のことである。またデータ管理とは、データの実体がどのワーカーにあるかのメタデータの管理である。HDFSでは両方をNameNodeが管理し、しかもそのメタデータをメモリに持っていた。
Ozoneでは前者をOzone Manager, 後者をStorage Container Managerが管理することにより分離した。また、HDFSではあるデータをそのまま一つのブロック(デフォルト: 256MB)としてワーカーに格納し、その情報をNameNodeが管理していたが、OzoneではContainerと呼ばれるもう少し大きい単位(デフォルト: 5GB)でStorage Container Managerが管理する。Container には複数のデータが格納されるが、その管理はワーカーが実施する。
つまり、OzoneではNameNodeが実施していた責務を複数のプロセスに分割することにより、NameNodeが持っていたスケーラビリティの問題を緩和した。
ここでは、Ozoneが具体的にどのようにオブジェクトストレージとして実装されていて、どのようにメタデータや実データを管理しているのかを見ていく。
用語定義
本記事では以下の表記を使う。また、特に断りのない限り、本記事の内容は Ozone v1.4.1に基づく。
- OM: Ozone Managerのこと
- SCM: Storage Container Managerのこと
- DN: DataNodeのこと。HDFSのDataNodeとは別なので注意(
なんで用語被らせるんだ・・・) - S3G: S3 Gatewayのこと
- Client: Ozoneに対して各種リクエストを出すプロセス。文脈によってSparkだったりOzone Shellのことだったりする
Ozoneを支える技術
Ozone内部では以下の技術を多用する。
- RocksDB: Ozoneのメタデータを格納する際には基本的にRocksDBを使う。
- Raft: OM-HA, SCM-HA, 実データのレプリケーションにRaftを利用する。実装としてはApache Ratisを利用する。
- gRPC: Ozone内での通信 (Client <=> S3G, Client <=> DNなど)に利用される。DN <=> DN通信はRatisが利用されるが、Ratisの通信も中身はデフォルト設定ではgRPCである。
- Hadoop IPC: Hadoopでは独自の通信プロトコルを利用する。OzoneはHadoop上のアプリケーションとの互換性を保つため、ClientとOM間の通信などにおいてHadoop IPCを利用する。
メタデータをどのように管理しているかを確認するためには、RocksDBの中身を見ることが簡単である。本記事ではRocksDBに書かれたメタデータと、それに関係するコードを確認する。また、コンポーネント間の通信も特徴的なので、どのような通信を行っているかを追うことはOzoneの理解の助けになる。通信については別の記事でまとめる。
下準備: RocksDBの見方
RocksDBに関する内容に興味がない場合は見る必要なし
RockDB はKey-Valueストア型の組み込み向けデータベースである。LSMツリー構造と呼ばれるデータ構造に基づいており、高速な書き込みが可能である。
RocksDBの中身を見るためには少し工夫が必要である。最も一般的な方法はRocksDBに付属する sst_dump
を利用することだろう。
RocksDBのinstall方法 を参照しビルドすると、sst_dump
というバイナリファイルが作成される。そのバイナリを使って、以下のようにすることでデータのダンプが可能である。(ここでは、 /tmp/metadata/om.db
以下にデータがあるものとする。)
$ ./sst_dump --file=/tmp/metadata/om.db/000053.sst --command=raw
options.env is 0x564209068d50
Process /tmp/metadata/om.db/000053.sst
Sst file format: block-based
raw dump written to file /tmp/metadata/om.db/000053_dump.txt
$ cat /tmp/metadata/om.db/000053_dump.txt
$ cat /tmp/metadata/om.db/000053_dump.txt
Footer Details:
--------------------------------------
metaindex handle: CB084F offset: 1099 size: 79
index handle: B80110 offset: 184 size: 16
table_magic_number: 9863518390377041911
format version: 5
Metaindex Details:
--------------------------------------
Filter block handle: 6E45
Properties block handle: CD01F906
Table Properties:
--------------------------------------
# data blocks: 1
# entries: 1
# deletions: 0
# merge operands: 0
# range deletions: 0
raw key size: 12
raw average key size: 12.000000
raw value size: 117
raw average value size: 117.000000
data block size: 110
index block size (user-key? 1, delta-value? 1): 21
filter block size: 69
# entries for filter: 1
(estimated) table size: 200
filter policy name: bloomfilter
prefix extractor name: nullptr
column family ID: 13
column family name: volumeTable
comparator name: leveldb.BytewiseComparator
user defined timestamps persisted: true
largest sequence number in file: 18446744073709551615
merge operator name: nullptr
property collectors names: []
SST file compression algo: Snappy
SST file compression options: window_bits=-14; level=32767; strategy=0; max_dict_bytes=0; zstd_max_train_bytes=0; enabled=0; max_dict_buffer_bytes=0; use_zstd_dict_trainer=1;
creation time: 1735435834
time stamp of earliest key: 1735435834
time stamp of newest key: 0
file creation time: 1735435880
slow compression estimated data size: 0
fast compression estimated data size: 0
DB identity: ad2a9fc2-3681-4596-a72b-9d12eb547341
DB session identity: WFHBYB3ZI2FR1KO3L51Q
DB host id: 56445fff8010
original file number: 53
unique ID: 5F228155FE1F3D83-73E7CB0D6B021DC6
Sequence number to time mapping:
Index Details:
--------------------------------------
Block key hex dump: Data block handle
Block key ascii
HEX 2F733376: 0069 offset 0 size 105
ASCII / s 3 v
------
Data Block # 1 @ 0069
--------------------------------------
HEX 2F733376: 0A066861646F6F7012066861646F6F701A0373337620FFFFFFFFFFFFFFFFFF01320F080112066861646F6F701A01802000320F080212066861646F6F701A0180200038C4ABA580C1324080FEFFFFFFFFFFFFBF0148FFFFFFFFFFFFFFFFFF0150C4ABA580C13258FFFFFFFFFFFFFFFFFF0160006800
ASCII / s 3 v :
h a d o o p h a d o o p ␦ s 3 v � � � � � � � � � 2 h a d o o p ␦ � \0 2 h a d o o p ␦ � \0 8 � � � � � 2 @ � � � � � � � � � H � � � � � � � � � P � � � � � 2 X � � � � � � � � � ` \0 h \0
------
Data Block Summary:
--------------------------------------
# data blocks: 1
min data block size: 105
max data block size: 105
avg data block size: 105.000000
一点はまりどころとして、Ozone上で何らかの操作を実施したのち、メタデータを確認しても期待するようなSSTファイルがない場合がある。これは、RocksDBはSSTファイルを作成する前にLOGにデータを書き込み、適当なタイミングでcompactionが走るとSSTファイルが作成されるためである。そういった場合は以下のように手動でcompactionを実施することで、SSTファイルが作成されることがある。
$ ./ldb compact --db=/tmp/metadata/om.db
なお、 ldb
はRocksDBをビルドすると sst_dump
等と一緒に作成されるコマンドラインツールである。
メタデータ
ここでは、メタデータについて確認していく。
OM メタデータ
ozone-site.xml
で設定される ozone.metadata.dirs
に指定された場所にファイルが作成される。
OMが管理するメタデータには以下の種類がある。元ネタはこちら
Common Tables:
Column Family | VALUE |
---|---|
userTable | /user->UserVolumeInfo |
volumeTable | /volume->VolumeInfo |
bucketTable | /volume/bucket-> BucketInfo |
s3SecretTable | s3g_access_key_id -> s3Secret |
dTokenTable | OzoneTokenID -> renew_time |
prefixInfoTable | prefix -> PrefixInfo |
multipartInfoTable | /volumeName/bucketName/keyName/uploadId ->... |
transactionInfoTable | #TRANSACTIONINFO -> OMTransactionInfo |
Multi-Tenant Tables:
tenantStateTable | tenantId -> OmDBTenantState |
---|---|
tenantAccessIdTable | accessId -> OmDBAccessIdInfo |
principalToAccessIdsTable | userPrincipal -> OmDBUserPrincipalInfo |
Simple Tables:
Column Family | VALUE |
---|---|
keyTable | /volumeName/bucketName/keyName->KeyInfo |
deletedTable | /volumeName/bucketName/keyName->RepeatedKeyInfo |
openKey | /volumeName/bucketName/keyName/id->KeyInfo |
Prefix Tables:
Column Family | VALUE |
---|---|
directoryTable | /volumeId/bucketId/parentId/dirName -> DirInfo |
fileTable | /volumeId/bucketId/parentId/fileName -> KeyInfo |
openFileTable | /volumeId/bucketId/parentId/fileName/id -> KeyInfo |
deletedDirTable | /volumeId/bucketId/parentId/dirName/objectId -> KeyInfo |
Snapshot Tables:
Column Family | VALUE |
---|---|
snapshotInfoTable | /volume/bucket/snapshotName -> SnapshotInfo |
snapshotRenamedTable | /volumeName/bucketName/objectID -> One of: (*) |
compactionLogTable | dbTrxId-compactionTime -> compactionLogEntry |
(*): objectIDの種類
- /volumeId/bucketId/parentId/dirName
- /volumeId/bucketId/parentId/fileName
- /volumeName/bucketName/keyName
全て見るのは大変なので、今回は volumeTable, bucketTable, fileTable についてdumpデータを確認していく。
$ cat 000053_dump.txt
Footer Details:
--------------------------------------
metaindex handle: CB084F offset: 1099 size: 79
index handle: B80110 offset: 184 size: 16
table_magic_number: 9863518390377041911
format version: 5
Metaindex Details:
--------------------------------------
Filter block handle: 6E45
Properties block handle: CD01F906
Table Properties:
--------------------------------------
# data blocks: 1
# entries: 1
# deletions: 0
# merge operands: 0
# range deletions: 0
raw key size: 12
raw average key size: 12.000000
raw value size: 117
raw average value size: 117.000000
data block size: 110
index block size (user-key? 1, delta-value? 1): 21
filter block size: 69
# entries for filter: 1
(estimated) table size: 200
filter policy name: bloomfilter
prefix extractor name: nullptr
column family ID: 13
column family name: volumeTable
comparator name: leveldb.BytewiseComparator
user defined timestamps persisted: true
largest sequence number in file: 18446744073709551615
merge operator name: nullptr
property collectors names: []
SST file compression algo: Snappy
SST file compression options: window_bits=-14; level=32767; strategy=0; max_dict_bytes=0; zstd_max_train_bytes=0; enabled=0; max_dict_buffer_bytes=0; use_zstd_dict_trainer=1;
creation time: 1735435834
time stamp of earliest key: 1735435834
time stamp of newest key: 0
file creation time: 1735435880
slow compression estimated data size: 0
fast compression estimated data size: 0
DB identity: ad2a9fc2-3681-4596-a72b-9d12eb547341
DB session identity: WFHBYB3ZI2FR1KO3L51Q
DB host id: 56445fff8010
original file number: 53
unique ID: 5F228155FE1F3D83-73E7CB0D6B021DC6
Sequence number to time mapping:
Index Details:
--------------------------------------
Block key hex dump: Data block handle
Block key ascii
HEX 2F733376: 0069 offset 0 size 105
ASCII / s 3 v
------
Data Block # 1 @ 0069
--------------------------------------
HEX 2F733376: 0A066861646F6F7012066861646F6F701A0373337620FFFFFFFFFFFFFFFFFF01320F080112066861646F6F701A01802000320F080212066861646F6F701A0180200038C4ABA580C1324080FEFFFFFFFFFFFFBF0148FFFFFFFFFFFFFFFFFF0150C4ABA580C13258FFFFFFFFFFFFFFFFFF0160006800
ASCII / s 3 v :
h a d o o p h a d o o p ␦ s 3 v � � � � � � � � � 2 h a d o o p ␦ � \0 2 h a d o o p ␦ � \0 8 � � � � � 2 @ � � � � � � � � � H � � � � � � � � � P � � � � � 2 X � � � � � � � � � ` \0 h \0
------
Data Block Summary:
--------------------------------------
# data blocks: 1
min data block size: 105
max data block size: 105
avg data block size: 105.000000
まずポイントとなるのが Table Properties
の column family name
である。この部分に前述したColumnデータが入ってくる。今回の場合だと volumeTable
である。この部分はSSTファイルごとに異なってくる。
次に Data Block
部分を見ていく。
Data Block # 1 @ 0069
--------------------------------------
HEX 2F733376: 0A066861646F6F7012066861646F6F701A0373337620FFFFFFFFFFFFFFFFFF01320F080112066861646F6F701A01802000320F080212066861646F6F701A0180200038C4ABA580C1324080FEFFFFFFFFFFFFBF0148FFFFFFFFFFFFFFFFFF0150C4ABA580C13258FFFFFFFFFFFFFFFFFF0160006800
ASCII / s 3 v :
h a d o o p h a d o o p ␦ s 3 v � � � � � � � � � 2 h a d o o p ␦ � \0 2 h a d o o p ␦ � \0 8 � � � � � 2 @ � � � � � � � � � H � � � � � � � � � P � � � � � 2 X � � � � � � � � � ` \0 h \0
特にOzoneにデータを格納していない場合でも、 /s3v
というボリュームが見える。 /s3v
は事前に予約されたボリュームであり、ClientからS3G経由でデータを書き込むと /s3v
配下にバケットやキー (S3でいうオブジェクト)が書き込まれる。ASCIIの :
より後が文字化けしているが、心の目で見るとこれがProtocol Buffersで書きこまれたバイナリファイルであるとわかるはずだ。JSONでデコードすると以下である。
{
"metadata": [],
"volumeAcls": [
{
"type": "USER",
"name": "hadoop",
"rights": [
128
],
"aclScope": "ACCESS"
},
{
"type": "GROUP",
"name": "hadoop",
"rights": [
128
],
"aclScope": "ACCESS"
}
],
"adminName": "hadoop",
"ownerName": "hadoop",
"volume": "s3v",
"quotaInBytes": 18446744073709552000,
"creationTime": 1735435834820,
"objectID": 13835058055282164000,
"updateID": 18446744073709552000,
"modificationTime": 1735435834820,
"quotaInNamespace": -1,
"usedNamespace": 0,
"refCount": 0
}
この辺りの情報はOzoneのProtoファイル[1]に定義されている。
/s3v
はあらかじめ用意されたボリュームだが、Ozoneでは新しくボリュームを作成することももちろん可能である。例えば以下のようなコマンドを実行する。
$ ozone sh --verbose volume create /myvolume1
{
"metadata" : { },
"name" : "myvolume1",
"admin" : "hadoop",
"owner" : "hadoop",
"quotaInBytes" : -1,
"quotaInNamespace" : -1,
"usedNamespace" : 0,
"creationTime" : "2024-12-29T10:36:35.818Z",
"modificationTime" : "2024-12-29T10:36:35.818Z",
"acls" : [ {
"type" : "USER",
"name" : "hadoop",
"aclScope" : "ACCESS",
"aclList" : [ "ALL" ]
}, {
"type" : "GROUP",
"name" : "hadoop",
"aclScope" : "ACCESS",
"aclList" : [ "READ", "LIST" ]
} ],
"refCount" : 0
}
次にbucketTableである。なおこの前に、以下の操作で Volume, Bucket, Keyを作成している。
$ ozone sh volume create /myvolume1
$ ozone sh bucket create /myvolume1/mybucket1
$ ozone sh key put o3://om/myvolume1/mybucket1/myhosts /etc/hosts
以下がSSTファイルをダンプした結果である。
Footer Details:
--------------------------------------
metaindex handle: AD0950 offset: 1197 size: 80
index handle: D50121 offset: 213 size: 33
table_magic_number: 9863518390377041911
format version: 5
Metaindex Details:
--------------------------------------
Filter block handle: 8B0145
Properties block handle: FB01AD07
Table Properties:
--------------------------------------
# data blocks: 1
# entries: 1
# deletions: 0
# merge operands: 0
# range deletions: 0
raw key size: 28
raw average key size: 28.000000
raw value size: 128
raw average value size: 128.000000
data block size: 139
index block size (user-key? 1, delta-value? 1): 38
filter block size: 69
# entries for filter: 1
(estimated) table size: 246
filter policy name: bloomfilter
prefix extractor name: nullptr
column family ID: 22
column family name: bucketTable
comparator name: leveldb.BytewiseComparator
user defined timestamps persisted: true
largest sequence number in file: 13
merge operator name: StringAppendOperator
property collectors names: []
SST file compression algo: Snappy
SST file compression options: window_bits=-14; level=32767; strategy=0; max_dict_bytes=0; zstd_max_train_bytes=0; enabled=0; max_dict_buffer_bytes=0; use_zstd_dict_trainer=1;
creation time: 1735477537
time stamp of earliest key: 0
time stamp of newest key: 1735477537
file creation time: 0
slow compression estimated data size: 0
fast compression estimated data size: 0
DB identity: 76cd09ec-ab02-482d-925c-8112f3c3578b
DB session identity: VO1T2SM8DQD1XG622VJ2
DB host id: XXXXXXX
original file number: 62
unique ID: D0DF7C606CCFBAEE-B04275A4183D8248
Sequence number to time mapping:
Index Details:
--------------------------------------
Block key hex dump: Data block handle
Block key ascii
HEX 2F6D79766F6C756D65312F6D796275636B657431: 008601 offset 0 size 134
ASCII / m y v o l u m e 1 / m y b u c k e t 1
------
Data Block # 1 @ 008601
--------------------------------------
HEX 2F6D79766F6C756D65312F6D796275636B657431: 0A096D79766F6C756D653112096D796275636B6574311A0F080112066861646F6F701A018020001A0F080212066861646F6F701A010920002000280130B2CBB093C1324880868080808080808001500358B2CBB093C13270AE0178FFFFFFFFFFFFFFFFFF018001FFFFFFFFFFFFFFFFFF018801019001029A01066861646F6F70
ASCII / m y v o l u m e 1 / m y b u c k e t 1 :
m y v o l u m e 1 m y b u c k e t 1 ␦ h a d o o p ␦ � \0 ␦ h a d o o p ␦ \0 \0 ( 0 � � � � � 2 H � � � � � � � � � P X � � � � � 2 p � x � � � � � � � � � � � � � � � � � � � � � � h a d o o p
------
Data Block Summary:
--------------------------------------
# data blocks: 1
min data block size: 134
max data block size: 134
avg data block size: 134.000000
まあ大体書いてあることはわかるだろう。JSONにデコードしたものが以下である。
{
"acls": [
{
"type": "USER",
"name": "hadoop",
"rights": [
128
],
"aclScope": "ACCESS"
},
{
"type": "GROUP",
"name": "hadoop",
"rights": [
9
],
"aclScope": "ACCESS"
}
],
"metadata": [],
"volumeName": "myvolume1",
"bucketName": "mybucket1",
"isVersionEnabled": false,
"storageType": "DISK",
"creationTime": 1735475865010,
"beinfo": null,
"objectID": 9223372036854776000,
"updateID": 3,
"modificationTime": 1735475865010,
"sourceVolume": "",
"sourceBucket": "",
"usedBytes": 174,
"quotaInBytes": -1,
"quotaInNamespace": -1,
"usedNamespace": 1,
"bucketLayout": "FILE_SYSTEM_OPTIMIZED",
"owner": "hadoop",
"defaultReplicationConfig": null
}
次に fileTableである。
Footer Details:
--------------------------------------
metaindex handle: 820A50 offset: 1282 size: 80
index handle: 970236 offset: 279 size: 54
table_magic_number: 9863518390377041911
format version: 5
(snip)
column family name: fileTable
(snip)
Data Block # 1 @ 00C801
--------------------------------------
HEX 2F2D393232333337323033363835343737353535322F2D393232333337323033363835343737353034302F2D393232333337323033363835343737353034302F6D79686F737473: 0A096D79766F6C756D653112096D796275636B6574311A076D79686F73747320AE01280130013A210800121B0A100A0C080110818080E097E587CA011002180020AE01280048002000408D9BBF93C13248AEA4BF93C13250006A0F080112066861646F6F701A018020006A0F080212066861646F6F701A0109200070818A80808080808080017807800180868080808080808001980101A201066861646F6F70
ASCII / - 9 2 2 3 3 7 2 0 3 6 8 5 4 7 7 5 5 5 2 / - 9 2 2 3 3 7 2 0 3 6 8 5 4 7 7 5 0 4 0 / - 9 2 2 3 3 7 2 0 3 6 8 5 4 7 7 5 0 4 0 / m y h o s t s :
m y v o l u m e 1 m y b u c k e t 1 ␦ m y h o s t s � ( 0 : ! \0
� � � � � � � \0 � ( \0 H \0 \0 @ � � � � � 2 H � � � � � 2 P \0 j h a d o o p ␦ � \0 j h a d o o p ␦ \0 p � � � � � � � � � x � � � � � � � � � � � � h a d o o p
------
(snip)
上記を見ると、ASCIIで表示されている部分について、volume名やbucket名が書かれていないことがわかる。これは、Ozoneにおいて FILE_SYSTEM_OPTIMIZED (FSO) と呼ばれる技術が使われているためである。JSONで見るとわかりやすい。
{
"keyLocationList": [
{
"keyLocations": [
{
"blockID": {
"containerBlockID": {
"containerID": 1,
"localID": 113750153625600000
},
"blockCommitSequenceId": 2
},
"offset": 0,
"length": 174,
"createVersion": 0,
"token": null,
"pipeline": null,
"partNumber": 0
}
],
"version": 0,
"fileEncryptionInfo": null,
"isMultipartKey": false
}
],
"metadata": [],
"acls": [
{
"type": "USER",
"name": "hadoop",
"rights": [
128
],
"aclScope": "ACCESS"
},
{
"type": "GROUP",
"name": "hadoop",
"rights": [
9
],
"aclScope": "ACCESS"
}
],
"tags": [],
"volumeName": "myvolume1",
"bucketName": "mybucket1",
"keyName": "myhosts",
"dataSize": 174,
"type": "RATIS",
"factor": "ONE",
"creationTime": 1735476104589,
"modificationTime": 1735476105774,
"latestVersion": 0,
"fileEncryptionInfo": null,
"objectID": 9223372036854778000,
"updateID": 7,
"parentID": 9223372036854776000,
"ecReplicationConfig": null,
"fileChecksum": null,
"isFile": true,
"ownerName": "hadoop",
"expectedDataGeneration": 0
}
先ほどのbucketTableにおいて、 /myvolume1/mybucket1
の objectIDが 9223372036854776000 であったことを思い出してほしい。つまり、Ozoneのファイル(key)メタ情報としては、その親となるbucket情報などは文字列で持たず別途数値のIDで管理している。このことにより、Ozoneはオブジェクトストレージではあるものの、あるbucket、もしくはディレクトリ(相当)のrenameが
OMが管理するメタデータは他にもたくさんあるが、重要なことはこれらのメタデータをOMはRocksDBで管理していることである。常にメモリに保持する必要がないため、スケーラビリティに寄与する。
SCM メタデータ
SCMのメタデータを見ていく。OMのときと同様に、以下のコマンドを実行済みの状態であるとする。
$ ozone sh volume create /myvolume1
$ ozone sh bucket create /myvolume1/mybucket1
$ ozone sh key put o3://om/myvolume1/mybucket1/myhosts /etc/hosts
Footer Details:
--------------------------------------
(snip)
Table Properties:
--------------------------------------
(snip)
column family ID: 1
column family name: sequenceId
(snip)
Index Details:
--------------------------------------
Block key hex dump: Data block handle
Block key ascii
HEX 6C6F63616C4964: 0060 offset 0 size 96
ASCII l o c a l I d
------
Data Block # 1 @ 0060
--------------------------------------
HEX 43657274696669636174654964: 0000000000000002
ASCII C e r t i f i c a t e I d : \0 \0 \0 \0 \0 \0 \0
------
HEX 636F6E7461696E65724964: 0000000000000000
ASCII c o n t a i n e r I d : \0 \0 \0 \0 \0 \0 \0 \0
------
HEX 64656C54786E4964: 0000000000000000
ASCII d e l T x n I d : \0 \0 \0 \0 \0 \0 \0 \0
------
HEX 6C6F63616C4964: 01941F297C000000
ASCII l o c a l I d : � ) | \0 \0 \0
------
(snip)
ここでも注目するべきは column family name
だろう。SCMは起動時に SCMMetadataStore
を起動する。実装としては SCMMetadataStoreImpl[2] が使われる。sequenceID は Containerを作成する際の BlockID を生成するために使われる。Monotonicallyに増加するようになっており、SCMが再起動してもBlockIDが一意になることを保証する。そのようなメタデータを永続化するためにRocksDBが使われていることがわかる。
なお、ContainerID + LocalIDでBlockIDとなり、これが一つのkeyを示すこととなる。この採番は新しいBlockが払い出されたときに実施される[3]。
DN メタデータ
Ozone v1.4.1の場合、DataNodeのSchemaはV3となっている。SchemaV2ではContainer一つごとにそのメタデータを管理しているRocksDBができていたが、Schema V3ではディスクごとになっている[4]。
ここではわかりやすいように大量にデータを書き込んだうえでメタデータを確認する。以下はblock_dataである。
Footer Details:
--------------------------------------
metaindex handle: BFCD0253 offset: 42687 size: 83
index handle: B7C202BA03 offset: 41271 size: 442
table_magic_number: 9863518390377041911
format version: 5
Metaindex Details:
--------------------------------------
Filter block handle: ADB802850A
Properties block handle: F6C502C407
Table Properties:
--------------------------------------
(snip)
prefix extractor name: rocksdb.FixedPrefix.9
column family ID: 3
column family name: block_data
(snip)
HEX 00000000000000017C313133373530313533363235363030303334: 00BF0A offset 0 size 1343
ASCII | 1 1 3 7 5 0 1 5 3 6 2 5 6 0 0 0 3 4
------
HEX 00000000000000017C313133373530313533363235363030303638: C40AE40A offset 1348 size 1380
ASCII | 1 1 3 7 5 0 1 5 3 6 2 5 6 0 0 0 6 8
------
(snip)
Data Block # 1 @ 00BF0A -------------------------------------- HEX 00000000000000017C313133373530313533363235363030303031: 0A0E080110818080E097E587CA0118021A0B0A045459504512034B455922AB030A1A3131333735303135333632353630303030315F6368756E6B5F311000188080402A86030802108080011A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D2861A04AB54D28628808040
(snip)
Data Block部分をデコードすると以下である。
{
"metadata": [
{
"key": "TYPE",
"value": "KEY"
}
],
"chunks": [
{
"metadata": [],
"chunkName": "113750153625600001_chunk_1",
"offset": 0,
"len": 1048576,
"checksumData": {
"checksums": [
[
171,
84,
210,
134
],
(snip)
"type": "CRC32",
"bytesPerChecksum": 16384
},
"stripeChecksum": []
}
],
"blockID": {
"containerID": 1,
"localID": 113750153625600000,
"blockCommitSequenceId": 2,
"replicaIndex": 0
},
"flags": 0,
"size": 1048576
}
前提として、OzoneではあるContaienrの中に複数のブロックが存在し、ブロックの中も複数のchunkでデータが分かれている。chunkのデータはDataNodeのファイルシステムから確認可能。どのchunkno
データがどのファイルに入っているのかを確認するためにRocksDBの中を使っている。上記のデータからもそれが確認できる。
以下はmetadataである。
Footer Details:
--------------------------------------
metaindex handle: F8084F offset: 1144 size: 79
index handle: 99011F offset: 153 size: 31
table_magic_number: 9863518390377041911
format version: 5
Metaindex Details:
--------------------------------------
Filter block handle: 4F45
Properties block handle: BD01B607
Table Properties:
--------------------------------------
(snip)
column family ID: 4
column family name: metadata
(snip)
Index Details:
--------------------------------------
Block key hex dump: Data block handle
Block key ascii
HEX 00000000000000017C23425954455355534544: 004A offset 0 size 74
ASCII | # B Y T E S U S E D
------
Data Block # 1 @ 004A
--------------------------------------
HEX 00000000000000017C234243534944: 0000000000000BB9
ASCII \0 \0 \0 \0 \0 \0 \0 | # B C S I D : \0 \0 \0 \0 \0 \0
�
------
HEX 00000000000000017C23424C4F434B434F554E54: 00000000000003E8
ASCII \0 \0 \0 \0 \0 \0 \0 | # B L O C K C O U N T : \0 \0 \0 \0 \0 \0 �
------
HEX 00000000000000017C23425954455355534544: 000000003E800000
ASCII \0 \0 \0 \0 \0 \0 \0 | # B Y T E S U S E D : \0 \0 \0 \0 > � \0 \0
------
Data Block Summary:
--------------------------------------
# data blocks: 1
min data block size: 74
max data block size: 74
avg data block size: 74.000000
これはcontainerごとにどれくらいbytesを消費しているかなどのメタデータを表示する。
終わりに
Ozoneのメタデータがどう管理されているかを垣間見るための一助になれば。次は通信についてまとめる。
TIPS
Ozoneが提供しているDocker Composeファイルを以下のように追記するとRemote Debugできる。
services:
datanode:
<<: *common-config
ports:
- 19864
- 9882
- 5005:5005
environment:
<<: *replication
OZONE_OPTS: "-agentlib:jdwp=transport=dt_socket,address=*:5005,server=y,suspend=n"
command: ["ozone","datanode"]
-
https://github.com/apache/ozone/blob/345c46850f2f145d4f735214d32390e422544894/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto#L607 ↩︎
-
https://github.com/apache/ozone/blob/a9d5d1c630d903b28984d1c46daa78a1812674ac/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/metadata/SCMMetadataStoreImpl.java ↩︎
-
https://github.com/apache/ozone/blob/a9d5d1c630d903b28984d1c46daa78a1812674ac/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/block/BlockManagerImpl.java#L186 ↩︎
Discussion