将设为首页浏览此站
开启辅助访问 天气与日历 收藏本站联系我们

易陆发现论坛

 找回密码
 开始注册
易陆发现论坛 门户 openstack频道 查看内容

ceph 分布式存储mds 修复

2022-8-4 13:42| 发布者: admin| 查看: 1| 评论: 7|原作者: admin

摘要: 解决:两种方法:方法一:#ceph mds repaired AI_Spacefs:0#ceph mds repaired AI_Spacefs:1 此方法一般情况下是好使,当有osd stuck的状态时也会失效。这个时候要重启osd,若未发现stuck就要手动触发数据迁移把stuck ...


这里实验在使用cephfs时,如果cephfs的元数据损坏或丢失了,那该如何恢复出用户数据。下面就为大家演示下如何恢复。



我是基于L版本做的实验,J版也是可以的。测试环境如下:

[root@ceph05 ~]# ceph -v
ceph version 12.2.11 (26dc3775efc7bb286a1d6d66faee0ba30ea23eee) luminous (stable)

[root@ceph05 ~]# ceph osd tree
ID CLASS WEIGHT  TYPE NAME       STATUS REWEIGHT PRI-AFF 
-1       0.07397 root default                            
-3       0.03699     host ceph05                         
 0   hdd 0.01799         osd.0       up  1.00000 1.00000 
 1   hdd 0.01900         osd.1       up  1.00000 1.00000 
-5       0.03699     host ceph06                         
 2   hdd 0.01900         osd.2       up  1.00000 1.00000 
 3   hdd 0.01900         osd.3       up  1.00000 1.00000

[root@ceph05 deployceph]# ceph -s
  cluster:
    id:     176feab8-ca22-47bf-b809-202deac53c6f
    health: HEALTH_WARN
            crush map has straw_calc_version=0

  services:
    mon: 1 daemons, quorum ceph05
    mgr: ceph05(active)
    mds: cephfs-1/1/1 up  {0=ceph05=up:active}
    osd: 4 osds: 4 up, 4 in

  data:
    pools:   10 pools, 304 pgs
    objects: 918 objects, 2.60GiB
    usage:   6.70GiB used, 71.3GiB / 78.0GiB avail
    pgs:     304 active+clean

2.2、准备测试数据

挂载kc

[root@ceph05 deployceph]# mount -t ceph 192.168.10.30:/ /cephfs
[root@ceph05 deployceph]# df -h|grep ceph
···
192.168.10.30:/           78G  6.8G   72G   9% /cephfs
···

写入数据(这里我写入了几个比较有代表性的文件类型:txt、jpg、png、pdf、word、excel)

[root@ceph05 deployceph]# ll /cephfs/
total 5912
-rw-r--r-- 1 root root   31232 Mar 15 12:18 111.doc
-rw-r--r-- 1 root root   20593 Mar 15 12:18 22.xlsx
-rw-r--r-- 1 root root   12494 Mar 15 12:17 5be23a3eec2c0.png
-rw-r--r-- 1 root root    3189 Mar 15 12:17 cmap.txt
-rw-r--r-- 1 root root 5985243 Mar 15 12:17 hello0.pdf


这里直接模拟元数据丢失的情况,删除metadata池里面所有的元数据对象:

[root@ceph05 deployceph]# rados -p metadata ls|xargs -i rados -p metadata rm {}
[root@ceph05 deployceph]# ceph df
GLOBAL:
    SIZE        AVAIL       RAW USED     %RAW USED 
    78.0GiB     71.3GiB      6.71GiB          8.60 
POOLS:
    NAME                          ID     USED        %USED     MAX AVAIL     OBJECTS 
    .rgw.root                     1      1.09KiB         0       63.3GiB           4 
    default.rgw.control           2           0B         0       63.3GiB           8 
    default.rgw.meta              3         720B         0       63.3GiB           5 
    default.rgw.log               4           0B         0       63.3GiB         207 
    default.rgw.buckets.index     5           0B         0       63.3GiB           1 
    default.rgw.buckets.data      6      1.02KiB         0       63.3GiB           2 
    pool01                        7      2.60GiB      3.94       63.3GiB         666 
    rbd                           8          36B         0       63.3GiB           4 
    metadata                      17          0B         0       63.3GiB           0 
    data                          18     5.77MiB         0       63.3GiB           6

看到metadata池里面没有对象了已经,重启下mds看效果,因为mds里面会缓存元数据信息,所以要重启下mds:

[root@ceph05 deployceph]# systemctl restart ceph-mds@ceph05
[root@ceph05 deployceph]# 
[root@ceph05 deployceph]# 
[root@ceph05 deployceph]# ceph -s
  cluster:
    id:     176feab8-ca22-47bf-b809-202deac53c6f
    health: HEALTH_WARN
            1 filesystem is degraded
            1 filesystem has a failed mds daemon
            crush map has straw_calc_version=0

  services:
    mon: 1 daemons, quorum ceph05
    mgr: ceph05(active)
    mds: cephfs-0/1/1 up , 1 failed
    osd: 4 osds: 4 up, 4 in

  data:
    pools:   10 pools, 304 pgs
    objects: 905 objects, 2.60GiB
    usage:   6.71GiB used, 71.3GiB / 78.0GiB avail
    pgs:     304 active+clean

看到集群现在不正常了,访问kc里面的数据卡住,说明数据已经无法正常读取了。


使用我编写的py脚本(文末给出了源码)恢复,把脚本放到集群任意一台节点上执行:

[root@ceph05 rcy]# python recovery_cephfs.py -p data

-p指定cephfs的数据池,运行完之后会在当前目录下产生两个文件夹和一个运行脚本的日志文件。

