CEPH-0001 - Ceph RBD Volume Header Recovery

storage ceph

1. Background

1.1. Ceph

7h sáng, chợt bị đánh thức từ nhiều cuộc gọi từ sếp. Mình vội dậy để kiểm tra thì thấy 1 trong các hệ thống Ceph mình đang quản lý hiện gặp lỗi, khiến cho các volumes trên Ceph không thể thao tác được nữa.

Sau 1 hồi thì mình cũng fix được, tuy nhiên impact khá lớn, khiến cho Ceph bị mất 1 số objects quan trọng, gây ra việc các group of objects tạo nên các volumes không còn hoàn chỉnh. Trong đó có sự cố rbd_header object bị mất, khiến cho RBD không ánh xạ được data của volume, dù cho data đó vẫn còn tồn tại trên Ceph.

Các Volume được ánh xạ thông qua RBD (RADOS Block Device) interface. Bài viết này sử dụng Octopus version nên sẽ chỉ debug format 2 volumes.

Bài viết này sẽ mô tả cách khôi phục rbd_header object dựa trên thông tin dữ liệu còn lại dựa trên RBD.

1.2. Tóm tắt

RBD (RADOS Block Device) là cách mà Ceph ánh xạ các objects, và OS sẽ xem như một block storage thông thường. Các objects sẽ được nhóm lại tạo thành volume, mỗi volume này sẽ gồm các objects mô tả các thông tin tương ứng, đại diện cho chính volume đó.

Object PrefixMô tả
rbd_id.NAMEObject chứa ID để định danh cho các objects tương ứng với NAME của volume
rbd_header.IDObject chứa các metadata của các objects tương ứng (features, object_prefix, size, ..)
rbd_data.IDGồm các objects chứa data của volume với prefix được defined trong header

1.3. Ví dụ

  • Ceph Pool: VNDATA-0001
  • RBD Volume Name: image01

1.3.1. rbd_id.image01

$ sudo rados -p VNDATA-0001 get rbd_id.image01 - strings
291615ea562c04

1.3.2. rbd_header.291615ea562c04

$ sudo rados -p VNDATA-0001 listomapvals rbd_header.291615ea562c04
access_timestamp
value (8 bytes) :
00000000  8d 96 94 62 ba 03 a9 06                           |...b....|
00000008

create_timestamp
value (8 bytes) :
00000000  8d 96 94 62 ba 03 a9 06                           |...b....|
00000008

features
value (8 bytes) :
00000000  3d 00 00 00 00 00 00 00                           |=.......|
00000008

modify_timestamp
value (8 bytes) :
00000000  8d 96 94 62 ba 03 a9 06                           |...b....|
00000008

object_prefix
value (27 bytes) :
00000000  17 00 00 00 72 62 64 5f  64 61 74 61 2e 32 39 31  |....rbd_data.291|
00000010  36 31 35 65 61 35 36 32  63 30 34                 |615ea562c04|
0000001b

order
value (1 bytes) :
00000000  16                                                |.|
00000001

size
value (8 bytes) :
00000000  00 00 00 80 02 00 00 00                           |........|
00000008

snap_seq
value (8 bytes) :
00000000  00 00 00 00 00 00 00 00                           |........|
00000008

Ở đây, chúng ta có thể thấy các thông tin metadata của volume image01 tương ứng với rbd_header có ID 291615ea562c04 như timestamp, features, rbd_data prefix, size, ..

Ngoài ra, chúng ta có thể xem các thông tin này với format “dễ nhìn” hơn, với rbd cli:

$ sudo rbd -p VNDATA-0001 info image01
rbd image 'image01':
  size 10 GiB in 2560 objects
  order 22 (4 MiB objects)
  snapshot_count: 0
  id: 291615ea562c04
  block_name_prefix: rbd_data.291615ea562c04
  format: 2
  features: layering, exclusive-lock, object-map, fast-diff, deep-flatten
  op_features: 
  flags: 
  create_timestamp: Mon May 30 17:03:57 2022
  access_timestamp: Mon May 30 17:03:57 2022
  modify_timestamp: Mon May 30 17:03:57 2022

2. RBD Volume Header Recovery

2.1. Xây dựng kịch bản

