不完整的 PG -- 天哪!

linuxkidd

最近我有幸在一个 Firefly 集群 (0.80.8) 上工作,由于断电导致两个 OSD 发生故障。和许多技术问题一样,这并非故事的全部。断电和 OSD 故障发生的方式导致集群中的 5 个 Placement Group (PG) 进入不完整状态。在我介入之前,故障的 OSD 已经被从集群中弹出,并部署了新的 OSD 来替代它们。

好消息是,其中一个“故障”的 OSD 在大部分情况下仍然可读,这使得我们可以使用一个新工具来恢复 PG 内容。

警告:这是一个有风险的过程! 未经 Red Hat Ceph 支持人员协助,请勿在生产集群上尝试此操作。您可能会导致集群中数据不可逆转地丢失。ol.LKmainlist>li { padding-top: 5px; } ol.LKmainlist>li:first-line { font-weight: bold; } .LKwarning { padding-left: 30px; }

  1. 下载 'ceph-test' 软件包

    • 如果在运行 Dumpling,目前用于此过程的工具不可用,也不期望被反向移植到 Ceph Dumpling。尝试在 Dumpling OSD 上使用 Firefly 或 Giant 版本的 'ceph-test' 软件包几乎肯定会导致 OSD 无法在 Dumpling 集群中运行。

    • 如果在运行 Firefly (0.80.8 或更早版本),请使用 Ceph Giant 'ceph-test' 软件包

      注: 已经提交了一个拉取请求,将 Giant 中引入的所有工具反向移植到 Firefly。截至本文撰写之时,这些工具在 Firefly 版本中的可用性没有 ETA,但您应该在转向 Giant 版本之前尝试 0.80.9 及更高版本 Firefly 中的 'ceph-test' 软件包。

    • 如果在运行 Giant 或更高版本,请使用您特定 Ceph 版本的 'ceph-test' 软件包。

  2. 安装新下载的软件包

    • DEB 发行版:# dpkg -i ceph-test*.deb
    • RPM 发行版:# rpm -ivh ceph-test*.rpm
  3. 挂载旧的 OSD 磁盘:# mkdir /mnt/old # mount /dev/sdX1 /mnt/old

  4. 确定您需要的 PG:# ceph health detail | grep incomplete 示例:

    # ceph health detail | grep incomplete pg 6.399 is incomplete, acting [18,19,9] pg 3.16f is incomplete, acting [20,8,21] pg 3.c2 is incomplete, acting [30,25,4] pg 6.84 is incomplete, acting [28,21,8] pg 3.6a is incomplete, acting [30,27,8]

  5. 查看不完整的 PG 是否在旧的 OSD 上:# ls -ld /mnt/old/current/<pg.id>_head 示例: # ls -ld /mnt/old/current/6.399_head drwxr-xr-x 2 root root 6 Feb 6 17:22 /mnt/old/current/6.399_head

  6. 确定磁盘上 PG 内容的大小:# du -sh /mnt/old/current/<pg.id>_head 示例: # du -sh /mnt/old/current/6.399_head 115M /mnt/old/current/6.399_head

  7. 确认您在目标导出路径上有足够的空间:# df -h ./ 示例: # df -h ./ Filesystem Size Used Avail Use% Mounted on /dev/sde4 60G 9.7G 47G 18% /

  8. 导出相关 PG:# ceph_objectstore_tool --op export --pgid <pg.id> --data-path /mnt/old --journal-path /mnt/old/journal --file <pg.id>.export

    : 如果您得到类似于下面的堆栈跟踪,您的日志可能已损坏。您可以通过添加 '--skip-journal-replay' 选项来克服此问题,但请注意,日志中未刷新到后端文件存储的任何数据将从导出中丢失。在大多数情况下,这比丢失 PG 的全部内容要好。在我工作的集群中,OSD 日志确实已损坏,因此在我的示例命令中,我使用了 '--skip-journal-replay' 选项。

    日志损坏的示例崩溃

    os/FileJournal.cc: In function 'void FileJournal::wrap_read_bl(off64_t, int64_t, ceph::bufferlist*, off64_t*)' thread 7f219004a780 time 2015-02-26 19:06:41.087861 os/FileJournal.cc: 1638: FAILED assert(0) ceph version 0.87.1 (283c2e7cfa2457799f534744d7d549f83ea1335e) 1: (ceph::__ceph_assert_fail(char const*, char const*, int, char const*)+0x7f) [0xae272f] 2: (FileJournal::wrap_read_bl(long, long, ceph::buffer::list*, long*)+0x2aa) [0x9b12ca] 3: (FileJournal::do_read_entry(long, long*, ceph::buffer::list*, unsigned long*, std::ostream*, FileJournal::entry_header_t*)+0x2f0) [0x9bb6b0] 4: (FileJournal::read_entry(ceph::buffer::list&, unsigned long&, bool*)+0x29e) [0x9bc25e] 5: (FileJournal::open(unsigned long)+0x6b5) [0x9b9d95] 6: (JournalingObjectStore::journal_replay(unsigned long)+0x139) [0x930729] 7: (FileStore::mount()+0x31d7) [0x905c97] 8: (main()+0x1774) [0x61c7f4] 9: (__libc_start_main()+0xed) [0x7f218cd2876d] 10: ceph_objectstore_tool() [0x624b09]

    PG 导出示例: # ceph_objectstore_tool --op export --pgid 6.399 --data-path /mnt/old --journal-path /mnt/old/journal --skip-journal-replay --file 6.399.export Exporting 6.399 Read b9183399/rb.0.599b0.7b1f49e.000000000cf3/head//6 Read c76e7399/rb.0.599b0.7b1f49e.000000000bd5/head//6 Read 3a00a399/rb.0.599b9.9537f2e.000000000480/head//6 Read 18c9c399/rb.0.599b9.9537f2e.0000000018fb/head//6 Read eb51799/rb.0.599b9.9537f2e.000000001c75/head//6 Read 84cc2799/rb.0.599b9.9537f2e.000000000682/head//6 Read 95235799/rb.0.599b0.7b1f49e.000000001b83/head//6 Read 7b826799/rb.0.599b9.9537f2e.000000002076/head//6 Read a6549799/rb.0.599b9.9537f2e.000000000ec1/head//6 Read 3566d799/rb.0.599b0.7b1f49e.000000001cec/head//6 Read 93ced799/rb.0.599b6.664d4c7e.0000000007f2/head//6 Read 9bb2e799/rb.0.599b9.9537f2e.0000000014d8/head//6 Read 8ddd0b99/rb.0.599b9.9537f2e.00000001b801/head//6 Read 13c72b99/rb.0.599b0.7b1f49e.0000000009dc/head//6 Read 372d2b99/rb.0.599b0.7b1f49e.00000000102e/head//6 Read beb3b99/rb.0.599b9.9537f2e.0000000016ee/head//6 Read 6ba1ab99/rb.0.599b6.664d4c7e.000000005602/head//6 Read df5afb99/rb.0.599b0.7b1f49e.00000000087d/head//6 Read a32f0f99/rb.0.599b9.9537f2e.0000000003e4/head//6 Read 6f52f99/rb.0.599b6.664d4c7e.00000001c003/head//6 Read a11b3f99/rb.0.599b9.9537f2e.0000000005a5/head//6 Read e2154f99/rb.0.599b0.7b1f49e.0000000009bb/head//6 Read a7946f99/rb.0.599b6.664d4c7e.000000000f80/head//6 Read 69638f99/rb.0.599b9.9537f2e.00000001f3ff/head//6 Read 80159f99/rb.0.599b0.7b1f49e.000000001c31/head//6 Read 30b1af99/rb.0.599b6.664d4c7e.000000001365/head//6 Read 919fbf99/rb.0.599b9.9537f2e.000000001266/head//6 Read a1fcf99/rb.0.599b0.7b1f49e.00000000115a/head//6 Read d5bbff99/rb.0.599b0.7b1f49e.00000000082b/head//6 Export successful

  9. 设置 'noout' 标志:# ceph osd set noout

  10. 如果您之前尝试过 Linux 文件复制 (cp, rsync 等) PG 内容,则必须从其 acting OSD 中删除 PG 内容。

    : 我,以及我确信的许多其他人...尝试了这种方法并失败了。我尝试了 'cp -r src/current/6.399_head dest/current/6.399_head',并且..认为失败是由于缺少 xattrs,我甚至使用了 'rsync -aXP src/current/6.399_head/ dest/current/6.399_head/'。PG 文件夹结构中的裸文件只是数据在集群中运行所需的一部分。还有其他元数据没有被 Linux 文件复制工具捕获,因此导致磁盘上的数据无法使用。使用 Linux 文件复制工具复制的数据将导致 OSD 进程在下次启动时崩溃。

    1. 确认哪些 OSD 正在为相关 PG 工作(来自步骤 4 的输出): 示例输出:pg 6.399 is incomplete, acting [18,19,9] 这表明 OSD 18、19 和 9 正在存储 PG 6.399(OSD ID 在 [] 中)。
    2. 确定存储 OSD 的主机:# ceph osd find ## 示例: # ceph osd find 18 { "osd": 18, "ip": "192.168.22.5:6801\/3806", "crush_location": { "host": "osdnode2", "root": "default"}}
    3. SSH 到主机(通过主机名或 'ceph osd find' 提供的 IP 地址)。 # ssh <hostname or IP addr>
    4. 停止 OSD 进程
      • Upstart:# stop ceph-osd id=##
      • Sysvinit:# service ceph stop osd.##
    5. 将 PG 的内容移开:# mkdir # mv /var/lib/ceph/osd/ceph-##/current/<pg.id>_head 示例:# mkdir /root/backup # mv /var/lib/ceph/osd/ceph-18/current/6.399_head /root/backup
    6. 启动 OSD
      • Upstart:# start ceph-osd id=##
      • Sysvinit:# service ceph start osd.##
    7. 对其他 acting OSD 重复步骤 10.b 到 10.f(在上面的示例中,OSD 19 和 9)。
  11. 停止您要导入 PG 的 OSD 进程

    • Upstart:# stop ceph-osd id=##
    • Sysvinit:# service ceph stop osd.##

