SSD Nodes Learn 🎉 VPS $5.50/월부터
가이드 Matt Connor작성자 Matt Connor

ZFS 스크럽 주기 설정 가이드: 단일 VPS 풀 환경

단일 디스크 VPS 환경에서 ZFS 스크럽은 데이터 손상을 복구할 수 없지만 잠재적 오류를 조기에 발견하는 유일한 방법입니다. 체크섬 불일치로 인한 파일 손상을 방지하기 위해 권장되는 스크럽 주기와 시스템 부하를 최소화하며 스케줄링하는 구체적인 방법을 정리했습니다.

ZFS 스크럽(scrub)의 실제 동작 방식

ZFS 스크럽은 풀(pool)에 할당된 모든 블록을 읽어 체크섬을 다시 계산하고, 그 결과를 상위 블록 포인터에 저장된 체크섬과 비교합니다. 두 값이 일치하지 않으면 ZFS는 풀이 보유한 중복성(redundancy)을 사용하여 해당 블록을 복구합니다. ZFS에서 이 작업을 수행하는 기능은 스크럽뿐입니다. 일반적인 읽기 작업은 사용자가 접근하는 블록만 검증하므로, 2년 동안 열어보지 않은 파일은 스크럽이 해당 파일을 읽기 전까지 검증되지 않은 상태로 남습니다.

스크럽은 다른 파일 시스템에서 필요한 오프라인 복구 과정인 fsck와는 다릅니다. ZFS는 디스크상의 데이터 형식을 손상된 상태로 두지 않기 때문에 구조적 복구 단계가 존재하지 않습니다. 모든 쓰기 작업은 새로운 위치로 수행되며, 풀의 루트 포인터인 uberblock은 마지막에 업데이트됩니다. 또한 스크럽은 전체 장치를 읽지 않습니다. 할당된 블록만 읽기 때문에 거의 비어 있는 풀은 몇 분 만에 스크럽이 끝나지만, 80%가 찬 동일한 풀은 훨씬 더 오랜 시간이 걸립니다.

스크럽은 ZFS가 가진 가장 낮은 I/O 우선순위로 실행됩니다. Linux에서 zfs_vdev_scrub_max_active은 기본값이 2이므로, vdev(ZFS가 하나의 단위로 취급하는 디스크 그룹)당 최대 2개의 스크럽 읽기 작업이 동시에 진행됩니다. zfs_scrub_min_time_ms은 기본값이 750이며, 이는 ZFS가 쓰기 작업을 일괄 처리하는 트랜잭션 그룹 플러시(transaction group flushes) 사이에서 동기화 스레드가 스크럽 작업에 할애하는 최소 시간(밀리초)을 의미합니다. 유휴 상태의 시스템에서는 스크럽이 디스크 자원을 모두 사용하지만, 부하가 걸리면 작업을 양보합니다. 장치가 1~2개뿐인 풀은 양보할 곳이 없으므로, 60개의 드라이브가 장착된 대형 섀시보다 스케줄링이 훨씬 더 중요합니다.

복구할 수 없는 풀을 왜 스크러빙(scrub)해야 합니까?

소규모 풀에서는 이 질문이 모든 것을 결정합니다. 중복성이 없으면 스크러빙은 데이터 손상을 감지할 뿐 수정할 수는 없습니다. VPS의 단일 가상 디스크는 미러링이나 패리티가 없는 풀입니다. ZFS는 불량 블록을 읽고 체크섬 검사에 실패하면 CKSUM 열에 이를 기록하고 해당 파일명을 알린 뒤 멈춥니다. 재구축할 두 번째 사본이 없기 때문입니다.

두 가지 부분적인 예외 사항을 알아둘 필요가 있습니다. ZFS는 기본적으로 메타데이터의 추가 사본(redundant_metadata=all)을 장치의 다른 영역에 저장하므로, 단일 장치 풀에서도 스크러빙을 통해 손상된 디렉터리 항목이나 블록 포인터를 복구할 수 있습니다. 또한 copies=2 속성이 설정된 데이터셋은 데이터 블록의 사본을 두 개 유지하지만, 저장 공간은 두 배로 소모됩니다. 두 경우 모두 장치 자체가 사라지는 상황에서는 데이터를 보호할 수 없습니다. copies 속성 문서에서는 바로 이 점을 경고합니다. 스트라이프 풀을 구성하고 copies=2를 설정한 뒤 이를 중복성이라고 믿어서는 안 됩니다.