Pool VNDATA-0001 với 5 volumes tương ứng với các dung lượng lớn nhỏ khác nhau, với 3 trong số 5 volumes sẽ bị mất rbd_header object, trong đó có 2 volume bị mất cả rbd_id object.

Ở đây, mình sẽ giả lập ở tình huống thực tế rằng không xác định được dung lượng USED cụ thể của từng volume.

$ sudo rbd du -p VNDATA-0001
NAME     PROVISIONED  USED   
image01       10 GiB  4.1 GiB
image02      100 GiB   70 GiB
image03       70 GiB   50 GiB
image04        5 GiB   60 MiB
image05      150 GiB   20 GiB
      335 GiB  145 GiB

2.2. Missing Volume rbd_header

Để “giả lập” tình huống mất volume rbd_header, mình dùng lệnh sau:

$ sudo rados -p VNDATA-0001 get rbd_id.image02 - strings
12632c59ecee6c
$ sudo rados -p VNDATA-0001 rm rbd_header.12632c59ecee6c

2.2.1. Xác định lỗi

Lúc này, mình sẽ thấy RBD báo lỗi, có thể thấy 3 volumes bị biến mất đó là image02, image03, image04, nhưng chỉ RBD chỉ báo 1 dòng lỗi.

$ sudo rbd du -p VNDATA-0001
2022-06-01T12:05:48.487+0700 7fd7e2ffd700 -1 librbd::image::OpenRequest: failed to retrieve initial metadata: (2) No such file or directory
NAME     PROVISIONED  USED   
image01       10 GiB  4.1 GiB
image05      150 GiB   20 GiB
      160 GiB   24 GiB

Mặc dù khi mình dùng rbd ls thì vẫn thấy có tên của 3 volumes này trong list.

$ sudo rbd ls -p VNDATA-0001
image01
image02
image03
image04
image05

Có thể thấy, dòng lỗi đó là báo rằng không thể đọc được metadata của volume vì:

  • RBD get được ID của volume từ rbd_id.VOLUME_NAME object.
  • Nhưng khi đọc đến rbd_header.ID object thì object này không tồn tại.

2.2.2. Xác định cụ thể lỗi của từng volume

Tiếp theo, mình sẽ liệt kê từng volume bằng lệnh rbd info để tìm volume đang bị mất rbd_id object.

$ sudo rbd -p VNDATA-0001 info image02
2022-06-01T11:56:27.760+0700 7fc780ff9700 -1 librbd::image::OpenRequest: failed to retrieve initial metadata: (2) No such file or directory
rbd: error opening image image02: (2) No such file or directory

$ sudo rbd -p VNDATA-0001 info image03
rbd: error opening image image03: (2) No such file or directory

$ sudo rbd -p VNDATA-0001 info image04
rbd: error opening image image04: (2) No such file or directory

Lúc này, mình đã xác định được lỗi của của 3 volumes:

  • image02 bị lỗi mất rbd_header object.
  • image03, image04 bị lỗi mất rbd_header object và rbd_id object.

2.3. Recover rbd_header object

Để recovery được các object, mình phải thu thập các thông tin từ dữ liệu sẵn có để đi gần hơn đến các volumes lỗi.

2.3.1. Xác định ID của các volumes bị lỗi tương ứng

Bước này, mình sẽ xác định cụ thể ID của volume lỗi mất rbd_header object và 2 volumes lỗi mất rbd_headerrbd_id object.

$ sudo rbd -p VNDATA-0001 ls | xargs -n1 bash -c 'echo "$0: $(sudo rados -p VNDATA-0001 get rbd_id.$0 - strings)"'
image01: 163c90c86bdec6
image02: 12632c59ecee6c
error getting VNDATA-0001/rbd_id.image03: (2) No such file or directory
image03: 
error getting VNDATA-0001/rbd_id.image04: (2) No such file or directory
image04: 
image05: 12638ff6e985d4

Thông tin mình có hiện tại như sau:

  • image02: 12632c59ecee6c (lỗi mất rbd_header)
  • image03: không xác định được ID (lỗi mất rbd_header và rbd_id)
  • image04: không xác định được ID (lỗi mất rbd_header và rbd_id)