[root@ceph05 rcy]# ll
total 16
-rw-r--r-- 1 root root 3826 Mar 15 13:57 recovery_cephfs.py
drwxr-xr-x 2 root root  120 Mar 15 13:57 recoveryfiles
-rw-r--r-- 1 root root 4804 Mar 15 13:57 recovery.log
drwxr-xr-x 2 root root 4096 Mar 15 13:57 recoveryobjs

查看恢复出来的文件在recoveryfiles文件夹下:

[root@ceph05 rcy]# ll recoveryfiles/
total 12364
-rw-r--r-- 1 root root 5985243 Mar 15 13:57 10000000000-pdf
-rw-r--r-- 1 root root    3189 Mar 15 13:57 10000000001-text
-rw-r--r-- 1 root root   12494 Mar 15 13:57 10000000002-png
-rw-r--r-- 1 root root   31232 Mar 15 13:57 10000000003-text
-rw-r--r-- 1 root root   20593 Mar 15 13:57 10000000004-excel

文件名格式为”文件在cephfs里面的inode-该文件可能的类型“。恢复出来的文件名后面会给出该文件的类型。这样就可以使用合适的软件打开该文件来验证文件是否完整。


在cephfs文件系统的元数据完全损坏的情况下,只要数据池对象不丢失,就可以恢复出完整的数据。恢复的思路如下:

  1. 获取数据池对象
  2. 根据inode找到该文件的所有对象
  3. 拼接对象

使用脚本注意事项:

  1. 现在的脚本在只加入了txt、jpg、png、pdf、word、excel这些文件类型的识别,需要其他的就需要自己加入到脚本里面了
  2. 只适合副本池
  3. 如果数据量特别大,不适合使用脚本,不过可以参考脚本的思路去一个一个文件恢复


脚本
# coding: utf-8
import os
import shutil
import json
import sys
import subprocess
import copy
import logging
import argparse

__auth__ = 'ypdai'

SLEEP_INTERVAL = 1
logging.basicConfig(filename='./recovery.log', format='%(asctime)s : %(levelname)s  %(message)s',
                    level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S')

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
RECOVERY_OBJ_DIR = os.path.join(BASE_DIR, 'recoveryobjs')
RECOVERY_FILE_DIR = os.path.join(BASE_DIR, 'recoveryfiles')


def exec_cmd(cmd):
    """
    执行shell命令,并返回标准输出和执行状态码
    :param cmd:
    :return:
    """
    logging.info('exec_cmd():: cmd: {}'.format(cmd))
    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    p.wait()
    out = p.stdout.read()
    code = p.returncode
    logging.info('exec_cmd():: cmd exec out: {}, code: {}'.format(out, code))
    return out, code


def prepare(pool_name):
    """
    准备恢复条件
    1、检查所给pool name是不是cephfs的数据池
    2、创建recoveryobjs和recoveryfiles文件加
    :param pool_name:
    :return:
    """
    cmd = 'ceph fs ls -f json-pretty'
    out, code = exec_cmd(cmd)
    out = json.loads(out)
    for cnt in out:
        if pool_name not in cnt.get('data_pools'):
            return False

    if os.path.isdir(RECOVERY_OBJ_DIR):
        shutil.rmtree(RECOVERY_OBJ_DIR)
    os.mkdir(RECOVERY_OBJ_DIR)

    if os.path.isdir(RECOVERY_FILE_DIR):
        shutil.rmtree(RECOVERY_FILE_DIR)
    os.mkdir(RECOVERY_FILE_DIR)

    return True


def get_file_type(file_path):
    cmd = 'file %s' % file_path
    out, code = exec_cmd(cmd)
    out = out.split(':')[-1].lower()

    file_type = 'text'
    if 'word' in out:
        file_type = 'word'
    elif 'excel' in out:
        file_type = 'excel'
    elif 'pdf' in out:
        file_type = 'pdf'
    elif 'text' in out:
        file_type = 'text'
    elif 'jpeg' in out:
        file_type = 'jpg'
    elif 'png' in out:
        file_type = 'png'

    return file_type


def do_recovery(pool_name):
    """
    具体执行恢复,大概恢复逻辑如下:
    1、从数据池里面获取所有的数据对象
    2、找到每个文件的head对象,然后把数据这个文件的其他对象内容写入head对象里面
    3、根据head对象的文件类型,推测该文件的实际类型
    :param pool_name:
    :return:
    """
    cmd = 'for obj in $(rados -p %s ls);do rados -p %s get ${obj} %s/${obj};done' % (
        pool_name, pool_name, RECOVERY_OBJ_DIR)
    out, code = exec_cmd(cmd)

    if code != 0:
        logging.error('do_recovery():: get obj from rados failed.')
        return

    cmd = 'ls %s' % RECOVERY_OBJ_DIR
    out, code = exec_cmd(cmd)
    if code != 0:
        logging.error('do_recovery():: list obj failed.')
        return

    done_lst = []
    objects = out.split()
    for obj in objects:
        inode, number = obj.split('.')
        if inode in done_lst:
            continue

        cmd = '''ls -l %s | awk '{print $NF}' | grep ^%s |sort''' % (RECOVERY_OBJ_DIR, inode)
        out, code = exec_cmd(cmd)
        files = out.split('\n')

        head_file = files[0]
        file_type = get_file_type('%s/%s' % (RECOVERY_OBJ_DIR, head_file))
        cmd = 'cp %s/%s %s/%s-%s' % (RECOVERY_OBJ_DIR, head_file, RECOVERY_FILE_DIR, inode, file_type)
        out, code = exec_cmd(cmd)
        for f in files[1:]:
            if not f:
                continue
            cmd = 'cat %s/%s >> %s/%s-%s' % (RECOVERY_OBJ_DIR, f, RECOVERY_FILE_DIR, inode, file_type)
            out, code = exec_cmd(cmd)

        done_lst.append(inode)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-p', '--pool', required=True, type=str, dest='pool',
                        help='select given cephfs data pool by name')
    args = parser.parse_args()

    if not prepare(args.pool):
        logging.error('main():: invalid pool name.')
        sys.exit(1)

    logging.info('=== main():: recovery start')
    do_recovery(args.pool)
    logging.info('=== main():: recovery done')





