
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.
Các tầng offset
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.
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”.
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_sizeSơ đồ mô tả mối quan hệ giữa offset FS (4 KB) và offset object Ceph (4 MB) cho file 1 GB
- File = 1 GB
- FS block size = 4 KB → 262,144 block
- Ceph object size = 4 MB → 256 object
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 |
+--------------------------------------------------+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.
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, ...)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. 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 driver4. 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).
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 OSDs6. 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)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)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.
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ốngVí 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 MBVí 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ốngNhậ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.
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.