2.3.2. Xác định ID của volume lỗi mất rbd_id

Dựa vào rbd_data object để loại trừ các ID đã biết để tìm ra ID còn sót lại.

$ sudo rados ls -p VNDATA-0001 | grep rbd_data | awk -F'.' '{print $1"."$2}' | sort | uniq
rbd_data.123fb15cd8a797
rbd_data.12632c59ecee6c (image02)
rbd_data.126368a06c08b7
rbd_data.12638ff6e985d4 (image05)
rbd_data.163c90c86bdec6 (image01)

Đoạn này mới khó, không có rbd_headerrbd_id, chỉ có 2 ID không rõ nguồn gốc, công việc của mình là phải gán đúng 2 ID này với 2 volumes đã bị mất rbd_id object. Bỗng nhiên, mình phát hiện mỗi 1 rbd_data object đều có size là 4194304 iB = 4 MiB.

$ sudo rados -p VNDATA-0001 ls | head -n 5 | xargs -n1 bash -c 'echo "$0: $(sudo rados -p VNDATA-0001 stat $0 | grep -Eo "size [0-9]+")"'
rbd_data.12632c59ecee6c.0000000000003582: size 4194304
rbd_data.123fb15cd8a797.0000000000002cb9: size 4194304
rbd_data.123fb15cd8a797.00000000000014f7: size 4194304
rbd_data.123fb15cd8a797.00000000000007b0: size 4194304
rbd_data.163c90c86bdec6.0000000000000382: size 4194304

Mình có thể dựa vào dữ kiện này để phân biệt 2 ID còn lại của 2 volumes nào.

$ echo "rbd_data.123fb15cd8a797: $(($(sudo rados -p VNDATA-0001 ls | grep rbd_data.123fb15cd8a797 | wc -l)*4/1024)) GiB"
rbd_data.123fb15cd8a797: 50 GiB

$ echo "rbd_data.126368a06c08b7: $(($(sudo rados -p VNDATA-0001 ls | grep rbd_data.126368a06c08b7 | wc -l)*4/1024)) GiB"
rbd_data.126368a06c08b7: 0 GiB

(0 GiB là do bash shell làm tròn, nên dung lượng used của volume này < 1 GiB)

Lúc này, mình biết được hiện đang còn lại 2 volumes với dung lượng provisioned tương ứng.

NAME     PROVISIONED  USED   
image03       70 GiB   xx
image04        5 GiB   xx

Mình có thể thấy có 1 ID rbd_data.123fb15cd8a797 có dung lượng used50 GiB. Trong khi đó, volume image04 chỉ có dung lượng provisioned5 GiB, không thể chứa được 50 GiB dung lượng dữ liệu, nên ID này chỉ có thể thuộc về volume image03.

Lúc này mình đã biết được đầy đủ ID của các volumes lỗi:

  • image02: 12632c59ecee6c
  • image03: 123fb15cd8a797
  • image04: 126368a06c08b7

2.3.3. Recover rbd_id object

Mình sử dụng lệnh rados getrados put để gán lại ID cho các volumes đã bị mất rbd_id object.

# image03
$ sudo rados -p VNDATA-0001 get rbd_id.image01 rbd_id
$ sudo sed -i 's/163c90c86bdec6/123fb15cd8a797/g' rbd_id
$ sudo rados -p VNDATA-0001 put rbd_id.image03 rbd_id

# image04
$ sudo rados -p VNDATA-0001 get rbd_id.image01 rbd_id
$ sudo sed -i 's/163c90c86bdec6/126368a06c08b7/g' rbd_id
$ sudo rados -p VNDATA-0001 put rbd_id.image04 rbd_id

Kiểm tra lại xem rbd_id object đã được gán đúng ID chưa.

$ sudo rados -p VNDATA-0001 get rbd_id.image03 - strings
123fb15cd8a797
$ sudo rados -p VNDATA-0001 get rbd_id.image04 - strings
126368a06c08b7

2.3.4. Get các metadata từ rbd_header của volume không lỗi

Sử dụng lệnh rados listomapvals để lấy metadata của volume image01.

$ sudo rados -p VNDATA-0001 listomapvals rbd_header.163c90c86bdec6