鲜花

握手

雷人

路过

鸡蛋
发表评论

最新评论

admin 2022-8-3 11:47
元数据故障恢复
设置允许多文件系统

ceph fs flag set enable_multiple true --yes-i-really-mean-it
创建一个新的元数据池,这里是为了不去动原来的metadata的数据,以免损坏原来的元数据

ceph osd pool create recovery 8
将老的存储池data和新的元数据池recovery关联起来并且创建一个新的recovery-fs

[root@lab102 ~]# ceph fs new recovery-fs recovery data --allow-dangerous-metadata-overlay
new fs with metadata pool 3 and data pool 2
做下新的文件系统的初始化相关工作

[root@lab102 ~]#cephfs-data-scan init --force-init --filesystem recovery-fs --alternate-pool recovery
reset下新的fs

[root@lab102 ~]#ceph fs reset recovery-fs --yes-i-really-mean-it
[root@lab102 ~]#cephfs-table-tool recovery-fs:all reset session
[root@lab102 ~]#cephfs-table-tool recovery-fs:all reset snap
[root@lab102 ~]#cephfs-table-tool recovery-fs:all reset inode
做相关的恢复

[root@lab102 ~]# cephfs-data-scan scan_extents --force-pool --alternate-pool recovery --filesystem ceph  data
[root@lab102 ~]# cephfs-data-scan scan_inodes --alternate-pool recovery --filesystem ceph --force-corrupt --force-init data
[root@lab102 ~]# cephfs-data-scan scan_links --filesystem recovery-fs
[root@lab102 ~]# systemctl start ceph-mds@lab102
等待mds active 以后再继续下面操作
[root@lab102 ~]# ceph daemon mds.lab102 scrub_path / recursive repair
设置成默认的fs

[root@lab102 ~]# ceph fs set-default recovery-fs
挂载检查数据

[root@lab102 ~]#  mount -t ceph 192.168.19.102:/ /mnt
[root@lab102 ~]# ll /mnt
total 0
drwxr-xr-x 1 root root 1 Jan  1  1970 lost+found
[root@lab102 ~]# ll /mnt/lost+found/
total 226986
-r-x------ 1 root root   569306 May 25 16:16 10000000001
-r-x------ 1 root root 16240627 May 25 16:16 10000000002
-r-x------ 1 root root  1356367 May 25 16:16 10000000003
-r-x------ 1 root root   137729 May 25 16:16 10000000004
-r-x------ 1 root root   155163 May 25 16:16 10000000005
-r-x------ 1 root root   118909 May 25 16:16 10000000006
-r-x------ 1 root root  1587656 May 25 16:16 10000000007
-r-x------ 1 root root   252705 May 25 16:16 10000000008
-r-x------ 1 root root  1825192 May 25 16:16 10000000009
-r-x------ 1 root root   156990 May 25 16:16 1000000000a
-r-x------ 1 root root  3493435 May 25 16:16 1000000000b
-r-x------ 1 root root   342390 May 25 16:16 1000000000c
-r-x------ 1 root root  1172247 May 25 16:16 1000000000d
-r-x------ 1 root root  2516169 May 25 16:16 1000000000e
-r-x------ 1 root root  3218770 May 25 16:16 1000000000f
-r-x------ 1 root root   592729 May 25 16:16 10000000010
可以看到在lost+found里面就有数据了

[root@lab102 ~]# file /mnt/lost+found/10000000010
/mnt/lost+found/10000000010: Microsoft PowerPoint 2007+
[root@lab102 ~]# file /mnt/lost+found/10000000011
/mnt/lost+found/10000000011: Microsoft Word 2007+
[root@lab102 ~]# file /mnt/lost+found/10000000012
/mnt/lost+found/10000000012: Microsoft Word 2007+
[root@lab102 ~]# file /mnt/lost+found/10000000013
/mnt/lost+found/10000000013: Microsoft PowerPoint 2007+
这个生成的文件名称就是实际文件存储的数据的prifix,也就是通过原始inode进行的运算得到的

如果提前备份好了原始的元数据信息

[root@lab102 ~]# ceph daemon mds.lab102 dump cache > /tmp/mdscache
那么可以比较轻松的找到丢失的文件
admin 2022-8-3 11:48
解决:两种方法:

方法一:

  #ceph mds repaired AI_Spacefs:0
  #ceph mds repaired AI_Spacefs:1
此方法一般情况下是好使,当有osd stuck的状态时也会失效。这个时候要重启osd,若未发现stuck就要手动触发数据迁移把stuck的osd暴露出来。然后再执行上面的操作。
经测试,有两次都是通过上述方法解决问题的。

方法二:
这次问题要严重的多,所有的mds都没有active的状态,造成元数据无法恢复,方法一失效,所以此时需要放弃原来的cephfs,重建构建基于原来data池生成新的cephfs.

元数据故障恢复

设置允许多文件系统

ceph fs flag set enable_multiple true --yes-i-really-mean-it
创建一个新的元数据池,这里是为了不去动原来的metadata的数据,以免损坏原来的元数据

ceph osd pool create recovery 8
将老的存储池data和新的元数据池recovery关联起来并且创建一个新的recovery-fs

ceph fs new recovery-fs recovery AI_Spacefs_data --allow-dangerous-metadata-overlay
做下新的文件系统的初始化相关工作

#cephfs-data-scan init --force-init --filesystem recovery-fs --alternate-pool recovery
2020-03-18T16:22:50.508+0800 7f4a11a1d700 -1 NetHandler create_socket couldn't create socket (97) Address family not supported by protocol
出现上述的错误可以忽略进行下一步。

reset下新的fs