-=- 警告 -=- 警告 -=- 警告 -=- 警告 -=- 警告 -=-

从这里开始存在不可逆转的集群数据丢失风险。强烈建议在继续之前联系 Red Hat Ceph 支持人员,尤其是在生产集群上!

强烈建议的最小化风险方法是部署一个新的 OSD 并将其权重设置为 0(ceph osd crush reweight osd.# 0),以防止数据流向新的 OSD,然后将 PG 导入到这个新的、权重为 0 的 OSD 中。一旦过程完成,您可以从集群中删除这个新的 OSD。这个新的临时 OSD 可以部署到一个备用分区,甚至部署到现有 OSD 驱动器上的一个目录。

在现有 OSD 驱动器上创建临时 OSD 的示例: # mkdir /var/lib/ceph/osd/ceph-0/tmposd/ # ceph-disk prepare /var/lib/ceph/osd/ceph-0/tmposd/ # ceph-disk activate /var/lib/ceph/osd/ceph-0/tmposd/ # ceph osd crush reweight osd.$(cat /var/lib/ceph/osd/ceph-0/tmposd/whoami) 0 获取新临时 OSD 的 OSD ID:cat /var/lib/ceph/osd/ceph-0/tmposd/whoami

-=- 警告 -=- 警告 -=- 警告 -=- 警告 -=- 警告 -=-

  1. 导入相关 PG 注: 导入 *无需* 在 PG 映射到的 OSD 之一上进行。数据将在 OSD 进程启动时回填到正确的位置。

    # ceph_objectstore_tool --op import --data-path /var/lib/ceph/osd/ceph-## --journal-path /var/lib/ceph/osd/ceph-##/journal --file <pg.id>.export

    PG 导入示例: # ceph_objectstore_tool --op import --data-path /var/lib/ceph/osd/ceph-22 --journal-path /var/lib/ceph/osd/ceph-22/journal --file 6.399.export Importing pgid 6.399 Write b9183399/rb.0.599b0.7b1f49e.000000000cf3/head//6 Write c76e7399/rb.0.599b0.7b1f49e.000000000bd5/head//6 Write 3a00a399/rb.0.599b9.9537f2e.000000000480/head//6 Write 18c9c399/rb.0.599b9.9537f2e.0000000018fb/head//6 Write eb51799/rb.0.599b9.9537f2e.000000001c75/head//6 Write 84cc2799/rb.0.599b9.9537f2e.000000000682/head//6 Write 95235799/rb.0.599b0.7b1f49e.000000001b83/head//6 Write 7b826799/rb.0.599b9.9537f2e.000000002076/head//6 Write a6549799/rb.0.599b9.9537f2e.000000000ec1/head//6 Write 3566d799/rb.0.599b0.7b1f49e.000000001cec/head//6 Write 93ced799/rb.0.599b6.664d4c7e.0000000007f2/head//6 Write 9bb2e799/rb.0.599b9.9537f2e.0000000014d8/head//6 Write 8ddd0b99/rb.0.599b9.9537f2e.00000001b801/head//6 Write 13c72b99/rb.0.599b0.7b1f49e.0000000009dc/head//6 Write 372d2b99/rb.0.599b0.7b1f49e.00000000102e/head//6 Write beb3b99/rb.0.599b9.9537f2e.0000000016ee/head//6 Write 6ba1ab99/rb.0.599b6.664d4c7e.000000005602/head//6 Write df5afb99/rb.0.599b0.7b1f49e.00000000087d/head//6 Write a32f0f99/rb.0.599b9.9537f2e.0000000003e4/head//6 Write 6f52f99/rb.0.599b6.664d4c7e.00000001c003/head//6 Write a11b3f99/rb.0.599b9.9537f2e.0000000005a5/head//6 Write e2154f99/rb.0.599b0.7b1f49e.0000000009bb/head//6 Write a7946f99/rb.0.599b6.664d4c7e.000000000f80/head//6 Write 69638f99/rb.0.599b9.9537f2e.00000001f3ff/head//6 Write 80159f99/rb.0.599b0.7b1f49e.000000001c31/head//6 Write 30b1af99/rb.0.599b6.664d4c7e.000000001365/head//6 Write 919fbf99/rb.0.599b9.9537f2e.000000001266/head//6 Write a1fcf99/rb.0.599b0.7b1f49e.00000000115a/head//6 Write d5bbff99/rb.0.599b0.7b1f49e.00000000082b/head//6 Import successful

  2. 启动 OSD 进程

    • Upstart:# start ceph-osd id=##
    • Sysvinit:# service ceph start osd.##
  3. 监控集群,直到 PG 从 'incomplete' 计数中消失,并出现在 'backfilling' 和 'active+clean' 计数中:watch -n 1 ceph -s 按 Ctrl-C 退出 'watch' 会话

  4. 对每个需要恢复的 PG ID 重复此操作。

  5. 删除您的临时 OSD(如果使用 - 强烈建议,请参阅上面的警告部分)

    1. 停止 OSD 进程
      • Upstart:# stop ceph-osd id=##
      • Sysvinit:# service ceph stop osd.##
    2. 从 Ceph 的 Crush、OSD 和 Auth 区域删除 OSD:# ceph osd crush remove osd.## # ceph osd rm osd.## # ceph auth del osd.##
    3. 删除 OSD 的内容,使其在重启时不会被重新挂载/激活: rm -rf /var/lib/ceph/osd/ceph-##/* 确保您删除的是正确的 OSD 目录!!!
  6. 所有恢复完成后,删除 'noout' 标志:ceph osd unset noout

  7. 一旦您对集群状态满意,您可以返回并删除您创建的所有 PG 备份目录。 示例: # ssh osdnode2 rm -f /root/backup/*

如果一切顺利,您的集群现在已恢复到 100% active+clean / HEALTH_OK 状态。请注意,PG 中可能仍然存储有不一致或过期的数据。这是因为故障 OSD 上的数据状态有些未知,特别是如果您在导出时不得不使用 '--skip-journal-replay' 选项。对于 RBD 数据,使用 RBD 的客户端应该对 RBD 运行文件系统检查。

我必须强调,此过程在生产集群上非常危险,强烈建议仅在 Red Hat Ceph 支持人员的帮助下执行。