Ở đây, mình chỉ phục hồi rbd_header object vừa đủ thông tin để có thể access vào volume và lấy dữ liệu, chứ không hướng đến việc phục hồi hoàn chỉnh rbd_header về lúc không lỗi, nên chỉ chú ý đến những metadata quan trọng:

  • features: 3d 00 00 00 00 00 00 00
  • object_prefix: 17 00 00 00 rbd_data.163c90c86bdec6
    • Mình sẽ dùng object_prefix tương ứng: 17 00 00 00 rbd_data.12632c59ecee6c (image02)
  • order: 16
  • size: 00 00 00 80 02 00 00 00
    • Mình có 100 GiB = 107374182400 iB (image02 có dung lượng 100 GiB)
    • Đổi qua mã hex, mình có 1900000000
    • Dịch qua mã hex 8 bytes mà máy có thể đọc hiểu: 00 00 00 00 19 00 00 00
  • snap_seq: 00 00 00 00 00 00 00 00

2.3.5. Recover rbd_header object

Recover rbd_header object bằng rados setomapval để gán các giá trị của metadata mà mình thu thập được ở 2.3.4.. Ở đây, mình sẽ ví dụ bằng image02 với ID 12632c59ecee6c, 2 volumes kia tương tự cách làm (chỉ thay rbd_data với ID tương ứng).

$ echo -en \\x3d\\x00\\x00\\x00\\x00\\x00\\x00\\x00 | sudo rados -p VNDATA-0001 setomapval rbd_header.12632c59ecee6c features
$ echo -en \\x17\\x00\\x00\\x00rbd_data.12632c59ecee6c | sudo rados -p VNDATA-0001 setomapval rbd_header.12632c59ecee6c object_prefix
$ echo -en \\x16 | sudo rados -p VNDATA-0001 setomapval rbd_header.12632c59ecee6c order
$ echo -en \\x00\\x00\\x00\\x00\\x19\\x00\\x00\\x00 | sudo rados -p VNDATA-0001 setomapval rbd_header.12632c59ecee6c size
$ echo -en \\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00 | sudo rados -p VNDATA-0001 setomapval rbd_header.12632c59ecee6c snap_seq

2.3.6. Kết quả

Kiểm tra kết quả sau khi recovery rbd_header object của image02

$ sudo rados -p VNDATA-0001 listomapvals rbd_header.12632c59ecee6c
features
value (8 bytes) :
00000000  3d 00 00 00 00 00 00 00                           |=.......|
00000008

object_prefix
value (27 bytes) :
00000000  17 00 00 00 72 62 64 5f  64 61 74 61 2e 31 32 36  |....rbd_data.126|
00000010  33 32 63 35 39 65 63 65  65 36 63                 |32c59ecee6c|
0000001b

order
value (1 bytes) :
00000000  16                                                |.|
00000001

size
value (8 bytes) :
00000000  00 00 00 00 19 00 00 00                           |........|
00000008

snap_seq
value (8 bytes) :
00000000  00 00 00 00 00 00 00 00                           |........|
00000008

$ sudo rbd -p VNDATA-0001 info image02
rbd image 'image02':
  size 100 GiB in 25600 objects
  order 22 (4 MiB objects)
  snapshot_count: 0
  id: 12632c59ecee6c
  block_name_prefix: rbd_data.12632c59ecee6c
  format: 2
  features: layering, exclusive-lock, object-map, fast-diff, deep-flatten
  op_features: 
  flags:

Dùng mount để kiểm tra data của volume.

$ sudo rbd -p VNDATA-0001 map image02 --name client.admin
/dev/rbd0
$ sudo mount /dev/rbd0 /mnt/
$ sudo df -hT /mnt/
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/rbd0      ext4   98G   71G   28G  72% /mnt

Kiểm tra toàn bộ rbd_header sau khi đã recover rbd_header object cho toàn bộ volumes.

sudo rbd -p VNDATA-0001 du
NAME     PROVISIONED  USED   
image01       10 GiB  4.1 GiB
image02      100 GiB   70 GiB
image03       70 GiB   50 GiB
image04        5 GiB   60 MiB
image05      150 GiB   20 GiB
      335 GiB  145 GiB

3. References