#ceph fs reset recovery-fs --yes-i-really-mean-it
若失败,要把所有mds fail掉或者stop掉,再快速执行上面命令。
#cephfs-table-tool recovery-fs:all reset session
#cephfs-table-tool recovery-fs:all reset snap
#cephfs-table-tool recovery-fs:all reset inode
出现Address family not supported by protocol的错误忽略掉
做相关的恢复

做下一步之前确保新建的recovery-fs没有active的mds,有则stop掉,不然该mds容易crashed。
#cephfs-data-scan scan_extents --force-pool --alternate-pool recovery --filesystem AI_Spacefs AI_Spacefs_data
#cephfs-data-scan scan_inodes --alternate-pool recovery --filesystem AI_Spacefs -force-corrupt --force-init AI_Spacefs_data
#cephfs-data-scan scan_inodes --alternate-pool recovery --filesystem AI_Spacefs --force-corrupt --force-init AI_Spacefs_data
#cephfs-data-scan scan_links --filesystem recovery-fs
出现Address family not supported by protocol的错误忽略掉

# systemctl start ceph-mds@node82
等待mds active 以后再继续下面操作
# ceph daemon mds.node82 scrub_path / recursive repair
事实上上面这一步并没有操作数据就已经恢复了。
设置成默认的fs

# ceph fs set-default recovery-fs
挂载检查数据

[root@node82 lyf3]# ls
DATASET  lost+found  SYSTEM  USER
[root@node82 lyf3]# ll lost+found/
total 1
-r-x------ 1 root root 237 Mar 11 13:21 1000172efb8
可以看到在lost+found里面就有数据了这个生成的文件名称就是实际文件存储的数据的prifix,也就是通过原始inode进行的运算得到的。

如果提前备份好了原始的元数据信息

# ceph daemon mds.node82 dump cache > /tmp/mdscache
那么可以比较轻松的找到丢失的文件
admin 2022-8-3 11:49
针对原因一:

此次情况比较特殊,ceph health detail命令竟然看不到stuck的osd,解决如下:
把 使用率比较高的osd降权重,手动触发osd的数据迁移。
重新ceph health detail命令,此时会发现stuck的osd,重启相应的osd,mds状态恢复。
针对原因二(收效甚微):

调小mds mds_cache_memory_limit到40G 以下
# ceph tell mds.\* injectargs '--mds_cache_memory_limit=40000000000
在/etc/ceph/ceph.conf修改,传到所有ceph节点
针对原因三(主要原因):

方法一 mds多活热备调低敏感度,有两个操作:

调高mds_beacon_grace=300,调高超时时间。

  写到/etc/ceph/ceph.conf里,传到所有mds节点,然后重启mds
1
mds采用热备模式,替换原来冷备模式,加快mds加入速度。

  ceph fs set <fs name> allow_standby_replay true
1
方法二 mds多活热备静态目录方法,即给目录划分不同的mds

命令:
setfattr -n ceph.dir.pin -v 2 path/to/dir
mkdir -p a/b
# “a” and “a/b” both start without an export pin set
setfattr -n ceph.dir.pin -v 1 a/
# a and b are now pinned to rank 1
setfattr -n ceph.dir.pin -v 0 a/b
# a/b is now pinned to rank 0 and a/ and the rest of its children are still pinned to rank 1
admin 2022-8-3 11:51
1.创建 cephfs
一个cephfs至少要求两个librados存储池,一个为data,一个为metadata。

step 1.创建cephfs存储池fs_metadata,fs_data

# 元数据库存储池
ceph osd pool create cephfs_metadata 8 8
# 数据
ceph osd pool create cephfs_data 8 8
step 2.使用fs new命令enable 文件系统

ceph fs new cephfs cephfs_metadata cephfs_data
使用mds来观察

[root@ceph-stroage01 ~]# ceph fs ls
name: cephfs, metadata pool: fs_metadata, data pools: [fs_data ]
[root@ceph-stroage01 ~]#
文件系统创建完毕后, MDS 服务器就能达到 active 状态了,比如在一个单 MDS 系统中:

[root@ceph-stroage01 ~]# ceph mds stat
cephfs:0
[root@ceph-stroage01 ~]#
查看集群监控状态:

[root@ceph-stroage01 ~]# ceph -s
  cluster:
    id:     db51539c-d566-11eb-a3e9-e6f1aaf957fd
    health: HEALTH_ERR
            2 filesystems are offline
            2 filesystems are online with fewer MDS than max_mds

  services:
    mon: 3 daemons, quorum ceph-stroage01,ceph-stroage02,ceph-stroage03 (age 9m)
    mgr: ceph-stroage01.cdthyk(active, since 9m), standbys: ceph-stroage03.dtasrq
    mds: cephfs:0 cephfs_k8s:0
    osd: 6 osds: 6 up (since 9m), 6 in (since 2w)

  data:
    pools:   8 pools, 201 pgs
    objects: 68 objects, 87 MiB
    usage:   6.6 GiB used, 593 GiB / 600 GiB avail
    pgs:     201 active+clean
[root@ceph-stroage01 ~]#
[root@ceph-stroage01 ~]# ceph health detail
HEALTH_ERR 2 filesystems are offline; 2 filesystems are online with fewer MDS than max_mds
[ERR] MDS_ALL_DOWN: 2 filesystems are offline
    fs cephfs is offline because no MDS is active for it.
    fs cephfs_k8s is offline because no MDS is active for it.
[WRN] MDS_UP_LESS_THAN_MAX: 2 filesystems are online with fewer MDS than max_mds
    fs cephfs has 0 MDS online, but wants 1
    fs cephfs_k8s has 0 MDS online, but wants 1
[root@ceph-stroage01 ~]#
所有的mds都没有active的状态,造成元数据无法恢复,所以此时需要放弃原来的cephfs,重建构建基于原来data池生成新的cephfs.

2.元数据故障恢复
step 1.设置允许多文件系统

