1. Tổng quan
Trong Ceph với RBD image, dữ liệu mà VM ghi xuống sẽ đi qua nhiều tầng khác nhau. Mỗi tầng có một đơn vị quản lý riêng:
- File System (FS) trong VM quản lý dữ liệu theo block nhỏ (default thường 4 KB).
- Volume (RBD image) cung cấp một dung lượng block logic liên tục, giống như một ổ đĩa thật.
- Object Ceph: RBD image được cắt thành các object cố định (default thường 4 MB mỗi object).
- Cluster Ceph: Object được phân tán và replicate trên các OSD theo CRUSH map.
Hiểu rõ cách mapping offset ở mỗi tầng giúp ta nắm được cơ chế ghi dữ liệu, từ đó dễ phân tích hiệu năng và dung lượng.
2. Các tầng offset
2.1 Offset trong File System (FS)
- FS (ext4, xfs, ntfs…) quản lý file bằng block size (ví dụ 4 KB).
- File được chia thành nhiều block, mỗi block map đến một địa chỉ LBA trên volume.
- Mỗi block này sẽ tương ứng với một range offset cố định trên volume (ví dụ: offset 0–4 KB, offset4–8 KB, offset 8–12 KB …).
Metadata (inode, extents, bitmap) của FS ghi lại mapping này để biết file offset nào nằm ở block nào trên volume.
Ví dụ:
File 1 GB
↓ (FS chia thành block 4 KB)
Block #0 → Volume offset 0–4 KB
Block #1 → Volume offset 4–8 KB
Block #2 → Volume offset 8–12 KB
...
Block #262143 → Volume offset 1,073,740,800–1,073,741,824 (tức 1 GB)
Tổng cộng = 262,144 offset range 4 KB trong volume.
FS metadata (inode/extents) ghi lại danh sách những offset này → giúp hệ thống biết phần nào của file nằm ở đâu trên volume.
2.2 Offset trong Volume (RBD image)
- Với VM, volume RBD xuất hiện như một disk liên tục 100 GB, offset tính từ 0 đến 100 GB.
- FS trong VM chỉ biết offset này, không biết gì về object Ceph.
- Khi FS cần ghi block 4 KB tại LBA N → kernel gửi yêu cầu “ghi 4 KB tại offset X trong volume”.
2.3 Offset trong Ceph Object
- Ceph định nghĩa object_size khi tạo RBD image (mặc định 4 MB).
- Toàn bộ volume được chia thành các range cố định theo object_size.
Ví dụ với object_size = 4 MB:
Object #0 → offset 0–4 MB
Object #1 → offset 4–8 MB
Object #2 → offset 8–12 MB
...
- Khi có một I/O xuống volume, Ceph dùng công thức tính:
object_id = floor(volume_offset / object_size)
object_off = volume_offset % object_size
2.4. So sánh Offset trong Volume với Offset trong Ceph object
- Volume 1 GB với object size 4 MB → chỉ có 256 object slot (1 GB / 4 MB).
- Nghĩa là: 262,144 offset FS (4 KB) được “gom” vào 256 offset Ceph object (4 MB).
- Cứ 1 object Ceph (4 MB) sẽ chứa tối đa 1,024 block FS (4 KB).
2.5. Sơ đồ mô tả mối quan hệ giữa offset FS (4 KB) và offset object Ceph (4 MB) cho file 1 GB.
Thông số
- File = 1 GB
- FS block size = 4 KB → 262,144 block
- Ceph object size = 4 MB → 256 object
Sơ đồ quan hệ
File 1 GB
|
|--- FS chia thành block 4 KB → offset nhỏ
| (262,144 offset range 4 KB)
|
v
+------------------- Volume RBD --------------------+
| Offset 0–4K | Offset 4K–8K | ... | Offset 1GB |
+--------------------------------------------------+
| | |
v v v
+---------------- Ceph object mapping ---------------+
| Obj #0: 0–4 MB | Obj #1: 4–8 MB | ... | Obj #255: 1020–1024 MB |
+--------------------------------------------------+
2.6. Zoom-in một object
Ví dụ Object #0 (0–4 MB):
- Chứa tối đa = 4 MB / 4 KB = 1,024 block FS.
- Mapping:
FS block #0 → Offset 0–4 KB → Object #0, offset 0
FS block #1 → Offset 4–8 KB → Object #0, offset 4 KB
FS block #2 → Offset 8–12 KB → Object #0, offset 8 KB
...
FS block #1023 → Offset ~4 MB → Object #0, offset ~4 MB
Nhận xét
- 262,144 offset FS (4 KB) gom thành 256 offset object (4 MB).
- Ceph không quan tâm đến block FS, chỉ nhìn offset volume → object.
- Mỗi object Ceph = 1 “container” cố định 4 MB, chứa rất nhiều block FS bên trong.
3. Cách ghi dữ liệu
- Bối cảnh giả định để dễ theo dõi.
- FS ext4 trong VM
- Block size = 4 KB
- RBD image object_size = 4 MB
- pool replica = 2 hoặc 3
- BlueStore làm backend OSD.
3.1 Ứng dụng ghi file → FS chia thành block (4 KB)
- Ứng dụng gọi
write()
vào một file. Kernel page cache nhận dữ liệu. - FS (ext4/xfs) làm việc theo block size (ví dụ 4 KB). Dữ liệu được chặt thành các block 4 KB theo file offset.
- Nếu ghi tuần tự, kernel có thể gom nhiều block liền kề thành các I/O lớn hơn (ví dụ 16 KB, 64 KB) trước khi đẩy xuống lớp block.
- Nếu ghi rời rạc, vẫn sẽ phát sinh nhiều I/O 4 KB tách rời.
Sơ đồ ngắn:
App write() → Page Cache → FS blocks (4K, 4K, 4K, ...)
3.2 FS metadata xác định LBA (block số N trong volume)
- FS quyết định mỗi block 4 KB của file offset sẽ nằm ở LBA nào của volume (RBD device).
- Thông tin ánh xạ này được lưu trong metadata của FS:
- ext4/xfs: inode + extents/bitmap cho biết file offset → danh sách LBA/extent trên volume.
- Kết quả: ta có các cặp ánh xạ kiểu:
file_offset 0–4K → volume LBA #A file_offset 4–8K → volume LBA #A+1 ...
- Đây là ánh xạ hoàn toàn nội bộ của FS; Ceph không biết, không cần can thiệp.
3.3 Kernel gửi request ghi 4 KB tại offset X trong volume
- Kernel (blk-mq) tạo các bio/request tới thiết bị block là RBD:
- Mỗi request mang:
offset = LBA * 4K
,length = n * 4K
. - Nếu hệ thống phát hiện các block liền kề, request có thể là 64 KB, 1 MB… thay vì 4 KB.
- Mỗi request mang:
- Queue I/O (queue depth) và thuật toán sắp xếp (mq-deadline, kyber…) ảnh hưởng mức độ gom/điều phối các request này.
Sơ đồ:
FS mapping → bio(rbd0, offset=X, len=Y) → blk-mq → rbd driver
3.4 RBD driver tính toán object_id
và object_off
- RBD nhìn image như dung lượng liên tục nhưng nội bộ chia bởi object_size (mặc định 4 MB).
- Với mỗi request từ 3.3, RBD tính:
object_id = floor(volume_offset / object_size) object_off = volume_offset % object_size
- Nếu request cắt ngang ranh giới object, RBD tách thành nhiều mảnh, mỗi mảnh rơi đúng vào một object:
- Ví dụ ghi 1 MB tại offset 3.5 MB:
- 0.5 MB vào Object #0 (offset 3.5–4.0 MB)
- 0.5 MB vào Object #1 (offset 0.0–0.5 MB)
- Ví dụ ghi 1 MB tại offset 3.5 MB:
- RBD tạo thông điệp RADOS tương ứng:
write(Object #k, offset=object_off, length=segment_len) write(Object #k+1, offset=..., length=...) ...
- Với RBD có bật object map/thin, vùng chưa từng ghi sẽ tạo object mới (size khởi điểm đúng bằng dữ liệu ghi).
3.5 Ceph cluster băm object → PG → CRUSH → OSD
- Object ID được băm ra PG (placement group).
- CRUSH map quyết định bộ OSD nào giữ PG đó (primary + replicas).
- Client (RBD) gửi request tới primary OSD của PG. Primary:
- Áp dụng chính sách bền vững: gửi bản sao tới các secondary OSD theo hệ số replica.
- Cơ chế trả ACK:
- write ack: trả về khi replicas đã nhận vào bộ nhớ/log.
- commit ack: trả về khi dữ liệu đã bền vững trên đĩa (BlueStore commit).
- Với EC (erasure coding), lộ trình khác replica (chia stripe/chunk theo k, m), nhưng nguyên lý map qua PG/CRUSH vẫn tương tự.
Sơ đồ:
Client → Primary OSD
↘ replicate → Secondary OSDs
3.6 OSD lưu object vào extent trên disk (sector 512B hoặc 4K)
- Backend BlueStore:
- Dữ liệu object được ghi thành các extent trên thiết bị data.
- Metadata (mapping object → extent) lưu trong RocksDB (DB/WAL).
- Ghi nhỏ có thể được align theo 4K; có thể có write amplification tùy thiết bị.
- Nếu DB/WAL đặt trên SSD/NVMe, độ trễ cam kết giảm đáng kể so với thuần HDD.
- Trên thiết bị vật lý:
- HDD: ghi theo sector 512B/4K, seek làm tăng độ trễ khi I/O rời rạc.
- SSD: FTL ánh xạ LBA → NAND pages (4K/8K/16K), có cơ chế wear leveling/GC.
- Khi hoàn tất giao dịch (transaction) lưu trữ:
- Primary OSD tổng hợp ACK từ replicas theo chính sách (write/commit) và trả về client.
Ví dụ tính nhanh
- Ghi 64 KB tại offset 6 MB (object_size 4 MB):
object_id = floor(6MB / 4MB) = 1 → Object #1 object_off = 6MB - 4MB = 2 MB → write(Object #1, off=2MB, len=64KB)
- BlueStore tạo/ cập nhật extent tương ứng trong Object #1 và flush theo pipeline của OSD.
Sơ đồ tóm tắt logic
App → Page Cache → FS (block 4K, metadata extents)
↓
bio(req) tới rbd0 (offset X, len Y)
↓
RBD: cắt/gom theo object_size 4MB → (obj_id, obj_off)
↓
RADOS: gửi tới PG primary → replicate tới OSD khác
↓
OSD (BlueStore): ghi extent + cập nhật RocksDB → flush/commit
↓
ACK về client (write ack hoặc commit ack)
4. Mối quan hệ offset giữa các tầng
[File trong FS]
|
|---> Chia thành block 4 KB (do FS ext4 định nghĩa)
v
[Volume RBD 100 GB, offset LBA liên tục]
|
|---> Offset này được map cố định vào Object (theo object_size = 4 MB)
v
[Ceph Object Mapping]
|
|---> Mỗi object = 1 slot 4 MB, chứa nhiều block FS
v
[Cluster Ceph]
|
|---> CRUSH → PG → OSD, dữ liệu phân tán và replicate
v
[OSD BlueStore]
|
|---> Lưu object vào extent → sector thật (HDD/SSD)
5. Ví dụ minh họa
Mình sẽ ghép pipeline chung cho cả ba ví dụ (file 1 KB, 20 MB, 21 MB) để các bạn thấy rõ toàn bộ đường đi từ file trong FS → block FS → offset volume → Ceph object.
Pipeline tổng quát
[File trong FS]
|
|---> Chia thành block 4 KB (do FS ext4 định nghĩa)
v
[Volume RBD 100 GB, offset LBA liên tục]
|
|---> Offset này được map cố định vào Object (theo object_size = 4 MB)
v
[Ceph Object Mapping]
|
|---> Mỗi object = 1 slot 4 MB, chứa nhiều block FS
v
[Cluster Ceph]
|
|---> CRUSH → PG → OSD, dữ liệu phân tán và replicate
v
[OSD BlueStore]
|
|---> Lưu object vào extent → sector thật (HDD/SSD)
Ví dụ 1: File 1 KB
- FS: file 1 KB → vẫn chiếm 1 block 4 KB.
- Volume offset: nằm trong range 0–4 MB.
- Ceph: tạo Object #0, offset 0, length 4 KB.
File size: 1 KB
FS block: 4 KB
Volume offset: 0–4 KB
Object map:
Object #0: [#...............] 4 KB dùng, còn lại trống
Ví dụ 2: File 20 MB
- FS: file 20 MB → 5120 block (4 KB).
- Volume offset: từ 0 đến 20 MB.
- Ceph: mapping thành 5 object, mỗi object đầy 4 MB.
File size: 20 MB
FS block: 4 KB × 5120
Volume offset: 0–20 MB
Object map:
Object #0: [################] 4 MB
Object #1: [################] 4 MB
Object #2: [################] 4 MB
Object #3: [################] 4 MB
Object #4: [################] 4 MB
Ví dụ 3: File 21 MB
- FS: file 21 MB → 5376 block (4 KB).
- Volume offset: từ 0 đến 21 MB.
- Ceph: mapping thành 6 object (5 đầy, 1 dư 1 MB).
File size: 21 MB
FS block: 4 KB × 5376
Volume offset: 0–21 MB
Object map:
Object #0: [################] 4 MB
Object #1: [################] 4 MB
Object #2: [################] 4 MB
Object #3: [################] 4 MB
Object #4: [################] 4 MB
Object #5: [#...............] 1 MB dùng, 3 MB trống
Nhận xét chung
- FS chỉ biết block 4 KB, không biết object Ceph.
- Ceph object là khung cố định (4 MB), nhưng chỉ chiếm dung lượng thật theo dữ liệu ghi.
- File không chia hết cho 4 MB → object cuối cùng chỉ đầy một phần.
6. Lời khuyên
- FS block size nhỏ (4 KB): nhiều block hơn, metadata FS nhiều hơn, nhưng linh hoạt cho I/O nhỏ.
- Object size nhỏ (2 MB): số object tăng, metadata Ceph nhiều hơn, thích hợp workload nhiều file nhỏ.
- Object size lớn (8 MB, 16 MB): metadata giảm, tối ưu cho workload tuần tự và file lớn, nhưng kém hiệu quả với file nhỏ.
- Thực tế: giữ mặc định 4 MB là hợp lý trừ khi có nhu cầu đặc biệt.
7. Kết luận
- Offset FS (4 KB) → offset volume (LBA) → offset object (4 MB) → OSD extents.
- FS không biết gì về object size Ceph, chỉ biết block 4 KB trên volume.
- Object Ceph tương ứng với một range offset cố định, chứa nhiều block FS.
- Mapping này cố định, không thay đổi, đảm bảo volume ảo liên tục nhưng dữ liệu thật được phân tán đều trên cluster Ceph.