따라서 단일 장치 풀에서 스크러빙은 조기에 정확한 알림을 제공한다는 한 가지 이점을 줍니다. 스크러빙은 조용히 진행되는 데이터 손상을 zpool status -v에 기록된 파일명으로 바꿔줍니다. 이때 백업본에는 해당 파일의 정상 버전이 남아 있을 것입니다. 이는 스크러빙을 하지 말아야 할 이유가 아니라, 백업을 해야 할 이유입니다. 특정 시점의 이미지와 실제 외부 복사본의 차이를 아직 이해하지 못했다면 VPS 스냅샷이 백업이 아닌 이유부터 확인하십시오. 스크러빙 결과는 다른 곳에 온전한 사본이 있을 때만 의미가 있기 때문입니다.

아무것도 발견되지 않은 스크러빙 결과 또한 의미가 있습니다. 이는 복원이나 마이그레이션을 수행하기 전에 신뢰하려는 데이터가 온전하다는 사실을 알려주기 때문입니다.

소규모 VPS 풀은 얼마나 자주 스크러빙해야 합니까?

매월 수행하는 것이 적절한 기본값이며, 패키지들도 이미 이를 가정하고 있습니다. Debian과 Ubuntu는 매월 두 번째 일요일에 정상 상태인 풀을 스크러빙하는 cron 작업을 기본으로 제공합니다. FreeBSD의 주기적 시스템은 일 단위 임계값을 기준으로 작동하며, daily_scrub_zfs_default_threshold의 기본값은 35일로, 매뉴얼에서는 이를 5주로 설명합니다.

사용량이 많은 소규모 풀에서 매주 스크러빙을 수행하는 것은 얻는 이득보다 비용이 더 큰 경우가 많습니다. 장치가 한두 개뿐인 경우 스크러빙 작업이 애플리케이션과 동일한 큐를 두고 경쟁하게 되며, 이를 흡수할 여유 장치가 없습니다. VPS에서는 I/O 허용량이 제한적이므로, 스크러빙에 사용되는 읽기 작업만큼 데이터베이스가 사용할 수 있는 읽기 작업이 줄어듭니다. 이러한 비용을 고려할 때, 매주 스크러빙을 수행해도 어차피 수리할 수 없는 결함에 대해 최대 3주 정도 일찍 경고를 받는 것뿐입니다. 이러한 교환은 스크러빙 비용이 저렴할 때만 의미가 있습니다.

시간을 측정해 본 뒤 결정하십시오. 수동으로 스크러빙을 한 번 실행하여 얼마나 걸리는지 확인하십시오.

  1. 한가한 저녁 시간에 sudo zpool scrub tank를 실행하고 zpool status에서 총 소요 시간을 기록하십시오.
  2. 1시간 이내에 완료되고 야간에 서버가 유휴 상태라면 매주 수행해도 무리가 없습니다.
  3. 풀이 트래픽을 처리하는 동안 수 시간 동안 실행되었다면, 매월 수행하는 방식을 유지하고 패키지화된 작업에 맡기십시오.
  4. 풀의 크기가 눈에 띄게 커질 때마다 시간을 다시 측정하십시오. 스크러빙 소요 시간은 디스크 용량이 아니라 할당된 데이터 양에 따라 달라지기 때문입니다.

어떤 주기를 선택하든 다른 정기 서버 작업과 함께 기록해 두십시오. 스크러빙은 패키지 업그레이드 및 로그 로테이션과 같은 목록에 포함되어야 합니다. 월간 Linux 서버 유지보수 체크리스트를 참조하십시오.

스크럽 시작, 일시 중지 및 중단

sudo zpool scrub tank
sudo zpool status tank

일시 중지와 중단은 서로 다른 작업이며, 잘못 선택하면 몇 시간 동안 반복 작업을 해야 할 수도 있습니다.