ceph fs flag set enable_multiple true --yes-i-really-mean-it
step 2.创建一个新的元数据池,以免损坏原来的元数据

ceph osd pool create recovery 8
step 3.将存储池fs_data和新的元数据池recovery关联创建一个新的recovery-fs

ceph fs new recovery-fs recovery fs_data --allow-dangerous-metadata-overlay
step 4.文件系统的初始化相关工作

cephfs-data-scan init --force-init --filesystem recovery-fs --alternate-pool recovery
step 5.reset fs

ceph fs reset recovery-fs --yes-i-really-mean-it
若失败,要把所有mds fail掉或者stop掉,再快速执行上面命令。

cephfs-table-tool recovery-fs:all reset session
cephfs-table-tool recovery-fs:all reset snap
cephfs-table-tool recovery-fs:all reset inode
出现Address family not supported by protocol的错误忽略掉

step 6.恢复相关
确保新建的recovery-fs没有active的mds,有则stop掉,不然该mds容易crashed。

cephfs-data-scan scan_extents --force-pool --alternate-pool recovery --filesystem cephfs fs_data
cephfs-data-scan scan_inodes --alternate-pool recovery --filesystem cephfs --force-corrupt --force-init fs_data
cephfs-data-scan scan_links --filesystem recovery-fs
出现Address family not supported by protocol的错误忽略掉

systemctl start ceph-mds@node82
等待 mds active 以后再继续下面操作

ceph daemon mds.node82 scrub_path / recursive repair
设置成默认的fs

ceph fs set-default recovery-fs
可以看到在lost+found里面就有数据了这个生成的文件名称就是实际文件存储的数据的prifix,也就是通过原始inode进行的运算得到的。

备份原始的元数据信息

ceph daemon mds.node82 dump cache > /tmp/mdscache
元数据恢复原理
一般文件系统采用的fsck命令来维护文件系统一致性,但是fsck对cephfs的难度是非常大的,主要原因在于其机制存在根本的区别:

cephfs修复的是一个rados集群数据而非一块磁盘设备;
需要精确的识别数据的所有数据片,及这些数据片所属的inode
大量的元数据不可能全部保存到内存中
数据丢失原因可能在于
(1)系统bug导致;
(2)由于RADOS同步的灾难性故障——可能到时大量数据丢失;
(3)bit位翻转(bitrot)
cephfs-data-scan
cephfs-data-scan根据数据存储池中的内容重新生成丢失文件和目录的元数据对象。
step 1.初始化

cephfs-data-scan init
step 2.扫描所有对象以计算索引节点的尺寸和 mtime 元数据;

cephfs-data-scan scan_extents <datapool>
```bash
step 3.从每个文件的第一个对象扫描出元数据并注入元数据存储池。
```bash
cephfs-data-scan scan_inodes <datapool>
如果数据存储池内的文件很多、或者有很大的文件,这个命令就要花费很长时间。要加快处理,可以让这个工具多跑几个例程。先确定例程数量、再传递给每个例程一个数字 N ,此数字应大于 0 且小于 (N - 1) ,像这样

# Worker 0
cephfs-data-scan scan_extents <data pool> 0 1
# Worker 1
cephfs-data-scan scan_extents <data pool> 1 1
# Worker 0
cephfs-data-scan scan_inodes <data pool> 0 1
# Worker 1
cephfs-data-scan scan_inodes <data pool> 1 1
3.mds rank 0 is damaged
启动ceph后,出现如下错误:

[root@k8s-node2 ~]# ceph health detail
HEALTH_ERR mds rank 0 is damaged; mds cluster is degraded
mds.0 is damaged
mds cluster is degraded
提示 mds.0 不可用。
在部署ceph时,安装了3个mds, 分别运行在3台服务器上。并且参考官方的配置,使其中一个作为 master 对外提供服务,另外两个作为 standby。来预防单点故障。(max_mds 设置为 2)
http://docs.ceph.com/docs/jewel/cephfs/standby/#examples

该错误发生后,3 个mds,均为 standby 状态。按照官网的描述来看,当所有 mds 均处于 standby 状态时,其中一个 mds 会选举称为 master.
于是挨个停止mds, 随后又逐个启动并查看/var/log/ceph/ceph-mds.xx.log。发现 mds 在称为 master 时,出现了如下错误:

...
2017-09-26 11:30:48.976326 7f9ee3941700  0 mds.0.journaler(ro) _finish_read got less than expected (4194304)
2017-09-26 11:30:48.976354 7f9ee1734700  0 mds.0.log _replay journaler got error -22, aborting
2017-09-26 11:30:49.071230 7f9ee3941700  0 mds.0.journaler(ro) _finish_read got less than expected (4194304)
2017-09-26 11:30:49.071472 7f9ee1734700 -1 log_channel(cluster) log [ERR] : Error loading MDS rank 0: (22) Invalid argument
2017-09-26 11:30:49.076129 7f9ee1734700  1 mds.k8s-node1 respawn
...
2017-09-26 11:30:49.099291 7f99e58be180  0 pidfile_write: ignore empty --pid-file
2017-09-26 11:30:49.571643 7f99dfacf700  1 mds.k8s-node1 handle_mds_map standby
提示在读取 journal 的时候出现差错,因此可以确定问题出在 mds journal 上。

$ ceph mds repaire 0
admin 2022-8-3 11:51
step 1.导出 journal
在执行有风险的操作之前,先将 journal 备份:

ceph-journal-tool journal export backup.bin
Note : 这个文件大小和ceph集群存储数据量成正比,会很大

step 2.从 journal 恢复
当 mds 因为 journal 损坏或者其他原因无法读取,为了恢复文件元数据可以执行:

ceph-journal-tool event recover_dentries summary
默认会从 mds.0 中恢复, 使用 –rank=<n> 来指定其他mds

