ZFS:有人知道如何让 zvol 回滚吗?

ZFS:有人知道如何让 zvol 回滚吗?

我有一个基于 ZFS 的 iSCSI SAN,它为许多 VM 服务器提供 ZVOL。今天,网络故障导致客户端上安装的所有 iSCSI 卷都进入 RO 状态。唯一的解决方法是关闭所有 iSCSI 卷并重新启动,通常需要运行 fsck 才能使 iSCSI 卷重新上线。好吧,fsck 决定彻底销毁其中一个卷。所以,看起来我无法修复 fsck 造成的混乱。

我读过很多关于在 ZFS 上恢复文件的文章,但在这种情况下,我处理的是 ZVOL,从某种意义上说,这要简​​单得多,但我还没有看到任何关于尝试回滚块设备内容的文章。有什么建议吗?

—短暂性脑缺血发作—

一些数据集详细信息:

Dataset zpool1/vm3 [ZVOL], ID 59, cr_txg 12078, 44.6G, 2 objects, rootbp DVA[0]=<6:6c2c4b1e00:200> DVA[1]=<7:487aa4b200:200> [L0 DMU objset] fletcher4 lz4 LE contiguous unique double size=800L/200P birth=7736596L/7736596P fill=2 cksum=4c78779ec:2049fb2de6c:6f2f6c4a44e9:1042484aee3ded

    Deadlist: 1K (512/512 comp)

mintxg 0 -> obj 48
mintxg 1 -> obj 4157

    Object  lvl   iblk   dblk  dsize  lsize   %full  type
         0    7    16K    16K  7.00K    16K    6.25  DMU dnode
        dnode flags: USED_BYTES
        dnode maxblkid: 0

    Object  lvl   iblk   dblk  dsize  lsize   %full  type
         1    5    16K     8K  44.6G   200G   36.45  zvol object
        dnode flags: USED_BYTES
        dnode maxblkid: 26214399

    Object  lvl   iblk   dblk  dsize  lsize   %full  type
         2    1    16K    512      0    512  100.00  zvol prop
        dnode flags: USED_BYTES
        dnode maxblkid: 0
        microzap: 512 bytes, 1 entries

                size = 214748364800

系统是CentOS 7.1

Linux san1srvp01 3.10.0-514.6.1.el7.x86_64 #1 SMP Wed Jan 18 13:06:36 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

没有相关的快照;我想这不言而喻。

我提出这个问题的原因和我所追求的与这篇文章有关,最大燃烧它深入研究了通过取证技术进行对象恢复。这当然依赖于对 ZFS 内部知识的了解。不过,我所见过的大部分内容都是关于回顾文件对象,这与实现原始块存储的对象非常不同,而且我几乎没有看到与 ZVOL 相关的内部内容。

即使我无法从技术上“回滚” fsck 所做的更改,但至少可以回滚并找到一些关键的原始块。考虑到 ZFS 的 COW 行为,这应该是可能的……而且我缺乏足够的知识,但我通常不会让这阻止我。

答案1

是的,可以做到。不过会比较乱。

https://gist.github.com/jshoward/5685757

由于链接已经失效,因此包括原始文件。

zfs_revert-0.1.py


#!/usr/bin/python
# -*- coding: utf-8 -*-

#Script for reverting ZFS changes by destroying uberblocks
#Author: Martin Vool
#E-mail: [email protected]
#Version: 0.1
#Date: 16 November 2009


import time
import subprocess
import sys
import os
#Default blocksize
bs=512
#default total blocks (sorry programming in estonian :-/)
suurus=None

if len(sys.argv) > 2:
    for arg in sys.argv:
        arg=arg.split('=')
        if len(arg) == 1:
            file=arg[0]
        elif arg[0] == '-bs':
            bs=int(arg[1])
        elif arg[0] == '-tb':
            suurus=int(arg[1])
else:
    print 'Usage: zfs_revert.py [-bs=n default:n=512 blocksize] \\n [-tb=n total block size in blocks] [file/device] You have to set -tb'
    exit(1)
print int(bs)
if suurus == None:
    print 'Total block size in blocks is undefined'
    exit(1)
#make solaris use gnu grep.
if os.uname()[0] == 'SunOS':
    grep_cmd='ggrep'
else:
    grep_cmd='grep'


#to format program output
def formatstd(inp):
    inp=inp.split('\n')
    ret=[]
    for line in inp:
        columns=line.split(' ')
        nc=[]
        for c in columns:
            if c != '':
                nc.append(c)
        ret.append(nc)
    return ret


#read blocks from beginning(64mb)
a_count=(256*bs)
#read blocks from end (64mb)
l_skip=suurus-(256*bs)