sudo zpool scrub -p tank
sudo zpool scrub tank
sudo zpool scrub -s tank

-p은 스크럽을 일시 중지합니다. 일시 중지 상태와 진행률은 주기적으로 디스크에 동기화되므로, 일시 중지된 스크럽은 풀을 export하거나 재부팅해도 유지됩니다. 풀이 다시 로드되면 스크럽은 일시 중지된 상태로 대기합니다. zpool scrub을 다시 실행하면 디스크에 기록된 마지막 체크포인트부터 재개됩니다. 반면 -s는 스크럽을 완전히 중단하며, 다음에 시작하는 스크럽은 처음부터 다시 진행됩니다. 한 시간 정도 디스크 자원이 필요하다면 -p을 사용하십시오. 스크럽을 완전히 종료하고 싶다면 -s을 사용하십시오.

알아두어야 할 플래그가 두 가지 더 있습니다. -w는 스크럽이 완료될 때까지 기다린 후 반환합니다. 스크립트 내부에서 다음 단계가 너무 일찍 시작되지 않도록 할 때 유용합니다. -ezpool status -v를 통해 보고된 데이터 오류가 있는 파일만 스크럽합니다. 백업에서 복원한 파일이 정상인지 빠르게 확인할 때 사용하는 방법입니다.

ZFS는 풀당 한 번에 하나의 스크럽 또는 리실버(장치 교체 후 재구축) 작업만 수행합니다. 두 작업 모두 I/O 집약적이기 때문입니다. 장치가 리실버 중이라면 스크럽은 순서를 기다립니다.

스크럽 실행 중 zpool status 확인 방법

sudo zpool status tank를 실행하여 타인의 수치와 비교하지 말고 본인의 수치를 직접 확인하십시오. 스크럽이 진행되는 동안 scan: 행에는 스캔된 수치, 발행된 수치, 전체 수치, 복구된 수치, 완료율, 남은 예상 시간이 표시됩니다.

Scanned는 메타데이터 단계입니다. ZFS가 블록 트리를 탐색하며 읽어야 할 주소를 수집하는 과정입니다. Issued는 데이터 단계입니다. 디스크 순서에 맞춰 정렬된 읽기 요청이 실제로 장치에 전달되는 과정입니다. 정렬된 스크럽 방식 때문에 두 개의 카운터가 존재하며, 실제 진행 상황은 issued 수치로 추적합니다. 초기에는 scanned가 issued보다 훨씬 앞서 나가므로 예상 시간은 큰 의미가 없습니다. 전체의 10퍼센트가 지난 시점부터 판단하십시오.

Repaired는 정상 복사본에서 다시 기록된 바이트 수를 나타냅니다. 중복성이 없는 풀에서는 스크럽이 무엇을 발견하든 이 수치는 0으로 유지됩니다. 이는 앞서 언급한 내용을 확인할 수 있는 수치로 표현한 것입니다.

그다음 장치별 열을 확인하십시오. READ와 WRITE는 장치 자체가 보고한 I/O 오류 횟수입니다. CKSUM은 체크섬 검증에 실패한 블록 수를 나타내며, 스크럽은 바로 이 CKSUM 열을 채우기 위해 존재합니다. 정상으로 보이는 장치에서 0이 아닌 CKSUM 값이 나타난다면 이는 실제 오류입니다. 데이터가 반환되었으나 잘못된 상태로 돌아온 것입니다.

마지막 행은 최종 결과입니다. errors: No known data errors은 통과를 의미합니다. 그 외의 결과가 나오면 sudo zpool status -v tank을 실행해야 합니다. 이 명령은 마지막 전체 스크럽 이후 발생한 모든 데이터 오류 목록을 파일명과 함께 출력합니다. 해당 파일들을 백업에서 복원하고, sudo zpool clear tank를 실행하여 카운터를 초기화한 뒤 다시 스크럽을 수행하십시오. 전체 스크럽이 완료될 때마다 목록이 새로 생성되므로, 완전히 깨끗한 스크럽 후 목록에서 사라진 파일명은 실제로 복구된 것입니다.

시스템에 설정된 주기적 스크럽 작업 확인하기