这条指令会将 journal 中所有可回收的 inodes/dentries 写到后端存储,前提是要写入的 innodes/dentries 版本比后端存储里面的要高。 如果某个区块的 journal 丢失或者损坏,那么就不会被写到后端存储中。

Note: 除了将inodes/dentries 写入之外, 这条命令还会更新每个 MDS 的 InoTables, 标明被写入的 inodes 号在使用中,在一般情况下,会让后端存储恢复正常状态。

此操作不能保证后端存储的状态达到前后一致,而且在此之后有必要执行 MDS 在线 scrub。此命令不会更改日志内容,恢复了能恢复的部分后应该把journal截短。

Note : 然而在执行这个命令之后,集群并未恢复正常,因此还需要往下看

step 3. 截断日志

cephfs-journal-tool journal reset
重置journal 将会造成metadata丢失,除非你提前通过诸如 recover_dentries 的方式将metadata保存。该操作可能会在数据池里面产生一些孤儿对象。这会造成已写入的inodes再次被重新分配, 权限规则可能因此被违反。

Note : 有个类似的 issue中也提到了上述步骤(link),但执行到这一步后,就没往下走了。
And mds rejoined again and everything seems to work fine now. 这点我没验证过,担心会有隐患。于是按照文档步骤继续执行了。

step 4. 清除 MDS 表
在 journal 被 reset 之后, 它可能不再符合MDS表的内容(包括:InoTable, SessionMap, SnapServer). 重置 SessionMap (即擦除所有Session) , 通过命令:

cephfs-table-tool all reset session
这条命令会在所有mds中生效, 可以将 all 替换为指定 mds 标号。
step 5.MDS MAP RESET
一旦文件系统底层的 RADOS 状态(即元数据存储池的内容)有所恢复,有必要更新 MDS 图以反映元数据存储池的内容。可以用下面的命令把 MDS MAP 重置到单个 MDS :

ceph fs reset <fs name> --yes-i-really-mean-it
运行此命令之后, MDS rank 保存在 RADOS 上的任何不为 0 的状态都会被忽略:因此这有可能导致数据丢失。

Note : fs name 可通过 ceph fs ls 查看

step 6. RECOVERY FROM MISSONG METADATA OBJECT

# Session table
cephfs-table-tool 0 reset session
# SnapServer
cephfs-table-tool 0 reset snap
# InoTable
cephfs-table-tool 0 reset inode
# Journal
cephfs-journal-tool --rank=0 journal reset
# Root inodes ("/" and MDS directory)
cephfs-data-scan init
最后,会基于数据池中丢失的文件和目录来重新创建 METADATA OBJECT。分为2步。

扫描所有对象并计算出inodes 的大小和 mtime metadata

cephfs-data-scan scan_extents <data pool>
扫描每个文件中的第一个对象来收集METADATA,随后注入到 metadata pool

cephfs-data-scan scan_inodes <data pool>
Note : 这两个步骤太耗时了,应该考虑起多个workers来同步处理。data pool 可通过 ceph fs ls 查看

# Worker 0
cephfs-data-scan scan_extents <data pool> 0 1
# Worker 1
cephfs-data-scan scan_extents <data pool> 1 1
# Worker 0
cephfs-data-scan scan_inodes <data pool> 0 1
# Worker 1
cephfs-data-scan scan_inodes <data pool> 1 1
Note : 一定要确保在所有 workers 完成 scan_extents 之后再进行scan_inodes操作。==

待所有操作执行完毕之后:

[root@k8s-master ~]# ceph -s
    cluster e61d687d-f43d-4f50-af66-da96e5856be9
     health HEALTH_OK
     monmap e5: 3 mons at {k8s-master=172.16.18.30:6789/0,k8s-node1=172.16.18.6:6789/0,k8s-node2=172.16.18.7:6789/0}
            election epoch 676, quorum 0,1,2 k8s-node1,k8s-node2,k8s-master
      fsmap e4970: 1/1/1 up {0=k8s-node1=up:active}, 2 up:standby
     osdmap e2914: 6 osds: 6 up, 6 in
            flags sortbitwise,require_jewel_osds
      pgmap v10110565: 256 pgs, 4 pools, 25575 MB data, 372 kobjects
            58730 MB used, 11115 GB / 11172 GB avail
                 256 active+clean
admin 2022-8-3 13:37
[root@ceph-1 ~]# ceph fs dump
dumped fsmap epoch 135
e135
enable_multiple, ever_enabled_multiple: 1,0
compat: compat={},rocompat={},incompat={1=base v0.20,2=client writeable ranges,3=default file layouts on dirs,4=dir inode in separate object,5=mds uses versioned encoding,6=dirfrag is stored in omap,8=no anchor table,9=file layout v2}
legacy client fscid: 3

Filesystem 'filefs' (3)
fs_name        filefs
epoch        135
flags        c
created        2022-07-27 11:43:28.889488
modified        2022-08-03 10:23:42.173444
tableserver        0
root        0
session_timeout        60
session_autoclose        300
max_file_size        1099511627776
last_failure        0
last_failure_osd_epoch        386
compat        compat={},rocompat={},incompat={1=base v0.20,2=client writeable ranges,3=default file layouts on dirs,4=dir inode in separate object,5=mds uses versioned encoding,6=dirfrag is stored in omap,8=no anchor table,9=file layout v2}
max_mds        1
in        0
up        {0=84099}
failed       
damaged       
stopped       
data_pools        [7]
metadata_pool        6
inline_data        disabled
balancer       
standby_count_wanted        1
84099:        192.168.120.31:6800/2291079756 'ceph-3' mds.0.117 up:active seq 40


Standby daemons:

84111:        192.168.120.16:6800/463439748 'ceph-2' mds.-1.0 up:standby seq 2
84120:        192.168.120.23:6800/4181220800 'ceph-1' mds.-1.0 up:standby seq 1
admin 2022-8-4 13:39
1、前言
这里实验在使用cephfs时,如果cephfs的元数据损坏或丢失了,那该如何恢复出用户数据。下面就为大家演示下如何恢复。