print 'Total of %s blocks'%suurus
print 'Reading from the beginning to %s blocks'%a_count
print 'Reading from %s blocks to the end'%l_skip

#get the uberblocks from the beginning and end
yberblocks_a=formatstd(subprocess.Popen('sync && dd bs=%s if=%s count=%s | od -A x -x | %s -A 2 "b10c 00ba" | %s -v "\-\-"'%(bs,file, a_count,grep_cmd,grep_cmd), shell=True, stdout=subprocess.PIPE).communicate()[0])
yberblocks_l=formatstd(subprocess.Popen('sync && dd bs=%s if=%s skip=%s | od -A x -x | %s -A 2 "b10c 00ba" | %s -v "\-\-"'%(bs,file, l_skip,grep_cmd,grep_cmd), shell=True, stdout=subprocess.PIPE).communicate()[0])


yberblocks=[]

for p in yberblocks_a:
    if len(p) > 0:
        #format the hex address to decmal so dd would eat it.
        p[0]=(int(p[0], 16)/bs)
        yberblocks.append(p)

for p in yberblocks_l:
    if len(p) > 0:
        #format the hex address to decmal so dd would eat it and add the skipped part.
        p[0]=((int(p[0], 16)/bs)+int(l_skip)) #we have to add until the place we skipped so the adresses would mach.
        yberblocks.append(p)
print '----'
#here will be kept the output that you will see later(TXG, timestamp and the adresses, should be 4, might be less)
koik={}
i=0
for p in yberblocks:
    if len(p) > 0:
        if i == 0:#the first output line
            address=p[0]
        elif i == 1:#second output line
            #this is the output of od that is in hex and needs to be reversed
            txg=int(p[4]+p[3]+p[2]+p[1], 16)
        elif i == 2:#third output line
            timestamp=int(p[4]+p[3]+p[2]+p[1], 16)
            try:
                aeg=time.strftime("%d %b %Y %H:%M:%S", time.localtime(timestamp))
            except:
                aeg='none'
            if koik.has_key(txg):
                koik[txg]['addresses'].append(address)
            else:
                koik[txg]={
                    'txg':txg,
                    'timestamp':timestamp,
                    'htime': aeg,
                    'addresses':[address]
                }
        if i == 2:
            i=0
        else:
            i+=1
    keys = koik.keys()
    keys.sort()
    
while True:
    keys = koik.keys()
    keys.sort()
    print 'TXG\tTIME\tTIMESTAMP\tBLOCK ADDRESSES'
    for k in keys:
        print '%s\t%s\t%s\t%s'%(k, koik[k]['htime'],koik[k]['timestamp'],koik[k]['addresses'])
    try:
        save_txg=int(input('What is the last TXG you wish to keep?\n'))
        keys = koik.keys()
        keys.sort()
        for k in keys:
            if k > save_txg:
                for adress in koik[k]['addresses']:
                    #wrtie zeroes to the unwanted uberblocks
                    format=formatstd(subprocess.Popen('dd bs=%s if=/dev/zero of=%s seek=%s count=1 conv=notrunc'%(bs, file, adress), shell=True, stdout=subprocess.PIPE).communicate()[0])
                del(koik[k])
        #sync changes to disc!
        sync=formatstd(subprocess.Popen('sync', shell=True, stdout=subprocess.PIPE).communicate()[0])
    except:
        print ''
        break

答案2

没有相关的快照;我想这不言而喻。

因此,如果没有与 zpool 和数据健康的时间相关的快照,您就无法轻松地采取任何补救措施或进行回滚。

答案3

正如 Tero Kilkanen 在评论中正确指出的那样,您需要在 zvol 仍然有效时对其进行快照,否则您的数据就会丢失。


背景信息:

快照可以从任何数据集(文件系统或卷 (zvol))创建,并且本身也是一个(只读、依赖)数据集。它们始终是原子的,因此您可以获得整个数据集在某个时间点的状态(尽管对于您的应用程序来说,在最坏的情况下,它可能看起来像是磁盘或系统的硬重置),并且可以确保您的数据完整性得到保留(至少对于同步写入而言,异步写入当然可能会被部分丢弃)。

在这方面,zvols 和文件系统之间的唯一区别是 - 由于每个快照始终引用整个数据集 - 您可以从文件系统快照中挑选要恢复的文件(将其与当前或较旧的数据混合),但您只能选择使用整个 zvol,因为它就像一个非常大的文件(理论上,您可以复制字节范围并自行合并它们,但这会非常不方便)。除了对数据的这种“视图”(文件与块)之外,行为是相同的。

答案4

查看 zpool import -T 选项。注意:这是在池上,而不是 zvol 上。也许您可以将 zvol 发送到新的 zpool,然后使用 import -T 将其通过不同的 txg 导入。

相关内容