작업이 존재한다고 가정하지 마십시오. 또한 하나만 존재한다고 가정해서도 안 됩니다. 메커니즘은 플랫폼과 패키지에 따라 다릅니다. 풀 형식은 어디서나 동일하지만, 이를 둘러싼 도구는 그렇지 않다는 점을 간과하기 쉽습니다. FreeBSD와 Linux에서 ZFS가 제공되는 방식의 차이가 여기서 중요한 차이점입니다.

FreeBSD에서는 작업이 periodic 시스템 내에서 실행됩니다. /etc/periodic.conf에서 다음 설정을 지정하십시오.

daily_scrub_zfs_enable="YES"
daily_scrub_zfs_pools="tank"
daily_scrub_zfs_default_threshold="35"

daily_scrub_zfs_pools은 공백으로 구분된 풀 이름 목록이며, 비워두면 모든 풀을 스크럽합니다. daily_scrub_zfs_default_threshold는 풀별 임계값이 설정되지 않았을 때 스크럽을 수행하는 주기(일 단위)이며, 매뉴얼에서는 기본값으로 35를 제시합니다. 일일 작업은 매일 실행되지만, 임계값이 지난 경우에만 스크럽을 시작합니다.

Linux에서는 배포판의 ZFS 패키지에 따라 다르며, 일부 시스템은 두 메커니즘을 동시에 포함하기도 합니다. 풀별로 활성화하는 systemd 타이머인 zfs-scrub-monthly@tank.timerzfs-scrub-weekly@tank.timer가 있습니다. Debian과 Ubuntu는 매월 두 번째 일요일에 ONLINE 상태인 모든 풀을 스크럽하는 스크립트를 실행하는 /etc/cron.d/zfsutils-linux도 제공합니다. 무언가를 추가하기 전에 현재 상태를 확인하십시오.

systemctl list-timers 'zfs-*'
ls /etc/cron.d/ | grep -i zfs
sudo zpool history tank | grep scrub

zpool history이 정확한 확인 방법입니다. 풀이 실제로 시작한 스크럽 기록과 날짜를 보여주기 때문입니다. 한 달에 두 번 스크럽이 수행된다면 두 메커니즘이 모두 활성화된 상태이므로 하나를 제거해야 합니다. 타이머를 활성화하려면 다음을 실행하십시오.

sudo systemctl enable --now zfs-scrub-monthly@tank.timer

여유 공간은 어떤 튜닝 설정보다 중요하다

소규모 풀(pool)의 스크럽(scrub) 소요 시간은 할당된 데이터의 양과 데이터가 얼마나 분산되어 있는지에 따라 결정됩니다. 풀이 가득 찰수록 두 가지 요소 모두 악화됩니다.

OpenZFS 가이드는 풀의 여유 공간을 10% 이상으로 유지할 것을 권장합니다. 이 수치 아래로 떨어지면 할당자가 작업하는 단위인 메타슬랩(metaslab)의 여유 공간이 4% 임계값을 넘어서게 되며, 할당자는 first-fit 방식에서 best-fit 방식으로 전환됩니다. best-fit 방식은 CPU 자원을 훨씬 더 많이 소모합니다. 쓰기 지연 시간이 증가하고 단편화가 뒤따르며, 동일한 양의 데이터가 더 많고 작은 읽기 작업으로 처리되므로 다음 스크럽은 더욱 느려집니다.

따라서 첫 번째 해결책은 튜닝 설정이 아닙니다. 불필요한 데이터를 삭제하는 것입니다. ZFS 서버에서 가장 흔한 원인은 오래된 스냅샷이며, 그 뒤를 정리되지 않은 Docker 이미지 및 레이어업그레이드 후 남겨진 커널 패키지가 잇습니다. 다른 설정을 건드리기 전에 zfs list -o space을 실행하십시오. 스냅샷이 점유한 공간과 실제 데이터가 점유한 공간을 구분해 주기 때문입니다.

이제 튜닝 설정에 대해 간략히 설명하겠습니다. Linux에서는 다음 명령으로 현재 값을 확인할 수 있습니다.