2、准备测试环境
2.1、准备测试集群
我是基于L版本做的实验,J版也是可以的。测试环境如下:

[root@ceph05 ~]# ceph -v
ceph version 12.2.11 (26dc3775efc7bb286a1d6d66faee0ba30ea23eee) luminous (stable)

[root@ceph05 ~]# ceph osd tree
ID CLASS WEIGHT  TYPE NAME       STATUS REWEIGHT PRI-AFF
-1       0.07397 root default                           
-3       0.03699     host ceph05                        
0   hdd 0.01799         osd.0       up  1.00000 1.00000
1   hdd 0.01900         osd.1       up  1.00000 1.00000
-5       0.03699     host ceph06                        
2   hdd 0.01900         osd.2       up  1.00000 1.00000
3   hdd 0.01900         osd.3       up  1.00000 1.00000

[root@ceph05 deployceph]# ceph -s
  cluster:
    id:     176feab8-ca22-47bf-b809-202deac53c6f
    health: HEALTH_WARN
            crush map has straw_calc_version=0

  services:
    mon: 1 daemons, quorum ceph05
    mgr: ceph05(active)
    mds: cephfs-1/1/1 up  {0=ceph05=up:active}
    osd: 4 osds: 4 up, 4 in

  data:
    pools:   10 pools, 304 pgs
    objects: 918 objects, 2.60GiB
    usage:   6.70GiB used, 71.3GiB / 78.0GiB avail
    pgs:     304 active+clean
2.2、准备测试数据
挂载kc

[root@ceph05 deployceph]# mount -t ceph 192.168.10.30:/ /cephfs
[root@ceph05 deployceph]# df -h|grep ceph
···
192.168.10.30:/           78G  6.8G   72G   9% /cephfs
···
写入数据(这里我写入了几个比较有代表性的文件类型:txt、jpg、png、pdf、word、excel)

[root@ceph05 deployceph]# ll /cephfs/
total 5912
-rw-r--r-- 1 root root   31232 Mar 15 12:18 111.doc
-rw-r--r-- 1 root root   20593 Mar 15 12:18 22.xlsx
-rw-r--r-- 1 root root   12494 Mar 15 12:17 5be23a3eec2c0.png
-rw-r--r-- 1 root root    3189 Mar 15 12:17 cmap.txt
-rw-r--r-- 1 root root 5985243 Mar 15 12:17 hello0.pdf
3、模拟故障
这里直接模拟元数据丢失的情况,删除metadata池里面所有的元数据对象:

[root@ceph05 deployceph]# rados -p metadata ls|xargs -i rados -p metadata rm {}
[root@ceph05 deployceph]# ceph df
GLOBAL:
    SIZE        AVAIL       RAW USED     %RAW USED
    78.0GiB     71.3GiB      6.71GiB          8.60
POOLS:
    NAME                          ID     USED        %USED     MAX AVAIL     OBJECTS
    .rgw.root                     1      1.09KiB         0       63.3GiB           4
    default.rgw.control           2           0B         0       63.3GiB           8
    default.rgw.meta              3         720B         0       63.3GiB           5
    default.rgw.log               4           0B         0       63.3GiB         207
    default.rgw.buckets.index     5           0B         0       63.3GiB           1
    default.rgw.buckets.data      6      1.02KiB         0       63.3GiB           2
    pool01                        7      2.60GiB      3.94       63.3GiB         666
    rbd                           8          36B         0       63.3GiB           4
    metadata                      17          0B         0       63.3GiB           0
    data                          18     5.77MiB         0       63.3GiB           6
看到metadata池里面没有对象了已经,重启下mds看效果,因为mds里面会缓存元数据信息,所以要重启下mds:

[root@ceph05 deployceph]# systemctl restart ceph-mds@ceph05
[root@ceph05 deployceph]#
[root@ceph05 deployceph]#
[root@ceph05 deployceph]# ceph -s
  cluster:
    id:     176feab8-ca22-47bf-b809-202deac53c6f
    health: HEALTH_WARN
            1 filesystem is degraded
            1 filesystem has a failed mds daemon
            crush map has straw_calc_version=0

  services:
    mon: 1 daemons, quorum ceph05
    mgr: ceph05(active)
    mds: cephfs-0/1/1 up , 1 failed
    osd: 4 osds: 4 up, 4 in

  data:
    pools:   10 pools, 304 pgs
    objects: 905 objects, 2.60GiB
    usage:   6.71GiB used, 71.3GiB / 78.0GiB avail
    pgs:     304 active+clean
看到集群现在不正常了,访问kc里面的数据卡住,说明数据已经无法正常读取了。

4、开始恢复
使用我编写的py脚本(文末给出了源码)恢复,把脚本放到集群任意一台节点上执行:

[root@ceph05 rcy]# python recovery_cephfs.py -p data
-p指定cephfs的数据池,运行完之后会在当前目录下产生两个文件夹和一个运行脚本的日志文件。

[root@ceph05 rcy]# ll
total 16
-rw-r--r-- 1 root root 3826 Mar 15 13:57 recovery_cephfs.py
drwxr-xr-x 2 root root  120 Mar 15 13:57 recoveryfiles
-rw-r--r-- 1 root root 4804 Mar 15 13:57 recovery.log
drwxr-xr-x 2 root root 4096 Mar 15 13:57 recoveryobjs
查看恢复出来的文件在recoveryfiles文件夹下:

