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_size

Sơ đồ 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 * 4Klength = 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.
  • 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

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)
  • 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 OSDs

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)

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ố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.

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.

Leave a Reply

This site uses cookies to offer you a better browsing experience. By browsing this website, you agree to our use of cookies.