cat /sys/module/zfs/parameters/zfs_scrub_min_time_ms
cat /sys/module/zfs/parameters/zfs_vdev_scrub_max_active

FreeBSD는 sysctl을 통해 동일한 매개변수를 노출하므로 sysctl -a | grep scrub을 사용하여 값을 찾을 수 있습니다. 이 값을 높이면 스크럽은 빨리 끝나지만 애플리케이션 속도는 느려집니다. 값을 낮추면 그 반대 현상이 발생합니다. 장치가 한두 개인 풀에서는 하나의 큐를 나누어 써야 하므로 어떤 설정을 하더라도 두 가지 이점을 모두 얻을 수는 없습니다. 튜닝 설정으로 설계상의 문제를 해결하는 경우는 드뭅니다. 매월 수행하는 스크럽이 부담스럽다면, 풀이 너무 가득 찼거나 장치 속도가 너무 느린 것이며 튜닝 설정은 단지 문제를 다른 곳으로 옮길 뿐이라는 사실을 인정해야 합니다.

스크럽 시간은 리실버(resilver)의 예고편입니다

리실버는 스크럽과 동일한 과정을 거칩니다. 할당된 블록을 읽고 검증한 뒤, 누락된 블록을 교체 장치에 기록합니다. 따라서 스크럽에 소요되는 시간은 재구축에 걸리는 시간과, 그 과정에서 풀(pool)의 중복성이 저하된 상태로 운영될 시간을 가장 정확하게 예측할 수 있는 지표입니다.

ZFS는 스크럽 작업보다 리실버 작업을 더 공격적으로 스케줄링하므로, 재구축은 일반적으로 동일한 풀의 스크럽보다 빨리 완료됩니다. 스크럽 시간을 보수적인 상한선으로 간주하십시오. 스크럽에 9시간이 걸린다면 그 정도의 재구축 시간을 계획해야 하며, 해당 시간 내에 두 번째 장치가 고장 나면 풀을 잃게 된다는 점을 이해해야 합니다. 이것이 단일 대규모 raidz 그룹 대신 미러링된 쌍을 사용하는 것에 대한 실질적인 근거입니다. RAID 5를 대체하는 ZFS의 패리티 레이아웃인 raidz는 모든 생존 장치를 읽어야만 재구축이 가능하기 때문입니다.

단일 장치 풀에는 리실버 과정이 없습니다. 장치가 고장 나면 풀도 함께 사라집니다. 이 경우 복구 시간은 곧 복원 시간이므로, 대신 복원 시간을 측정하십시오. 한 번도 실행해 본 적 없는 복원은 복구 계획이 아닙니다.

디스크를 임대할 때 변경되는 사항

VPS에서 블록 장치는 가상화되어 있습니다. 하이퍼바이저가 볼륨을 제공하며, 그 하부에는 로컬 NVMe가 있거나 자체 패리티를 갖춘 복제 네트워크 볼륨이 있을 수 있습니다. 이로 인해 스크럽(scrub) 작업에는 두 가지 결과가 따릅니다.

첫째, 플랫폼의 중복성은 ZFS 입장에서 보이지 않으며 ZFS는 이를 활용할 수 없습니다. 플랫폼이 하위 계층에서 미디어 오류를 복구하더라도 ZFS는 해당 문제를 전혀 감지하지 못합니다. 만약 플랫폼이 잘못된 블록을 전달하면 ZFS는 이를 포착하지만, 올바른 복사본이 해당 경계 너머에 존재하므로 스스로 수정할 수 없습니다.

둘째, 일반적으로 가상 디스크 하위 장치의 SMART(Self-Monitoring, Analysis and Reporting Technology) 데이터를 읽을 수 없습니다. 따라서 VPS의 디스크 상태 모니터링이 의존하는 조기 경고 기능을 전혀 사용하지 못할 수도 있습니다. 결과적으로 스크럽 작업에서 발생하는 CKSUM 카운터가 사용자가 확인할 수 있는 주요 신호가 됩니다.