[root@ceph05 rcy]# ll recoveryfiles/
total 12364
-rw-r--r-- 1 root root 5985243 Mar 15 13:57 10000000000-pdf
-rw-r--r-- 1 root root    3189 Mar 15 13:57 10000000001-text
-rw-r--r-- 1 root root   12494 Mar 15 13:57 10000000002-png
-rw-r--r-- 1 root root   31232 Mar 15 13:57 10000000003-text
-rw-r--r-- 1 root root   20593 Mar 15 13:57 10000000004-excel
文件名格式为”文件在cephfs里面的inode-该文件可能的类型“。恢复出来的文件名后面会给出该文件的类型。这样就可以使用合适的软件打开该文件来验证文件是否完整。

5、总结
在cephfs文件系统的元数据完全损坏的情况下,只要数据池对象不丢失,就可以恢复出完整的数据。恢复的思路如下:

获取数据池对象
根据inode找到该文件的所有对象
拼接对象
使用脚本注意事项:

现在的脚本在只加入了txt、jpg、png、pdf、word、excel这些文件类型的识别,需要其他的就需要自己加入到脚本里面了
只适合副本池
如果数据量特别大,不适合使用脚本,不过可以参考脚本的思路去一个一个文件恢复
6、脚本
# coding: utf-8
import os
import shutil
import json
import sys
import subprocess
import copy
import logging
import argparse

__auth__ = 'ypdai'

SLEEP_INTERVAL = 1
logging.basicConfig(filename='./recovery.log', format='%(asctime)s : %(levelname)s  %(message)s',
                    level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S')

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
RECOVERY_OBJ_DIR = os.path.join(BASE_DIR, 'recoveryobjs')
RECOVERY_FILE_DIR = os.path.join(BASE_DIR, 'recoveryfiles')


def exec_cmd(cmd):
    """
    执行shell命令,并返回标准输出和执行状态码
    :param cmd:
    :return:
    """
    logging.info('exec_cmd():: cmd: {}'.format(cmd))
    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    p.wait()
    out = p.stdout.read()
    code = p.returncode
    logging.info('exec_cmd():: cmd exec out: {}, code: {}'.format(out, code))
    return out, code


def prepare(pool_name):
    """
    准备恢复条件
    1、检查所给pool name是不是cephfs的数据池
    2、创建recoveryobjs和recoveryfiles文件加
    :param pool_name:
    :return:
    """
    cmd = 'ceph fs ls -f json-pretty'
    out, code = exec_cmd(cmd)
    out = json.loads(out)
    for cnt in out:
        if pool_name not in cnt.get('data_pools'):
            return False

    if os.path.isdir(RECOVERY_OBJ_DIR):
        shutil.rmtree(RECOVERY_OBJ_DIR)
    os.mkdir(RECOVERY_OBJ_DIR)

    if os.path.isdir(RECOVERY_FILE_DIR):
        shutil.rmtree(RECOVERY_FILE_DIR)
    os.mkdir(RECOVERY_FILE_DIR)

    return True


def get_file_type(file_path):
    cmd = 'file %s' % file_path
    out, code = exec_cmd(cmd)
    out = out.split(':')[-1].lower()

    file_type = 'text'
    if 'word' in out:
        file_type = 'word'
    elif 'excel' in out:
        file_type = 'excel'
    elif 'pdf' in out:
        file_type = 'pdf'
    elif 'text' in out:
        file_type = 'text'
    elif 'jpeg' in out:
        file_type = 'jpg'
    elif 'png' in out:
        file_type = 'png'

    return file_type


def do_recovery(pool_name):
    """
    具体执行恢复,大概恢复逻辑如下:
    1、从数据池里面获取所有的数据对象
    2、找到每个文件的head对象,然后把数据这个文件的其他对象内容写入head对象里面
    3、根据head对象的文件类型,推测该文件的实际类型
    :param pool_name:
    :return:
    """
    cmd = 'for obj in $(rados -p %s ls);do rados -p %s get ${obj} %s/${obj};done' % (
        pool_name, pool_name, RECOVERY_OBJ_DIR)
    out, code = exec_cmd(cmd)

    if code != 0:
        logging.error('do_recovery():: get obj from rados failed.')
        return

    cmd = 'ls %s' % RECOVERY_OBJ_DIR
    out, code = exec_cmd(cmd)
    if code != 0:
        logging.error('do_recovery():: list obj failed.')
        return

    done_lst = []
    objects = out.split()
    for obj in objects:
        inode, number = obj.split('.')
        if inode in done_lst:
            continue

        cmd = '''ls -l %s | awk '{print $NF}' | grep ^%s |sort''' % (RECOVERY_OBJ_DIR, inode)
        out, code = exec_cmd(cmd)
        files = out.split('\n')

        head_file = files[0]
        file_type = get_file_type('%s/%s' % (RECOVERY_OBJ_DIR, head_file))
        cmd = 'cp %s/%s %s/%s-%s' % (RECOVERY_OBJ_DIR, head_file, RECOVERY_FILE_DIR, inode, file_type)
        out, code = exec_cmd(cmd)
        for f in files[1:]:
            if not f:
                continue
            cmd = 'cat %s/%s >> %s/%s-%s' % (RECOVERY_OBJ_DIR, f, RECOVERY_FILE_DIR, inode, file_type)
            out, code = exec_cmd(cmd)

        done_lst.append(inode)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-p', '--pool', required=True, type=str, dest='pool',
                        help='select given cephfs data pool by name')
    args = parser.parse_args()

    if not prepare(args.pool):
        logging.error('main():: invalid pool name.')
        sys.exit(1)

    logging.info('=== main():: recovery start')
    do_recovery(args.pool)
    logging.info('=== main():: recovery done')

查看全部评论(7)

关闭

站长推荐上一条 /3 下一条

QQ|返回首页|Archiver|手机版|小黑屋|易陆发现 点击这里给我发消息

GMT+8, 2022-8-4 13:42 , Processed in 0.044001 second(s), 27 queries .

Powered by LR.LINUX.cloud bbs168x X3.2 Licensed

© 2012-2025 Comsenz Inc.

返回顶部