ZFS 擦洗和 TRIM

来自 Alpine Linux

简介

在 Alpine Linux 上,没有像其他 Linux 发行版那样提供 cron/脚本来定期擦洗(以及最终 TRIM)您的存储池。
设置它很容易,只需几分钟即可完成。

擦洗

定义

擦洗检查指定存储池中的所有数据,以验证其校验和是否正确。 对于复制(镜像、raidz 或 draid)设备,ZFS 会自动修复在擦洗期间发现的任何损坏
当擦洗具有加密文件系统的存储池时,无需加载密钥。 但是,如果未加载密钥并且检测到无法修复的校验和错误,则文件名无法包含在 zpool status -v 详细错误报告中。
擦洗分为两个部分:元数据扫描和块擦洗。 元数据扫描将块排序为大的顺序范围,这样在发出擦洗 I/O 时,可以更有效地从磁盘读取。

另请参阅 Oracle - 磁盘擦洗 - 为什么以及何时?

创建脚本

此脚本用于列出存储池,确保它们在线,并且此时没有正在进行擦洗。
我们将把它写入 /usr/libexec/zfs/scrub
它取自 debian zfs 脚本

/usr/libexec/zfs/scrub 的内容/usr/libexec/zfs/scrub

#!/bin/sh -eu # directly exit successfully when zfs module is not loaded if ! [ -d /sys/module/zfs ]; then exit 0 fi # [auto] / enable / disable PROPERTY_NAME="org.alpine:periodic-scrub" get_property () { # Detect the ${PROPERTY_NAME} property on a given pool. # We are abusing user-defined properties on the root dataset, # since they're not available on pools https://github.com/openzfs/zfs/pull/11680 # TODO: use zpool user-defined property when such feature is available. pool="$1" zfs get -H -o value "${PROPERTY_NAME}" "${pool}" 2>/dev/null || return 1 } scrub_if_not_scrub_in_progress () { pool="$1" if ! zpool status "${pool}" | grep -q "scrub in progress"; then # Ignore errors and continue with scrubbing other pools. zpool scrub "${pool}" || true fi } # Scrub all healthy pools that are not already scrubbing as per their configs. zpool list -H -o health,name 2>&1 | \ awk -F'\t' '$1 == "ONLINE" {print $2}' | \ while read pool do # read user-defined config ret=$(get_property "${pool}") if [ $? -ne 0 ] || [ "disable" = "${ret}" ]; then  : elif [ "-" = "${ret}" ] || [ "auto" = "${ret}" ] || [ "enable" = "${ret}" ]; then scrub_if_not_scrub_in_progress "${pool}" else cat > /dev/stderr <<EOF $0: [WARNING] illegal value "${ret}" for property "${PROPERTY_NAME}" of ZFS dataset "${pool}". $0: Acceptable choices for this property are: auto, enable, disable. The default is auto. EOF fi done

然后使脚本可执行

# chmod +x /usr/libexec/zfs/scrub

使用 cron 启动擦洗脚本

建议定期启动擦洗,以确保您的存储池和数据状况良好。
在这里,擦洗将每月启动一次,在每月的第二个星期日。

在 root 用户下,编辑您的 crontabs

# crontab -e

并添加这两行

# zfs scrub the second sunday of every month
24      0       8-14    *       *       if [ $(date +\%w) -eq 0 ] && [ -x /usr/libexec/zfs/scrub ]; then /usr/libexec/zfs/scrub; fi

最后,确保 cron 已启动

# rc-update

应该有一行显示

crond |      default

如果不是,请将其添加到启动序列

# rc-update add crond

然后启动 crond 守护进程

# rc-service crond start

TRIM

定义

对存储池中所有可用空间启动立即按需 TRIM 操作。 此操作通知底层存储设备存储池中所有不再分配的块,并允许精简配置的设备回收空间。
手动按需 TRIM 操作的启动与 autotrim 存储池属性设置无关。 请参阅 autotrim 属性的文档,了解可以 TRIM 的 vdev 设备类型。

创建脚本

此脚本用于列出存储池,确保它们在线,仅使用 NVME ssd 驱动器构建,并且此时没有正在进行 TRIM。
我们将把它写入 /usr/libexec/zfs/trim
它取自 debian zfs 脚本

/usr/libexec/zfs/trim 的内容/usr/libexec/zfs/trim

#!/bin/sh -eu # directly exit successfully when zfs module is not loaded if ! [ -d /sys/module/zfs ]; then exit 0 fi # [auto] / enable / disable PROPERTY_NAME="org.alpine:periodic-trim" get_property () { # Detect the ${PROPERTY_NAME} property on a given pool. # We are abusing user-defined properties on the root dataset, # since they're not available on pools https://github.com/openzfs/zfs/pull/11680 # TODO: use zpool user-defined property when such feature is available. pool="$1" zfs get -H -o value "${PROPERTY_NAME}" "${pool}" 2>/dev/null || return 1 } trim_if_not_already_trimming () { pool="$1" if ! zpool status "${pool}" | grep -q "trimming"; then # Ignore errors (i.e. HDD pools), # and continue with trimming other pools. zpool trim "${pool}" || true fi } zpool_is_nvme_only () { zpool=$1 # get a list of devices attached to the specified zpool zpool list -vHPL "${zpool}" | awk -F'\t' '$2 ~ /^\/dev\// { if($2 !~ /^\/dev\/nvme/) exit 1 }' } # TRIM all healthy pools that are not already trimming as per their configs. zpool list -H -o health,name 2>&1 | \ awk -F'\t' '$1 == "ONLINE" {print $2}' | \ while read pool do # read user-defined config ret=$(get_property "${pool}") if [ $? -ne 0 ] || [ "disable" = "${ret}" ]; then  : elif [ "enable" = "${ret}" ]; then trim_if_not_already_trimming "${pool}" elif [ "-" = "${ret}" ] || [ "auto" = "${ret}" ]; then if zpool_is_nvme_only "${pool}"; then trim_if_not_already_trimming "${pool}" fi else cat > /dev/stderr <<EOF $0: [WARNING] illegal value "${ret}" for property "${PROPERTY_NAME}" of ZFS dataset "${pool}". $0: Acceptable choices for this property are: auto, enable, disable. The default is auto. EOF fi done

然后使脚本可执行

# chmod +x /usr/libexec/zfs/trim

使用 cron 启动 TRIM 脚本

在这里,TRIM 将每月启动一次,在每月的第一个星期日。

在 root 用户下,编辑您的 crontabs

# crontab -e

并添加这两行

# zfs trim the first sunday of every month
24      0       1-7    *       *       if [ $(date +\%w) -eq 0 ] && [ -x /usr/libexec/zfs/trim ]; then /usr/libexec/zfs/trim; fi

最后,确保 cron 已启动

# rc-update

应该有一行显示

crond |      default

如果不是,请将其添加到启动序列

# rc-update add crond

然后启动 crond 守护进程

# rc-service crond start