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
生成测试文件
使用脚本创建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
导出池
导出池后,池就不存在了。
df -hT
# 重启系统,不然导出池报错“pool is busy”
sudo init 6
sudo umount -l /iotdata
sudo zpool export iotdata
sudo zpool status -v
模拟文件损坏
使用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是要损坏的硬盘,根据自己的设备而定。
导入池
sudo zpool import
sudo zpool import iotdata
注意此时还没有检测到损坏的文件,因为报错是0。
使用md5校验文件的完整性全部OK,
说明zfs从冗余盘拿到了完整的数据。
因为触发了文件访问,现在再次查看池的状态就会显示错误的文件校验,如下图,
将数据同步到损坏的存储设备,
sudo zpool scrub iotdata
修复完成后我们可以消除错误警告,
sudo zpool clear iotdata
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 后面的部分也就是目标文件跳过多少块再开始写。