ZFS가 단순히 보고하는 것을 넘어 직접 복구하기를 원한다면, 동일한 인스턴스 내에 하나 이상의 장치가 필요하며 이는 튜닝이 아닌 계획 단계의 결정 사항입니다. 일반 VPS 대신 스토리지 VPS를 선택하면 용량은 확보할 수 있지만, 두 개의 독립적인 장치를 제공받을 수 있을지는 요금제에 따라 다릅니다. lsblk를 실행하여 하나의 볼륨을 두 개로 나눈 슬라이스에 미러를 구성하는 실수를 하지 않도록 사전에 확인하십시오. 당사는 관리형 ZFS 어플라이언스가 아닌 Linux 및 FreeBSD 서버를 임대하므로, 스크럽 일정과 백업은 사용자가 직접 운영해야 합니다. 이것이 바로 풀(pool)에 대한 완전한 제어권과 유지보수 책임을 갖는 대신 치러야 할 대가입니다.

FAQ

VPS에서 ZFS 풀을 얼마나 자주 스크러빙(scrub)해야 합니까?

대부분의 소규모 풀에는 매월 수행하는 것이 적절하며, 이는 기존 패키지들의 기본 설정과도 일치합니다. Debian과 Ubuntu는 매월 두 번째 일요일에 cron 작업을 수행하며, FreeBSD의 주기적 시스템은 35일을 기본 임계값으로 설정합니다. 매주 수행하는 것은 스크러빙 시간을 측정해 보았을 때 유휴 상태인 서버에서 빠르게 완료되는 경우에만 합리적입니다. 장치가 한두 개인 바쁜 풀에서 매주 스크러빙을 수행하면 실제 애플리케이션 I/O 자원을 매주 소모하게 되며, 얻을 수 있는 이점은 고작 몇 주 정도의 조기 경보뿐입니다.

단일 디스크 ZFS 풀에서 스크러빙은 무의미합니까?

아니요, 스크러빙이 제공하는 기능을 명확히 이해하고 있다면 그렇지 않습니다. 중복성이 없는 경우 스크러빙은 데이터 손상을 감지할 수는 있지만 복구할 수는 없습니다. 단, ZFS가 기본적으로 추가 복사본을 유지하는 메타데이터는 예외입니다. 이 과정에서 얻을 수 있는 것은 zpool status -v에 기록된 손상된 파일 목록이며, 다른 곳에 정상적인 복사본이 남아 있을 때 파일을 복구할 수 있을 만큼 충분히 빠르게 알 수 있습니다. 스크러빙은 정확히 어떤 파일을 복구해야 하는지 알려주므로, 올바른 대응은 더 나은 백업 체계를 갖추는 것입니다.

ZFS 스크러빙을 일시 중지했다가 나중에 완료할 수 있습니까?

네, 가능합니다. zpool scrub -p tank 명령으로 일시 중지할 수 있으며, 일시 중지 상태와 진행 상황은 주기적으로 디스크에 기록되므로 풀을 export하거나 재부팅해도 스크러빙 상태는 유지됩니다. 마지막 체크포인트부터 다시 시작하려면 zpool scrub tank를 다시 실행하십시오. 이 목적으로 zpool scrub -s tank을 사용해서는 안 됩니다. -s는 스크러빙을 완전히 중단시키며, 다음에 실행할 때는 처음부터 다시 시작하게 됩니다.

ZFS 스크러빙이 너무 느린데 속도를 높일 수 있습니까?

스크러빙 시간은 디스크 용량이 아니라 할당된 데이터 양과 단편화 정도에 따라 결정됩니다. 90% 이상 사용 중인 풀은 4% 미만의 여유 공간을 가진 메타슬랩(metaslab)이 할당 방식을 first-fit에서 best-fit으로 변경하기 때문에 느려지며, 이로 인해 발생하는 단편화는 스크러빙을 수많은 작은 읽기 작업으로 변환합니다. 일반적으로 공간을 확보하는 것이 어떤 튜닝 설정보다 더 효과적입니다. zfs_scrub_min_time_ms 또는 zfs_vdev_scrub_max_active 값을 높여 스크러빙에 더 많은 큐 점유율을 할당할 수 있지만, 장치가 한두 개인 풀에서는 해당 점유율만큼 애플리케이션 성능이 직접적으로 저하됩니다.