zfs文件损坏自愈测试

Table of Contents

背景

测试在硬盘冗余的zfs文件系统下,某个硬盘里的文件损坏后是否可以正常访问。

环境信息

系统:ubuntu22.04.4,使用VMware虚拟机创建,除了系统盘之外,添加另外两个相同大小的1G虚拟磁盘。

zfs版本:

(py38) pc@vm-ubuntu2204:~$ zfs -V
zfs-2.1.5-1ubuntu6~22.04.4
zfs-kmod-2.1.5-1ubuntu6~22.04.3

模拟过程

建立zfs池和数据集

建立两个盘的池,有硬盘冗余,raid-1模式。

sudo apt update -y
sudo apt install zfsutils-linux -y
sudo zpool create iotdata mirror /dev/sdh /dev/sdi
sudo zfs create iotdata/tf
sudo zpool status -v

image-20240531021044902

生成测试文件

使用脚本创建150个文件,大小1k~10MB随机,且生成每个文件的md5校验值到/opt目录下,脚本内容如下,

#!/bin/bash

# @Time         : 2024/5/29 23:20
# @Author       : Attaboy
# @Email        : [email protected]
# @File         : mk_dummy_files.sh
# @Software     : PyCharm

# 批量生成测试文件,大小1k~10MB随机,内容使用随机字符串填充。
# 用于zfs文件损坏自愈测试。

# 脚本执行方式:sudo bash mk_dummy_file.sh

DIRECTORY="/iotdata/tf"
FILE_COUNT=150
BLOCK_SIZE=1
MIN_BLOCK_COUNT=1
MAX_BLOCK_COUNT=10240

gen_rand_int() {

    # 生成指定范围的随机整数
    min=$1
    max=$(($2-$min+1))
    num=$(cat /dev/urandom | head -n 10 | cksum | awk -F ' ' '{print $1}')
    echo $(($num%$max+$min))

}

make_dummy_file() {

    # 批量生成测试文件,大小1k~10MB随机,内容使用随机字符串填充。
    for n in $(seq 1 $1)
    do
        echo "loop count = ${n}"
        dummy_file=$2/$n.bin
        sudo dd if=/dev/urandom of=${dummy_file} bs=${3}k \
        count=$(gen_rand_int $4 $5)
        # 写入制作的文件的md5值到本地文件,用于后续的文件完整性校验
        sudo md5sum ${dummy_file} | sudo tee -a /opt/md5sum_results.txt
    done

}

[ -d $DIRECTORY ] && sudo rm -rf $DIRECTORY/*.bin
[ ! -d $DIRECTORY ] && sudo mkdir -p $DIRECTORY
sudo rm -f /opt/md5sum_results.txt 2>/dev/null

make_dummy_file $FILE_COUNT $DIRECTORY $BLOCK_SIZE $MIN_BLOCK_COUNT \
$MAX_BLOCK_COUNT

exit 0

image-20240531021218448

导出池

导出池后,池就不存在了。

df -hT
# 重启系统,不然导出池报错“pool is busy”
sudo init 6
sudo umount -l /iotdata
sudo zpool export iotdata
sudo zpool status -v

image-20240531021538570

模拟文件损坏

使用dd将底层的数据块sector写入随机内容,模拟文件损坏,使用脚本实现,内容如下,

#!/bin/bash

# @Time         : 2024/5/30 20:04
# @Author       : Attaboy
# @Email        : [email protected]
# @File         : randomDD.sh
# @Software     : PyCharm

# 随机损坏文件,使用dd命令写空数据到zfs文件系统以下的底层sector

DISK_NAME="/dev/sdb"

gen_rand_int() {

    # 生成指定范围的随机整数
    min=$1
    max=$(($2-$min+1))
    num=$(cat /dev/urandom | head -n 10 | cksum | awk -F ' ' '{print $1}')
    echo $(($num%$max+$min))

}

for i in $(seq 1 100)
do
    random_seek_num=$(gen_rand_int 1 999999)
    random_count_num=$(gen_rand_int 5 9)
    sudo dd if=/dev/urandom of=${DISK_NAME} bs=512 seek=${random_seek_num} \
count=${random_count_num}
done

exit 0

注意sdb是要损坏的硬盘,根据自己的设备而定。

image-20240531021831085

导入池

sudo zpool import
sudo zpool import iotdata

注意此时还没有检测到损坏的文件,因为报错是0。

image-20240531022136396

使用md5校验文件的完整性全部OK,

image-20240531022304318

image-20240531022336288

说明zfs从冗余盘拿到了完整的数据。

因为触发了文件访问,现在再次查看池的状态就会显示错误的文件校验,如下图,

image-20240531022551724

将数据同步到损坏的存储设备,

sudo zpool scrub iotdata

image-20240531022833407

修复完成后我们可以消除错误警告,

sudo zpool clear iotdata

image-20240531022938644

FAQ

zfs的一些概念

详见官方文档

http://www.giis.co.in/Zfs_ondiskformat.pdf

ZFS Recordsize 和 TXG 探索

https://tim-tang.github.io/blog/2016/06/01/zfs-recordsize-txg

zfs的常见操作

详见官方文档

https://docs.oracle.com/cd/E26926_01/html/E25826/gbchy.html#gazqr

dd 命令中的 skip 和 seek 理解

skip=xxx 是在备份时对if 后面的部分也就是原文件跳过多少块再开始备份;

seek=xxx则是在备份时对of 后面的部分也就是目标文件跳过多少块再开始写。